Skip to content

Commit

Permalink
Merge pull request #12532 from CyrusNajmabadi/anonymousTypeConstruction
Browse files Browse the repository at this point in the history
Support doc comments for symbols declared off an anonymous type.

Fixes #12530
  • Loading branch information
CyrusNajmabadi authored Jul 19, 2016
2 parents a2c0077 + 4387637 commit ced621e
Show file tree
Hide file tree
Showing 15 changed files with 370 additions and 7 deletions.
14 changes: 14 additions & 0 deletions src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2835,6 +2835,20 @@ protected override INamedTypeSymbol CommonCreateTupleTypeSymbol(INamedTypeSymbol
return TupleTypeSymbol.Create(csharpUnderlyingTuple, elementNames);
}

protected override INamedTypeSymbol CommonCreateAnonymousTypeSymbol(
ImmutableArray<ITypeSymbol> memberTypes, ImmutableArray<string> memberNames)
{
for (int i = 0, n = memberTypes.Length; i < n; i++)
{
memberTypes[i].EnsureCSharpSymbolOrNull<ITypeSymbol, TypeSymbol>($"{nameof(memberTypes)}[{i}]");
}

var fields = memberTypes.SelectAsArray((type, index, loc) => new AnonymousTypeField(memberNames[index], loc, (TypeSymbol)type), Location.None);
var descriptor = new AnonymousTypeDescriptor(fields, Location.None);

return this.AnonymousTypeManager.ConstructAnonymousTypeSymbol(descriptor);
}

protected override ITypeSymbol CommonDynamicType
{
get { return DynamicType; }
Expand Down
24 changes: 24 additions & 0 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4534,6 +4534,30 @@ public void CreateTupleTypeSymbol_VisualBasicElements()
}
}

[Fact]
public void CreateAnonymousTypeSymbol_VisualBasicElements()
{
var vbSource = @"Public Class C
End Class";

var vbComp = CreateVisualBasicCompilation("VB", vbSource,
compilationOptions: new VisualBasic.VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

vbComp.VerifyDiagnostics();
var vbType = (ITypeSymbol)vbComp.GlobalNamespace.GetMembers("C").Single();

var comp = CSharpCompilation.Create("test", references: new[] { MscorlibRef });
try
{
comp.CreateAnonymousTypeSymbol(ImmutableArray.Create(vbType), ImmutableArray.Create("m1"));
Assert.True(false);
}
catch (ArgumentException e)
{
Assert.Contains(CSharpResources.NotACSharpSymbol, e.Message);
}
}

[Fact]
public void CreateTupleTypeSymbol2_BadArguments()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2150,6 +2150,82 @@ public void PreviousSubmissionWithError()
Assert.Throws<InvalidOperationException>(() => CreateSubmission("a + 1", previous: s0));
}

[Fact()]
public void CreateAnonymousType_IncorrectLengths()
{
var compilation = CSharpCompilation.Create("HelloWorld");
Assert.Throws<ArgumentException>(() =>
compilation.CreateAnonymousTypeSymbol(
ImmutableArray.Create((ITypeSymbol)null),
ImmutableArray.Create("m1", "m2")));
}

[Fact()]
public void CreateAnonymousType_NullArgument1()
{
var compilation = CSharpCompilation.Create("HelloWorld");
Assert.Throws<ArgumentNullException>(() =>
compilation.CreateAnonymousTypeSymbol(
default(ImmutableArray<ITypeSymbol>),
ImmutableArray.Create("m1")));
}

[Fact()]
public void CreateAnonymousType_NullArgument2()
{
var compilation = CSharpCompilation.Create("HelloWorld");
Assert.Throws<ArgumentNullException>(() =>
compilation.CreateAnonymousTypeSymbol(
ImmutableArray.Create((ITypeSymbol)null),
default(ImmutableArray<string>)));
}

[Fact()]
public void CreateAnonymousType_NullArgument3()
{
var compilation = CSharpCompilation.Create("HelloWorld");
Assert.Throws<ArgumentNullException>(() =>
compilation.CreateAnonymousTypeSymbol(
ImmutableArray.Create((ITypeSymbol)null),
ImmutableArray.Create("m1")));
}

[Fact()]
public void CreateAnonymousType_NullArgument4()
{
var compilation = CSharpCompilation.Create("HelloWorld");
Assert.Throws<ArgumentNullException>(() =>
compilation.CreateAnonymousTypeSymbol(
ImmutableArray.Create((ITypeSymbol)compilation.GetSpecialType(SpecialType.System_Int32)),
ImmutableArray.Create((string)null)));
}

