From f31cb792a044c157698177000d189620d5118a8e Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 14 Jul 2016 15:08:40 -0700 Subject: [PATCH] Support doc comments for symbols declared off an anonymous type. --- .../Portable/Compilation/CSharpCompilation.cs | 14 +++++ .../Core/Portable/Compilation/Compilation.cs | 25 ++++++++ .../Core/Portable/PublicAPI.Unshipped.txt | 3 +- .../Compilation/VisualBasicCompilation.vb | 18 ++++++ .../AnonymousTypes/AnonymousTypeDescriptor.vb | 6 +- .../CSharpCompletionCommandHandlerTests.vb | 24 ++++++++ ...isualBasicCompletionCommandHandlerTests.vb | 23 ++++++++ .../SymbolKey.AnonymousTypeSymbolKey.cs | 57 +++++++++++++++++++ .../SymbolId/SymbolKey.SymbolKeyReader.cs | 2 + .../SymbolId/SymbolKey.SymbolKeyWriter.cs | 6 ++ .../Core/Portable/Workspaces.csproj | 1 + 11 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 src/Workspaces/Core/Portable/SymbolId/SymbolKey.AnonymousTypeSymbolKey.cs diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 472405bc968..70e4a367218 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -2835,6 +2835,20 @@ protected override INamedTypeSymbol CommonCreateTupleTypeSymbol(INamedTypeSymbol return TupleTypeSymbol.Create(csharpUnderlyingTuple, elementNames); } + protected override INamedTypeSymbol CommonCreateAnonymousTypeSymbol( + ImmutableArray memberTypes, ImmutableArray memberNames) + { + for (int i = 0, n = memberTypes.Length; i < n; i++) + { + memberTypes[i].EnsureCSharpSymbolOrNull($"{nameof(memberTypes)}[{i}]"); + } + + var fields = memberTypes.Select((t, i) => new AnonymousTypeField(memberNames[i], Location.None, (TypeSymbol)t)).ToImmutableArray(); + var descriptor = new AnonymousTypeDescriptor(fields, Location.None); + + return this.AnonymousTypeManager.ConstructAnonymousTypeSymbol(descriptor); + } + protected override ITypeSymbol CommonDynamicType { get { return DynamicType; } diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index 3f33fda87b5..ad2a992fc04 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -851,6 +851,31 @@ public INamedTypeSymbol CreateTupleTypeSymbol(INamedTypeSymbol underlyingType, I protected abstract INamedTypeSymbol CommonCreateTupleTypeSymbol(INamedTypeSymbol underlyingType, ImmutableArray elementNames); + /// + /// Returns a new anonymous type symbol with the given member types member names. + /// + public INamedTypeSymbol CreateAnonymousTypeSymbol( + ImmutableArray memberTypes, ImmutableArray memberNames) + { + if (memberTypes.Length != memberNames.Length) + { + throw new ArgumentException($"{nameof(memberTypes)} and {nameof(memberNames)} must have the same length."); + } + + for (int i = 0, n = memberTypes.Length; i < n; i++) + { + if (memberTypes[i] == null) + { + throw new ArgumentNullException($"{nameof(memberTypes)}[{i}]"); + } + } + + return CommonCreateAnonymousTypeSymbol(memberTypes, memberNames); + } + + protected abstract INamedTypeSymbol CommonCreateAnonymousTypeSymbol( + ImmutableArray memberTypes, ImmutableArray memberNames); + #endregion #region Diagnostics diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 95ecc7767a0..5a7f4910c23 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ +Microsoft.CodeAnalysis.Compilation.CreateAnonymousTypeSymbol(System.Collections.Immutable.ImmutableArray memberTypes, System.Collections.Immutable.ImmutableArray memberNames) -> Microsoft.CodeAnalysis.INamedTypeSymbol Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(Microsoft.CodeAnalysis.INamedTypeSymbol underlyingType, System.Collections.Immutable.ImmutableArray elementNames = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(System.Collections.Immutable.ImmutableArray elementTypes, System.Collections.Immutable.ImmutableArray elementNames = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.RegisterOperationAction(System.Action action, params Microsoft.CodeAnalysis.OperationKind[] operationKinds) -> void @@ -908,4 +909,4 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.Vi virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitVariableDeclarationStatement(Microsoft.CodeAnalysis.Semantics.IVariableDeclarationStatement operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitWhileUntilLoopStatement(Microsoft.CodeAnalysis.Semantics.IWhileUntilLoopStatement operation, TArgument argument) -> TResult virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitWithStatement(Microsoft.CodeAnalysis.Semantics.IWithStatement operation, TArgument argument) -> TResult -virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitYieldBreakStatement(Microsoft.CodeAnalysis.Semantics.IReturnStatement operation, TArgument argument) -> TResult +virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor.VisitYieldBreakStatement(Microsoft.CodeAnalysis.Semantics.IReturnStatement operation, TArgument argument) -> TResult \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index 6c21b1d6853..0006205e33e 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2605,6 +2605,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Throw New NotSupportedException(VBResources.ThereAreNoPointerTypesInVB) End Function + Protected Overrides Function CommonCreateAnonymousTypeSymbol( + memberTypes As ImmutableArray(Of ITypeSymbol), + memberNames As ImmutableArray(Of String)) As INamedTypeSymbol + + Dim index = 0 + For Each t In memberTypes + t.EnsureVbSymbolOrNothing(Of TypeSymbol)($"{NameOf(memberTypes)}({index})") + + index = index + 1 + Next + + Dim fields = memberTypes.Select(Function(t, i) New AnonymousTypeField(memberNames(i), DirectCast(t, TypeSymbol), Location.None)). + ToImmutableArray() + + Dim descriptor = New AnonymousTypeDescriptor(fields, Location.None, isImplicitlyDeclared:=False) + Return Me.AnonymousTypeManager.ConstructAnonymousTypeSymbol(descriptor) + End Function + Protected Overrides ReadOnly Property CommonDynamicType As ITypeSymbol Get Throw New NotSupportedException(VBResources.ThereIsNoDynamicTypeInVB) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/AnonymousTypeDescriptor.vb b/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/AnonymousTypeDescriptor.vb index 529c4ba8297..92180ca137d 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/AnonymousTypeDescriptor.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/AnonymousTypeDescriptor.vb @@ -47,10 +47,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property - Public Sub New(fields As ImmutableArray(Of AnonymousTypeField), _location As Location, _isImplicitlyDeclared As Boolean) + Public Sub New(fields As ImmutableArray(Of AnonymousTypeField), location As Location, isImplicitlyDeclared As Boolean) Me.Fields = fields - Me.Location = _location - Me.IsImplicitlyDeclared = _isImplicitlyDeclared + Me.Location = location + Me.IsImplicitlyDeclared = isImplicitlyDeclared Me.Key = ComputeKey(fields, Function(f) f.Name, Function(f) f.IsKey) End Sub diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index e0e5156baf5..b1201f4f437 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -1859,5 +1859,29 @@ class Program Await state.AssertSelectedCompletionItem("value", isHardSelected:=True).ConfigureAwait(True) End Using End Function + + + + Public Async Function TestAnonymousTypeDescription() As Task + Using state = TestState.CreateCSharpTestState( + , extraExportedTypes:={GetType(CSharpEditorFormattingService)}.ToList()) + state.SendInvokeCompletionList() + Await state.WaitForAsynchronousOperationsAsync() + Await state.AssertSelectedCompletionItem(description:= +"(extension) 'a[] System.Collections.Generic.IEnumerable<'a>.ToArray<'a>() + +Anonymous Types: + 'a is new { int x }") + End Using + End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb index 1e0e955580e..005b89c818d 100644 --- a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb @@ -2432,5 +2432,28 @@ End Class]]>) Await state.AssertSelectedCompletionItem("value", isHardSelected:=False, isSoftSelected:=True).ConfigureAwait(True) End Using End Function + + + + Public Async Function TestAnonymousTypeDescription() As Task + Using state = TestState.CreateVisualBasicTestState( + ) + state.SendInvokeCompletionList() + Await state.WaitForAsynchronousOperationsAsync() + Await state.AssertSelectedCompletionItem(description:= +" Function IEnumerable(Of 'a).ToArray() As 'a() + +Anonymous Types: + 'a is New With { .x As Integer }") + End Using + End Function End Class End Namespace \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/SymbolId/SymbolKey.AnonymousTypeSymbolKey.cs b/src/Workspaces/Core/Portable/SymbolId/SymbolKey.AnonymousTypeSymbolKey.cs new file mode 100644 index 00000000000..4ba9f431fe2 --- /dev/null +++ b/src/Workspaces/Core/Portable/SymbolId/SymbolKey.AnonymousTypeSymbolKey.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; + +namespace Microsoft.CodeAnalysis +{ + internal partial struct SymbolKey + { + private static class AnonymousTypeSymbolKey + { + public static void Create(INamedTypeSymbol symbol, SymbolKeyWriter visitor) + { + Debug.Assert(symbol.IsAnonymousType); + + var properties = symbol.GetMembers().OfType().ToArray(); + var propertyTypes = properties.Select(p => p.Type).ToImmutableArray(); + var propertyNames = properties.Select(p => p.Name).ToImmutableArray(); + + visitor.WriteSymbolKeyArray(propertyTypes); + visitor.WriteStringArray(propertyNames); + } + + public static int GetHashCode(GetHashCodeReader reader) + { + // The hash of the underlying type is good enough, we don't need to include names. + var symbolKeyHashCode = reader.ReadSymbolKeyArrayHashCode(); + var elementNames = reader.ReadStringArray(); + + return symbolKeyHashCode; + } + + public static SymbolKeyResolution Resolve(SymbolKeyReader reader) + { + var propertyTypeSymbols = reader.ReadSymbolKeyArray(); + var propertyTypes = propertyTypeSymbols.Select(r => GetFirstSymbol(r)).ToImmutableArray(); + var propertyNames = reader.ReadStringArray(); + + if (propertyTypes.Length == propertyNames.Length) + { + try + { + var anonymousType = reader.Compilation.CreateAnonymousTypeSymbol(propertyTypes, propertyNames); + return new SymbolKeyResolution(anonymousType); + } + catch (ArgumentException) + { + } + } + + return new SymbolKeyResolution(reader.Compilation.ObjectType); + } + } + } +} \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/SymbolId/SymbolKey.SymbolKeyReader.cs b/src/Workspaces/Core/Portable/SymbolId/SymbolKey.SymbolKeyReader.cs index 65cfe7d573f..96178cd4e30 100644 --- a/src/Workspaces/Core/Portable/SymbolId/SymbolKey.SymbolKeyReader.cs +++ b/src/Workspaces/Core/Portable/SymbolId/SymbolKey.SymbolKeyReader.cs @@ -365,6 +365,7 @@ protected override int ReadWorker(SymbolKeyType type) case SymbolKeyType.Event: return EventSymbolKey.GetHashCode(this); case SymbolKeyType.ReducedExtensionMethod: return ReducedExtensionMethodSymbolKey.GetHashCode(this); case SymbolKeyType.TypeParameter: return TypeParameterSymbolKey.GetHashCode(this); + case SymbolKeyType.AnonymousType: return AnonymousTypeSymbolKey.GetHashCode(this); case SymbolKeyType.TypeParameterOrdinal: return TypeParameterOrdinalSymbolKey.GetHashCode(this); } @@ -520,6 +521,7 @@ protected override SymbolKeyResolution ReadWorker(SymbolKeyType type) case SymbolKeyType.Event: return EventSymbolKey.Resolve(this); case SymbolKeyType.ReducedExtensionMethod: return ReducedExtensionMethodSymbolKey.Resolve(this); case SymbolKeyType.TypeParameter: return TypeParameterSymbolKey.Resolve(this); + case SymbolKeyType.AnonymousType: return AnonymousTypeSymbolKey.Resolve(this); case SymbolKeyType.TypeParameterOrdinal: return TypeParameterOrdinalSymbolKey.Resolve(this); } diff --git a/src/Workspaces/Core/Portable/SymbolId/SymbolKey.SymbolKeyWriter.cs b/src/Workspaces/Core/Portable/SymbolId/SymbolKey.SymbolKeyWriter.cs index e35e5e7b2d3..d3dce8d9da1 100644 --- a/src/Workspaces/Core/Portable/SymbolId/SymbolKey.SymbolKeyWriter.cs +++ b/src/Workspaces/Core/Portable/SymbolId/SymbolKey.SymbolKeyWriter.cs @@ -33,6 +33,7 @@ private enum SymbolKeyType Event = 'V', ReducedExtensionMethod = 'X', TypeParameter = 'Y', + AnonymousType = 'Z', // Not to be confused with ArrayType. This indicates an array of elements in the stream. Array = '%', @@ -334,6 +335,11 @@ public override object VisitNamedType(INamedTypeSymbol namedTypeSymbol) WriteType(SymbolKeyType.TupleType); TupleTypeSymbolKey.Create(namedTypeSymbol, this); } + else if (namedTypeSymbol.IsAnonymousType) + { + WriteType(SymbolKeyType.AnonymousType); + AnonymousTypeSymbolKey.Create(namedTypeSymbol, this); + } else { WriteType(SymbolKeyType.NamedType); diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index a568e0710f5..d732a736d4d 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -435,6 +435,7 @@ + -- GitLab