PageRenderTime 35ms CodeModel.GetById 2ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 0ms

/Src/Compilers/CSharp/Test/Symbol/Symbols/AnonymousTypesSemanticsTests.cs

https://github.com/EkardNT/Roslyn
C# | 991 lines | 841 code | 132 blank | 18 comment | 15 complexity | 06a7f6fc5f26b5077e644e5aa8a8d2de MD5 | raw file
  1// Copyright (c) Microsoft Open Technologies, Inc.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
  2
  3using System;
  4using System.Collections.Generic;
  5using System.Linq;
  6using Microsoft.CodeAnalysis.CSharp.Symbols;
  7using Microsoft.CodeAnalysis.CSharp.Syntax;
  8using Microsoft.CodeAnalysis.Test.Utilities;
  9using Microsoft.CodeAnalysis.Text;
 10using Roslyn.Test.Utilities;
 11using Xunit;
 12
 13namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols
 14{
 15    public class AnonymousTypesSemanticsTests : CompilingTestBase
 16    {
 17        [Fact()]
 18        public void AnonymousTypeSymbols_Simple()
 19        {
 20            var source = @"
 21public class ClassA
 22{
 23    public struct SSS
 24    {
 25    }
 26
 27    public static void Test1(int x)
 28    {
 29        object v1 = [# new
 30        {
 31            [# aa  #] = 1,
 32            [# BB  #] = """",
 33            [# CCC #] = new SSS()
 34        } #];
 35
 36        object v2 = [# new
 37        {
 38            [# aa  #] = new SSS(),
 39            [# BB  #] = 123.456,
 40            [# CCC #] = [# new
 41            {
 42                (new ClassA()).[# aa  #],
 43                ClassA.[# BB  #],
 44                ClassA.[# CCC #]
 45            } #]
 46        } #];
 47
 48        object v3 = [# new {} #];
 49        var v4 = [# new {} #];
 50    }
 51
 52    public int aa
 53    {
 54        get { return 123; }
 55    }
 56
 57    public const string BB = ""-=-=-"";
 58
 59    public static SSS CCC = new SSS();
 60}";
 61            var data = Compile(source, 14);
 62
 63            var info0 = GetAnonymousTypeInfoSummary(data, 0,
 64                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span,
 65                            1, 2, 3);
 66
 67            var info1 = GetAnonymousTypeInfoSummary(data, 4,
 68                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 3).Span,
 69                            5, 6, 7);
 70
 71            var info2 = GetAnonymousTypeInfoSummary(data, 8,
 72                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 5).Span,
 73                            9, 10, 11);
 74
 75            Assert.Equal(info0.Type, info2.Type);
 76            Assert.NotEqual(info0.Type, info1.Type);
 77
 78            var info3 = GetAnonymousTypeInfoSummary(data, 12,
 79                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 7).Span);
 80            var info4 = GetAnonymousTypeInfoSummary(data, 13,
 81                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 8).Span);
 82            Assert.Equal(info3.Type, info4.Type);
 83        }
 84
 85        [Fact()]
 86        public void AnonymousTypeSymbols_ContextualKeywordsInFields()
 87        {
 88            var source = @"
 89class ClassA
 90{
 91    static void Test1(int x)
 92    {
 93        object v1 = [# new
 94        {
 95            [# var #] = ""var"",
 96            [# get #] = new {},
 97            [# partial #] = [# new
 98                            {
 99                                (new ClassA()).[# select #],
100                                [# global  #]
101                            } #]
102        } #];
103    }
104
105    public int select
106    {
107        get { return 123; }
108    }
109
110    public const string global = ""-=-=-"";
111}";
112            var data = Compile(source, 7);
113
114            var info0 = GetAnonymousTypeInfoSummary(data, 0,
115                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span,
116                            1, 2, 3);
117
118            var info1 = GetAnonymousTypeInfoSummary(data, 4,
119                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 3).Span,
120                            5, 6);
121
122            Assert.Equal(
123                "<anonymous type: System.String var, <empty anonymous type> get, <anonymous type: System.Int32 select, System.String global> partial>",
124                info0.Type.ToTestDisplayString());
125
126            Assert.Equal(
127                "<anonymous type: System.Int32 select, System.String global>..ctor(System.Int32 select, System.String global)",
128                info1.Symbol.ToTestDisplayString());
129        }
130
131        [Fact()]
132        public void AnonymousTypeSymbols_DelegateMembers()
133        {
134            var source = @"
135delegate bool D1();
136class ClassA
137{
138    void Main()
139    {
140        var at1 = [# new { [# module #] = (D1)(() => false)} #].module();
141    }
142}";
143            var data = Compile(source, 2);
144
145            var info0 = GetAnonymousTypeInfoSummary(data, 0,
146                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span,
147                            1);
148
149            Assert.Equal("<anonymous type: D1 module>", info0.Type.ToTestDisplayString());
150            Assert.Equal("<anonymous type: D1 module>..ctor(D1 module)", info0.Symbol.ToTestDisplayString());
151        }
152
153        [Fact()]
154        public void AnonymousTypeSymbols_BaseAccessInMembers()
155        {
156            var source = @"
157delegate bool D1();
158class ClassB
159{
160    protected System.Func<int, int> F = x => x;
161}
162class ClassA: ClassB
163{
164    void Main()
165    {
166        var at1 = [# [# new { base.[# F #] } #].F(1) #];
167    }
168}";
169            var data = Compile(source, 3);
170
171            var info0 = GetAnonymousTypeInfoSummary(data, 1,
172                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span,
173                            2);
174
175            Assert.Equal("<anonymous type: System.Func<System.Int32, System.Int32> F>", info0.Type.ToTestDisplayString());
176
177            var info1 = data.Model.GetSemanticInfoSummary(data.Nodes[0]);
178
179            Assert.Equal("System.Int32 System.Func<System.Int32, System.Int32>.Invoke(System.Int32 arg)", info1.Symbol.ToTestDisplayString());
180        }
181
182        [Fact()]
183        public void AnonymousTypeSymbols_InFieldInitializer()
184        {
185            var source = @"
186class ClassA
187{
188    private static object F = [# new { [# F123 #] = typeof(ClassA) } #];
189}";
190            var data = Compile(source, 2);
191
192            var info0 = GetAnonymousTypeInfoSummary(data, 0,
193                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span,
194                            1);
195
196            Assert.Equal("<anonymous type: System.Type F123>", info0.Type.ToTestDisplayString());
197        }
198
199        [Fact()]
200        public void AnonymousTypeSymbols_Equals()
201        {
202            var source = @"
203class ClassA
204{
205    static void Test1(int x)
206    {
207        bool result = [# new { f1 = 1, f2 = """" }.Equals(new { }) #];
208    }
209}";
210            var data = Compile(source, 1);
211
212            var info = data.Model.GetSemanticInfoSummary(data.Nodes[0]);
213
214            var method = info.Symbol;
215            Assert.NotNull(method);
216            Assert.Equal(SymbolKind.Method, method.Kind);
217            Assert.Equal("object.Equals(object)", method.ToDisplayString());
218        }
219
220        [Fact()]
221        public void AnonymousTypeSymbols_ToString()
222        {
223            var source = @"
224class ClassA
225{
226    static void Test1(int x)
227    {
228        string result = [# new { f1 = 1, f2 = """" }.ToString() #];
229    }
230}";
231            var data = Compile(source, 1);
232
233            var info = data.Model.GetSemanticInfoSummary(data.Nodes[0]);
234
235            var method = info.Symbol;
236            Assert.NotNull(method);
237            Assert.Equal(SymbolKind.Method, method.Kind);
238            Assert.Equal("object.ToString()", method.ToDisplayString());
239        }
240
241        [Fact()]
242        public void AnonymousTypeSymbols_GetHashCode()
243        {
244            var source = @"
245class ClassA
246{
247    static void Test1(int x)
248    {
249        int result = [# new { f1 = 1, f2 = """" }.GetHashCode() #];
250    }
251}";
252            var data = Compile(source, 1);
253
254            var info = data.Model.GetSemanticInfoSummary(data.Nodes[0]);
255
256            var method = info.Symbol;
257            Assert.NotNull(method);
258            Assert.Equal(SymbolKind.Method, method.Kind);
259            Assert.Equal("object.GetHashCode()", method.ToDisplayString());
260        }
261
262        [Fact()]
263        public void AnonymousTypeSymbols_Ctor()
264        {
265            var source = @"
266class ClassA
267{
268    static void Test1(int x)
269    {
270        var result = [# new { f1 = 1, f2 = """" } #];
271    }
272}";
273            var data = Compile(source, 1);
274
275            var info = data.Model.GetSemanticInfoSummary(data.Nodes[0]);
276
277            var method = info.Symbol;
278            Assert.NotNull(method);
279            Assert.Equal(SymbolKind.Method, method.Kind);
280            Assert.Equal("<anonymous type: int f1, string f2>..ctor(int, string)", method.ToDisplayString());
281            Assert.Equal("<anonymous type: System.Int32 f1, System.String f2>..ctor(System.Int32 f1, System.String f2)", method.ToTestDisplayString());
282        }
283
284        [Fact()]
285        public void AnonymousTypeTemplateCannotConstruct()
286        {
287            var source = @"
288class ClassA
289{
290    object F = [# new { [# F123 #] = typeof(ClassA) } #];
291}";
292            var data = Compile(source, 2);
293
294            var info0 = GetAnonymousTypeInfoSummary(data, 0,
295                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span,
296                            1);
297
298            var type = info0.Type;
299            Assert.Equal("<anonymous type: System.Type F123>", type.ToTestDisplayString());
300            Assert.True(type.IsDefinition);
301            AssertCannotConstruct(type);
302        }
303
304        [Fact()]
305        public void AnonymousTypeTemplateCannotConstruct_Empty()
306        {
307            var source = @"
308class ClassA
309{
310    object F = [# new { } #];
311}";
312            var data = Compile(source, 1);
313
314            var info0 = GetAnonymousTypeInfoSummary(data, 0,
315                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span);
316
317            var type = info0.Type;
318            Assert.Equal("<empty anonymous type>", type.ToTestDisplayString());
319            Assert.True(type.IsDefinition);
320            AssertCannotConstruct(type);
321        }
322
323        [Fact()]
324        public void AnonymousTypeFieldDeclarationIdentifier()
325        {
326            var source = @"
327class ClassA
328{
329    object F = new { [# F123 #] = typeof(ClassA) };
330}";
331            var data = Compile(source, 1);
332            var info = data.Model.GetSymbolInfo((ExpressionSyntax)data.Nodes[0]);
333            Assert.NotNull(info.Symbol);
334            Assert.Equal(SymbolKind.Property, info.Symbol.Kind);
335            Assert.Equal("System.Type <anonymous type: System.Type F123>.F123 { get; }", info.Symbol.ToTestDisplayString());
336        }
337
338        [Fact()]
339        public void AnonymousTypeFieldCreatedInQuery()
340        {
341            var source = LINQ + @"
342class ClassA
343{
344    void m()
345    {
346        var o = from x in new List1<int>(1, 2, 3) select [# new { [# x #], [# y #] = x } #];
347    }
348}";
349            var data = Compile(source, 3);
350
351            var info0 = GetAnonymousTypeInfoSummary(data, 0,
352                data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, NumberOfNewKeywords(LINQ) + 2).Span,
353                1, 2);
354
355            var info1 = data.Model.GetSymbolInfo(((AnonymousObjectMemberDeclaratorSyntax)data.Nodes[1]).Expression);
356            Assert.NotNull(info1.Symbol);
357            Assert.Equal(SymbolKind.RangeVariable, info1.Symbol.Kind);
358            Assert.Equal("x", info1.Symbol.ToDisplayString());
359
360            var info2 = data.Model.GetSymbolInfo((ExpressionSyntax)data.Nodes[2]);
361            Assert.NotNull(info2.Symbol);
362            Assert.Equal(SymbolKind.Property, info2.Symbol.Kind);
363            Assert.Equal("System.Int32 <anonymous type: System.Int32 x, System.Int32 y>.y { get; }", info2.Symbol.ToTestDisplayString());
364        }
365
366        [Fact()]
367        public void AnonymousTypeFieldCreatedInQuery2()
368        {
369            var source = LINQ + @"
370class ClassA
371{
372    void m()
373    {
374        var o = from x in new List1<int>(1, 2, 3) let y = """" select [# new { [# x #], [# y #] } #];
375    }
376}";
377            var data = Compile(source, 3);
378
379            var info0 = GetAnonymousTypeInfoSummary(data, 0,
380                data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, NumberOfNewKeywords(LINQ) + 2).Span,
381                1, 2);
382
383            Assert.Equal("<anonymous type: System.Int32 x, System.String y>", info0.Type.ToTestDisplayString());
384
385            var info1 = data.Model.GetSymbolInfo(((AnonymousObjectMemberDeclaratorSyntax)data.Nodes[1]).Expression);
386            Assert.NotNull(info1.Symbol);
387            Assert.Equal(SymbolKind.RangeVariable, info1.Symbol.Kind);
388            Assert.Equal("x", info1.Symbol.ToDisplayString());
389
390            var info2 = data.Model.GetSymbolInfo(((AnonymousObjectMemberDeclaratorSyntax)data.Nodes[2]).Expression);
391            Assert.NotNull(info2.Symbol);
392            Assert.Equal(SymbolKind.RangeVariable, info2.Symbol.Kind);
393            Assert.Equal("y", info2.Symbol.ToDisplayString());
394        }
395
396        [Fact()]
397        public void AnonymousTypeFieldCreatedInLambda()
398        {
399            var source = @"
400using System;
401class ClassA
402{
403    void m()
404    {
405        var o = (Action)(() => ( [# new { [# x #] = 1, [# y #] = [# new { } #] } #]).ToString());;
406    }
407}";
408            var data = Compile(source, 4);
409
410            var info0 = GetAnonymousTypeInfoSummary(data, 0,
411                data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span,
412                1, 2);
413
414            var info1 = GetAnonymousTypeInfoSummary(data, 3,
415                data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 2).Span);
416
417            Assert.Equal("<anonymous type: System.Int32 x, <empty anonymous type> y>..ctor(System.Int32 x, <empty anonymous type> y)", info0.Symbol.ToTestDisplayString());
418        }
419
420        [Fact()]
421        public void AnonymousTypeFieldCreatedInLambda2()
422        {
423            var source = @"
424using System;
425class ClassA
426{
427    void m()
428    {
429        var o = (Action)
430                    (() =>
431                        ((Func<string>) (() => ( [# new { [# x #] = 1, [# y #] = [# new { } #] } #]).ToString())
432                    ).Invoke());
433    }
434}";
435            var data = Compile(source, 4);
436
437            var info0 = GetAnonymousTypeInfoSummary(data, 0,
438                data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span,
439                1, 2);
440
441            var info1 = GetAnonymousTypeInfoSummary(data, 3,
442                data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 2).Span);
443
444            Assert.Equal("<anonymous type: System.Int32 x, <empty anonymous type> y>..ctor(System.Int32 x, <empty anonymous type> y)", info0.Symbol.ToTestDisplayString());
445        }
446
447        [Fact()]
448        public void AnonymousTypeSymbols_DontCrashIfNameIsQueriedBeforeEmit()
449        {
450            var source = @"
451public class ClassA
452{
453    public static void Test1(int x)
454    {
455        object v1 = [# new { [# aa  #] = 1, [# BB  #] = 2 } #];
456        object v2 = [# new { } #];
457    }
458}";
459            var data = Compile(source, 4);
460
461            var info0 = GetAnonymousTypeInfoSummary(data, 0,
462                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span,
463                            1, 2);
464
465            CheckAnonymousType(info0.Type, "", "");
466
467            info0 = GetAnonymousTypeInfoSummary(data, 3,
468                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 2).Span);
469
470            CheckAnonymousType(info0.Type, "", "");
471
472            //  perform emit
473            CompileAndVerify(
474                data.Compilation,
475                symbolValidator: module => CheckAnonymousTypes(module)
476            );
477        }
478
479        #region "AnonymousTypeSymbols_DontCrashIfNameIsQueriedBeforeEmit"
480
481        private void CheckAnonymousType(ITypeSymbol type, string name, string metadataName)
482        {
483            Assert.NotNull(type);
484            Assert.Equal(name, type.Name);
485            Assert.Equal(metadataName, type.MetadataName);
486        }
487
488        private void CheckAnonymousTypes(ModuleSymbol module)
489        {
490            var ns = module.GlobalNamespace;
491            Assert.NotNull(ns);
492
493            CheckAnonymousType(ns.GetMember<NamedTypeSymbol>("<>f__AnonymousType0"), "<>f__AnonymousType0", "<>f__AnonymousType0`2");
494            CheckAnonymousType(ns.GetMember<NamedTypeSymbol>("<>f__AnonymousType1"), "<>f__AnonymousType1", "<>f__AnonymousType1");
495        }
496
497        #endregion
498
499        [Fact()]
500        public void AnonymousTypeSymbols_Error_Simple()
501        {
502            var source = @"
503public class ClassA
504{
505    public static void Test1(int x)
506    {
507        object v1 = [# new
508        {
509            [# aa  #] = xyz,
510            [# BB  #] = """",
511            [# CCC #] = new SSS()
512        } #];
513
514        object v2 = [# new
515        {
516            [# aa  #] = new SSS(),
517            [# BB  #] = 123.456,
518            [# CCC #] = [# new
519            {
520                (new ClassA()).[# aa  #],
521                ClassA.[# BB  #],
522                ClassA.[# CCC #]
523            } #]
524        } #];
525    }
526}";
527            var data = Compile(source, 12,
528                // (8,25): error CS0103: The name 'xyz' does not exist in the current context
529                //                aa     = xyz,
530                Diagnostic(ErrorCode.ERR_NameNotInContext, "xyz").WithArguments("xyz"),
531                // (10,29): error CS0246: The type or namespace name 'SSS' could not be found (are you missing a using directive or an assembly reference?)
532                //                CCC    = new SSS()
533                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "SSS").WithArguments("SSS"),
534                // (15,29): error CS0246: The type or namespace name 'SSS' could not be found (are you missing a using directive or an assembly reference?)
535                //                aa     = new SSS(),
536                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "SSS").WithArguments("SSS"),
537                // (19,35): error CS1061: 'ClassA' does not contain a definition for 'aa' and no extension method 'aa' accepting a first argument of type 'ClassA' could be found (are you missing a using directive or an assembly reference?)
538                //                 (new ClassA()).   aa    ,
539                Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "aa").WithArguments("ClassA", "aa"),
540                // (20,27): error CS0117: 'ClassA' does not contain a definition for 'BB'
541                //                 ClassA.   BB    ,
542                Diagnostic(ErrorCode.ERR_NoSuchMember, "BB").WithArguments("ClassA", "BB"),
543                // (21,27): error CS0117: 'ClassA' does not contain a definition for 'CCC'
544                //                 ClassA.   CCC   
545                Diagnostic(ErrorCode.ERR_NoSuchMember, "CCC").WithArguments("ClassA", "CCC")
546            );
547
548            var info0 = GetAnonymousTypeInfoSummary(data, 0,
549                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span,
550                            1, 2, 3);
551
552            var info1 = GetAnonymousTypeInfoSummary(data, 4,
553                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 3).Span,
554                            5, 6, 7);
555
556            var info2 = GetAnonymousTypeInfoSummary(data, 8,
557                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 5).Span,
558                            9, 10, 11);
559
560            Assert.Equal("<anonymous type: ? aa, System.String BB, SSS CCC>", info0.Type.ToTestDisplayString());
561            Assert.Equal("<anonymous type: SSS aa, System.Double BB, <anonymous type: ? aa, ? BB, ? CCC> CCC>", info1.Type.ToTestDisplayString());
562            Assert.Equal("<anonymous type: ? aa, ? BB, ? CCC>", info2.Type.ToTestDisplayString());
563        }
564
565        [Fact()]
566        public void AnonymousTypeSymbols_Error_InUsingStatement()
567        {
568            var source = @"
569public class ClassA
570{
571    public static void Test1(int x)
572    {
573        using (var v1 = [# new { } #])
574        {
575        }
576    }
577}";
578            var data = Compile(source, 1,
579                // (6,16): error CS1674: '<empty anonymous type>': type used in a using statement must be implicitly convertible to 'System.IDisposable'
580                //         using (var v1 =    new { }   )
581                Diagnostic(ErrorCode.ERR_NoConvToIDisp, "var v1 =    new { }").WithArguments("<empty anonymous type>")
582            );
583
584            var info0 = GetAnonymousTypeInfoSummary(data, 0,
585                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span);
586
587            Assert.Equal("<empty anonymous type>", info0.Type.ToTestDisplayString());
588        }
589
590        [Fact()]
591        public void AnonymousTypeSymbols_Error_DuplicateName()
592        {
593            var source = @"
594public class ClassA
595{
596    public static void Test1(int x)
597    {
598        object v1 = [# new
599        {
600            [# aa  #] = 1,
601            ClassA.[# aa #],
602            [# bb #] = 1.2
603        } #];
604    }
605
606    public static string aa = ""-field-aa-"";
607}";
608            var data = Compile(source, 4,
609                // (9,13): error CS0833: An anonymous type cannot have multiple properties with the same name
610                //             ClassA.   aa   ,
611                Diagnostic(ErrorCode.ERR_AnonymousTypeDuplicatePropertyName, "ClassA.   aa")
612            );
613
614            var info0 = GetAnonymousTypeInfoSummary(data, 0,
615                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span,
616                            1, /*2,*/ 3);
617
618            Assert.Equal("<anonymous type: System.Int32 aa, System.String $1, System.Double bb>", info0.Type.ToTestDisplayString());
619
620            var properties = (from m in info0.Type.GetMembers() where m.Kind == SymbolKind.Property select m).ToArray();
621            Assert.Equal(3, properties.Length);
622
623            Assert.Equal("System.Int32 <anonymous type: System.Int32 aa, System.String $1, System.Double bb>.aa { get; }", properties[0].ToTestDisplayString());
624            Assert.Equal("System.String <anonymous type: System.Int32 aa, System.String $1, System.Double bb>.$1 { get; }", properties[1].ToTestDisplayString());
625            Assert.Equal("System.Double <anonymous type: System.Int32 aa, System.String $1, System.Double bb>.bb { get; }", properties[2].ToTestDisplayString());
626        }
627
628        [Fact()]
629        public void AnonymousTypeSymbols_LookupSymbols()
630        {
631            var source = @"
632public class ClassA
633{
634    public static void Test1(int x)
635    {
636        object v1 = [# new
637        {
638            [# aa  #] = """",
639            [# abc #] = 123.456
640        } #];
641        object v2 = [# new{ } #];
642    }
643}";
644            var data = Compile(source, 4);
645
646            var info0 = GetAnonymousTypeInfoSummary(data, 0,
647                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 1).Span,
648                            1, 2);
649
650            Assert.Equal("<anonymous type: System.String aa, System.Double abc>", info0.Type.ToTestDisplayString());
651
652            var pos = data.Nodes[0].Span.End;
653            var syms = data.Model.LookupSymbols(pos, container: info0.Type).Select(x => x.ToTestDisplayString()).OrderBy(x => x).ToArray();
654            Assert.Equal(8, syms.Length);
655
656            int index = 0;
657            Assert.Equal("System.Boolean System.Object.Equals(System.Object obj)", syms[index++]);
658            Assert.Equal("System.Boolean System.Object.Equals(System.Object objA, System.Object objB)", syms[index++]);
659            Assert.Equal("System.Boolean System.Object.ReferenceEquals(System.Object objA, System.Object objB)", syms[index++]);
660            Assert.Equal("System.Double <anonymous type: System.String aa, System.Double abc>.abc { get; }", syms[index++]);
661            Assert.Equal("System.Int32 System.Object.GetHashCode()", syms[index++]);
662            Assert.Equal("System.String <anonymous type: System.String aa, System.Double abc>.aa { get; }", syms[index++]);
663            Assert.Equal("System.String System.Object.ToString()", syms[index++]);
664            Assert.Equal("System.Type System.Object.GetType()", syms[index++]);
665
666            info0 = GetAnonymousTypeInfoSummary(data, 3,
667                            data.Tree.FindNodeOrTokenByKind(SyntaxKind.NewKeyword, 2).Span);
668
669            Assert.Equal("<empty anonymous type>", info0.Type.ToTestDisplayString());
670
671            pos = data.Nodes[3].Span.End;
672            syms = data.Model.LookupSymbols(pos, container: info0.Type).Select(x => x.ToTestDisplayString()).OrderBy(x => x).ToArray();
673            Assert.Equal(6, syms.Length);
674
675            index = 0;
676            Assert.Equal("System.Boolean System.Object.Equals(System.Object obj)", syms[index++]);
677            Assert.Equal("System.Boolean System.Object.Equals(System.Object objA, System.Object objB)", syms[index++]);
678            Assert.Equal("System.Boolean System.Object.ReferenceEquals(System.Object objA, System.Object objB)", syms[index++]);
679            Assert.Equal("System.Int32 System.Object.GetHashCode()", syms[index++]);
680            Assert.Equal("System.String System.Object.ToString()", syms[index++]);
681            Assert.Equal("System.Type System.Object.GetType()", syms[index++]);
682        }
683
684        [WorkItem(543189, "DevDiv")]
685        [Fact()]
686        public void CheckAnonymousTypeAsConstValue()
687        {
688            var source = @"
689public class A
690{
691    const int i = /*<bind>*/(new {a = 2}).a/*</bind>*/;
692}";
693
694            var comp = CreateCompilationWithMscorlib(source);
695            var tuple = GetBindingNodeAndModel<ExpressionSyntax>(comp);
696            var info = tuple.Item2.GetSymbolInfo(tuple.Item1);
697            Assert.NotNull(info.Symbol);
698            Assert.Equal("<anonymous type: int a>.a", info.Symbol.ToDisplayString());
699        }
700
701        [WorkItem(546416, "DevDiv")]
702        [Fact()]
703        public void TestAnonymousTypeInsideGroupBy_Queryable()
704        {
705            CompileAndVerify(
706 @"using System.Linq;
707
708public class Product
709{
710    public int ProductID;
711    public string ProductName;
712    public int SupplierID;
713}
714public class DB
715{
716    public IQueryable<Product> Products;
717}
718
719public class Program
720{
721    public static void Main()
722    {
723        var db = new DB();
724        var q0 = db.Products.GroupBy(p => new { Conditional = false ? new { p.ProductID, p.ProductName, p.SupplierID } : new { p.ProductID, p.ProductName, p.SupplierID } }).ToList();
725    }
726}", additionalRefs: new[] { SystemCoreRef }).VerifyDiagnostics();
727        }
728
729        [WorkItem(546416, "DevDiv")]
730        [Fact()]
731        public void TestAnonymousTypeInsideGroupBy_Enumerable()
732        {
733            CompileAndVerify(
734 @"using System.Linq;
735using System.Collections.Generic;
736
737public class Product
738{
739    public int ProductID;
740    public string ProductName;
741    public int SupplierID;
742}
743public class DB
744{
745    public IEnumerable<Product> Products;
746}
747
748public class Program
749{
750    public static void Main()
751    {
752        var db = new DB();
753        var q0 = db.Products.GroupBy(p => new { Conditional = false ? new { p.ProductID, p.ProductName, p.SupplierID } : new { p.ProductID, p.ProductName, p.SupplierID } }).ToList();
754    }
755}", additionalRefs: new[] { SystemCoreRef }).VerifyDiagnostics();
756        }
757
758        [WorkItem(546416, "DevDiv")]
759        [Fact()]
760        public void TestAnonymousTypeInsideGroupBy_Enumerable2()
761        {
762            CompileAndVerify(
763 @"using System.Linq;
764using System.Collections.Generic;
765
766public class Product
767{
768    public int ProductID;
769    public int SupplierID;
770}
771public class DB
772{
773    public IEnumerable<Product> Products;
774}
775
776public class Program
777{
778    public static void Main()
779    {
780        var db = new DB();
781        var q0 = db.Products.GroupBy(p => new { Conditional = false ? new { p.ProductID, p.SupplierID } : new { p.ProductID, p.SupplierID } }).ToList();
782        var q1 = db.Products.GroupBy(p => new { Conditional = false ? new { p.ProductID, p.SupplierID } : new { p.ProductID, p.SupplierID } }).ToList();
783    }
784}", additionalRefs: new[] { SystemCoreRef }).VerifyDiagnostics();
785        }
786
787        #region "Utility methods"
788
789        private void AssertCannotConstruct(ISymbol type)
790        {
791            var namedType = type as NamedTypeSymbol;
792            Assert.NotNull(namedType);
793
794            var objType = namedType.BaseType;
795            Assert.NotNull(objType);
796            Assert.Equal("System.Object", objType.ToTestDisplayString());
797
798            TypeSymbol[] args = new TypeSymbol[namedType.Arity];
799            for (int i = 0; i < namedType.Arity; i++)
800            {
801                args[i] = objType;
802            }
803
804            Assert.Throws<InvalidOperationException>(() => namedType.Construct(args));
805        }
806
807        private CompilationUtils.SemanticInfoSummary GetAnonymousTypeInfoSummary(TestData data, int node, TextSpan typeSpan, params int[] fields)
808        {
809            var info = data.Model.GetSemanticInfoSummary(data.Nodes[node]);
810            var type = info.Type;
811
812            Assert.True(type.IsAnonymousType);
813            Assert.False(type.CanBeReferencedByName);
814
815            Assert.Equal("System.Object", type.BaseType.ToTestDisplayString());
816            Assert.Equal(0, type.Interfaces.Length);
817
818            Assert.Equal(1, type.Locations.Length);
819            Assert.Equal(typeSpan, type.Locations[0].SourceSpan);
820
821            foreach (int field in fields)
822            {
823                CheckFieldNameAndLocation(data, type, data.Nodes[field]);
824            }
825
826            return info;
827        }
828
829        private void CheckFieldNameAndLocation(TestData data, ITypeSymbol type, SyntaxNode identifier)
830        {
831            var anonymousType = (NamedTypeSymbol)type;
832
833            var current = identifier;
834            while (current.Span == identifier.Span && !current.IsKind(SyntaxKind.IdentifierName))
835            {
836                current = current.ChildNodes().Single();
837            }
838            var node = (IdentifierNameSyntax)current;
839            Assert.NotNull(node);
840
841            var span = node.Span;
842            var fieldName = node.ToString();
843
844            var property = anonymousType.GetMember<PropertySymbol>(fieldName);
845            Assert.NotNull(property);
846            Assert.Equal(fieldName, property.Name);
847            Assert.Equal(1, property.Locations.Length);
848            Assert.Equal(span, property.Locations[0].SourceSpan);
849
850            MethodSymbol getter = property.GetMethod;
851            Assert.NotNull(getter);
852            Assert.Equal("get_" + fieldName, getter.Name);
853        }
854
855        struct TestData
856        {
857            public CSharpCompilation Compilation;
858            public SyntaxTree Tree;
859            public List<SyntaxNode> Nodes;
860            public SemanticModel Model;
861        }
862
863        private TestData Compile(string source, int expectedIntervals, params DiagnosticDescription[] diagnostics)
864        {
865            var intervals = ExtractTextIntervals(ref source);
866            Assert.Equal(expectedIntervals, intervals.Count);
867
868            var compilation = GetCompilationForEmit(
869                new[] { source },
870                new MetadataReference[] { },
871                DefaultCompilationOptions.WithOutputKind(OutputKind.DynamicallyLinkedLibrary)
872            );
873
874            compilation.VerifyDiagnostics(diagnostics);
875
876            var tree = compilation.SyntaxTrees[0];
877            var nodes = new List<SyntaxNode>();
878
879            foreach (var span in intervals)
880            {
881                var stack = new Stack<SyntaxNode>();
882                stack.Push(tree.GetCompilationUnitRoot());
883
884                while (stack.Count > 0)
885                {
886                    var node = stack.Pop();
887                    if (span.Contains(node.Span))
888                    {
889                        nodes.Add(node);
890                        break;
891                    }
892
893                    foreach (var child in node.ChildNodes())
894                    {
895                        stack.Push(child);
896                    }
897                }
898            }
899            Assert.Equal(expectedIntervals, nodes.Count);
900
901            return new TestData()
902            {
903                Compilation = compilation,
904                Tree = tree,
905                Model = compilation.GetSemanticModel(tree),
906                Nodes = nodes
907            };
908        }
909
910        private CSharpCompilation Compile(string source)
911        {
912            return GetCompilationForEmit(
913                new[] { source },
914                new MetadataReference[] { },
915                DefaultCompilationOptions.WithOutputKind(OutputKind.DynamicallyLinkedLibrary)
916            );
917        }
918
919        private static List<TextSpan> ExtractTextIntervals(ref string source)
920        {
921            const string startTag = "[#";
922            const string endTag = "#]";
923
924            List<TextSpan> intervals = new List<TextSpan>();
925
926            var all = (from s in FindAll(source, startTag)
927                       select new { start = true, offset = s }).Union(
928                                from s in FindAll(source, endTag)
929                                select new { start = false, offset = s }
930                      ).OrderBy(value => value.offset).ToList();
931
932            while (all.Count > 0)
933            {
934                int i = 1;
935                bool added = false;
936                while (i < all.Count)
937                {
938                    if (all[i - 1].start && !all[i].start)
939                    {
940                        intervals.Add(TextSpan.FromBounds(all[i - 1].offset, all[i].offset));
941                        all.RemoveAt(i);
942                        all.RemoveAt(i - 1);
943                        added = true;
944                    }
945                    else
946                    {
947                        i++;
948                    }
949                }
950                Assert.True(added);
951            }
952
953            source = source.Replace(startTag, "  ").Replace(endTag, "  ");
954
955            intervals.Sort((x, y) => x.Start.CompareTo(y.Start));
956            return intervals;
957        }
958
959        private static IEnumerable<int> FindAll(string source, string what)
960        {
961            int index = source.IndexOf(what);
962            while (index >= 0)
963            {
964                yield return index;
965                index = source.IndexOf(what, index + 1);
966            }
967        }
968
969        private int NumberOfNewKeywords(string source)
970        {
971            int cnt = 0;
972            foreach (var line in source.Split(new String[] { Environment.NewLine }, StringSplitOptions.None))
973            {
974                if (!string.IsNullOrWhiteSpace(line))
975                {
976                    if (!line.Trim().StartsWith("//"))
977                    {
978                        for (int index = line.IndexOf("new "); index >= 0; )
979                        {
980                            cnt++;
981                            index = line.IndexOf("new ", index + 1);
982                        }
983                    }
984                }
985            }
986            return cnt;
987        }
988
989        #endregion
990    }
991}