[Fact()]
public void CreateAnonymousType1()
{
var compilation = CSharpCompilation.Create("HelloWorld");
var type = compilation.CreateAnonymousTypeSymbol(
ImmutableArray.Create<ITypeSymbol>(compilation.GetSpecialType(SpecialType.System_Int32)),
ImmutableArray.Create("m1"));

Assert.True(type.IsAnonymousType);
Assert.Equal(1, type.GetMembers().OfType<IPropertySymbol>().Count());
Assert.Equal("<anonymous type: int m1>", type.ToDisplayString());
}

[Fact()]
public void CreateAnonymousType2()
{
var compilation = CSharpCompilation.Create("HelloWorld");
var type = compilation.CreateAnonymousTypeSymbol(
ImmutableArray.Create<ITypeSymbol>(compilation.GetSpecialType(SpecialType.System_Int32), compilation.GetSpecialType(SpecialType.System_Boolean)),
ImmutableArray.Create("m1", "m2"));

Assert.True(type.IsAnonymousType);
Assert.Equal(2, type.GetMembers().OfType<IPropertySymbol>().Count());
Assert.Equal("<anonymous type: int m1, bool m2>", type.ToDisplayString());
}

#region Script return values

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,6 @@ static void PrintFields(Type type)
}
}


[ClrOnlyFact]
public void AnonymousTypeSymbol_Empty()
{
Expand Down Expand Up @@ -1928,5 +1927,38 @@ void Test()
new TypeDescr() { FieldNames = new[] { "M" } },
new TypeDescr() { FieldNames = new[] { "N" } }));
}

[ClrOnlyFact]
public void CallingCreateAnonymousTypeDoesNotChangeIL()
{
var source = @"
class C
{
public static void Main(string[] args)
{
var v = new { m1 = 1, m2 = true };
}
}
}";

var expectedIL = @"{
// Code size 9 (0x9)
.maxstack 2
IL_0000: ldc.i4.1
IL_0001: ldc.i4.1
IL_0002: newobj ""<>f__AnonymousType0<int, bool>..ctor(int, bool)""
IL_0007: pop
IL_0008: ret
}";


CompileAndVerify(source).VerifyIL("C.Main", expectedIL);

var compilation = GetCompilationForEmit(new[] { source }, additionalRefs: null, options: null, parseOptions: null);
compilation.CreateAnonymousTypeSymbol(
ImmutableArray.Create<ITypeSymbol>(compilation.GetSpecialType(SpecialType.System_Int32), compilation.GetSpecialType(SpecialType.System_Boolean)),
ImmutableArray.Create("m1", "m2"));

this.CompileAndVerify(compilation).VerifyIL("C.Main", expectedIL);
}
}
}
40 changes: 40 additions & 0 deletions src/Compilers/Core/Portable/Compilation/Compilation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,46 @@ public INamedTypeSymbol GetTypeByMetadataName(string fullyQualifiedMetadataName)

protected abstract INamedTypeSymbol CommonCreateTupleTypeSymbol(INamedTypeSymbol underlyingType, ImmutableArray<string> elementNames);

/// <summary>
/// Returns a new anonymous type symbol with the given member types member names.
/// </summary>
public INamedTypeSymbol CreateAnonymousTypeSymbol(
ImmutableArray<ITypeSymbol> memberTypes, ImmutableArray<string> memberNames)
{
if (memberTypes.IsDefault)
{
throw new ArgumentNullException(nameof(memberTypes));
}

if (memberNames.IsDefault)
{
throw new ArgumentNullException(nameof(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}]");
}

if (memberNames[i] == null)
{
throw new ArgumentNullException($"{nameof(memberNames)}[{i}]");
}
}

return CommonCreateAnonymousTypeSymbol(memberTypes, memberNames);
}

protected abstract INamedTypeSymbol CommonCreateAnonymousTypeSymbol(
ImmutableArray<ITypeSymbol> memberTypes, ImmutableArray<string> memberNames);

#endregion

#region Diagnostics
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/Core/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Microsoft.CodeAnalysis.Compilation.CreateAnonymousTypeSymbol(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ITypeSymbol> memberTypes, System.Collections.Immutable.ImmutableArray<string> memberNames) -> Microsoft.CodeAnalysis.INamedTypeSymbol
Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(Microsoft.CodeAnalysis.INamedTypeSymbol underlyingType, System.Collections.Immutable.ImmutableArray<string> elementNames = default(System.Collections.Immutable.ImmutableArray<string>)) -> Microsoft.CodeAnalysis.INamedTypeSymbol
Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ITypeSymbol> elementTypes, System.Collections.Immutable.ImmutableArray<string> elementNames = default(System.Collections.Immutable.ImmutableArray<string>)) -> Microsoft.CodeAnalysis.INamedTypeSymbol
Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.RegisterOperationAction(System.Action<Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext> action, params Microsoft.CodeAnalysis.OperationKind[] operationKinds) -> void
Expand Down Expand Up @@ -911,4 +912,4 @@ virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.Vi
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.VisitVariableDeclarationStatement(Microsoft.CodeAnalysis.Semantics.IVariableDeclarationStatement operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.VisitWhileUntilLoopStatement(Microsoft.CodeAnalysis.Semantics.IWhileUntilLoopStatement operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.VisitWithStatement(Microsoft.CodeAnalysis.Semantics.IWithStatement operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.VisitYieldBreakStatement(Microsoft.CodeAnalysis.Semantics.IReturnStatement operation, TArgument argument) -> TResult
virtual Microsoft.CodeAnalysis.Semantics.OperationVisitor<TArgument, TResult>.VisitYieldBreakStatement(Microsoft.CodeAnalysis.Semantics.IReturnStatement operation, TArgument argument) -> TResult
Original file line number Diff line number Diff line change
Expand Up @@ -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 i = 0
For Each t In memberTypes
t.EnsureVbSymbolOrNothing(Of TypeSymbol)($"{NameOf(memberTypes)}({i})")

i = i + 1
Next

Dim fields = memberTypes.SelectAsArray(
Function(type, index, loc) New AnonymousTypeField(memberNames(index), DirectCast(type, TypeSymbol), loc), Location.None)

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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,52 @@ BC2014: the value '_' is invalid for option 'RootNamespace'
Assert.Throws(Of NotSupportedException)(Function() compilation.CreateTupleTypeSymbol(underlyingType:=Nothing, elementNames:=New ImmutableArray(Of String)))
End Sub

<Fact()>
Public Sub CreateAnonymousType_IncorrectLengths()
Dim compilation = VisualBasicCompilation.Create("HelloWorld")
Assert.Throws(Of ArgumentException)(
Function()
Return compilation.CreateAnonymousTypeSymbol(
ImmutableArray.Create(DirectCast(Nothing, ITypeSymbol)),
ImmutableArray.Create("m1", "m2"))
End Function)
End Sub

<Fact()>
Public Sub CreateAnonymousType_NothingArgument()
Dim compilation = VisualBasicCompilation.Create("HelloWorld")
Assert.Throws(Of ArgumentNullException)(
Function()
Return compilation.CreateAnonymousTypeSymbol(
ImmutableArray.Create(DirectCast(Nothing, ITypeSymbol)),
ImmutableArray.Create("m1"))
End Function)
End Sub

<Fact()>
Public Sub CreateAnonymousType1()
Dim compilation = VisualBasicCompilation.Create("HelloWorld")
Dim type = compilation.CreateAnonymousTypeSymbol(
ImmutableArray.Create(Of ITypeSymbol)(compilation.GetSpecialType(SpecialType.System_Int32)),
ImmutableArray.Create("m1"))

Assert.True(type.IsAnonymousType)
Assert.Equal(1, type.GetMembers().OfType(Of IPropertySymbol).Count())
Assert.Equal("<anonymous type: m1 As Integer>", type.ToDisplayString())
End Sub

<Fact()>
Public Sub CreateAnonymousType2()
Dim compilation = VisualBasicCompilation.Create("HelloWorld")
Dim type = compilation.CreateAnonymousTypeSymbol(
ImmutableArray.Create(Of ITypeSymbol)(compilation.GetSpecialType(SpecialType.System_Int32), compilation.GetSpecialType(SpecialType.System_Boolean)),
ImmutableArray.Create("m1", "m2"))

Assert.True(type.IsAnonymousType)
Assert.Equal(2, type.GetMembers().OfType(Of IPropertySymbol).Count())
Assert.Equal("<anonymous type: m1 As Integer, m2 As Boolean>", type.ToDisplayString())
End Sub

<Fact()>
Public Sub GetEntryPoint_Exe()
Dim source = <compilation name="Name1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1882,5 +1882,29 @@ class Program
Await state.AssertSelectedCompletionItem("value", isHardSelected:=True).ConfigureAwait(True)
End Using
End Function

<WpfFact, Trait(Traits.Feature, Traits.Features.Completion)>
<WorkItem(12530, "https://github.com/dotnet/roslyn/issues/12530")>
Public Async Function TestAnonymousTypeDescription() As Task
Using state = TestState.CreateCSharpTestState(
<Document><![CDATA[
using System.Linq;

class Program
{
static void Main(string[] args)
{
new[] { new { x = 1 } }.ToArr$$
}
}]]></Document>, 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
Loading

0 comments on commit ced621e

Please sign in to comment.