/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LinqTests.cs
C# | 396 lines | 354 code | 25 blank | 17 comment | 0 complexity | e7164b452ba422aeb9d1799e304a2bbb MD5 | raw file
1// Copyright (c) AlphaSierraPapa for the SharpDevelop Team 2// 3// Permission is hereby granted, free of charge, to any person obtaining a copy of this 4// software and associated documentation files (the "Software"), to deal in the Software 5// without restriction, including without limitation the rights to use, copy, modify, merge, 6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 7// to whom the Software is furnished to do so, subject to the following conditions: 8// 9// The above copyright notice and this permission notice shall be included in all copies or 10// substantial portions of the Software. 11// 12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 17// DEALINGS IN THE SOFTWARE. 18 19using System; 20using System.Linq; 21using ICSharpCode.NRefactory.Semantics; 22using ICSharpCode.NRefactory.TypeSystem; 23using ICSharpCode.NRefactory.TypeSystem.Implementation; 24using NUnit.Framework; 25 26namespace ICSharpCode.NRefactory.CSharp.Resolver 27{ 28 [TestFixture] 29 public class LinqTests : ResolverTestBase 30 { 31 [Test] 32 public void SimpleLinq() 33 { 34 string program = @"using System; using System.Linq; 35class TestClass { 36 void Test(string[] input) { 37 var r = from e in input 38 where e.StartsWith(""/"") 39 select e.Trim(); 40 r.ToString(); 41 } 42} 43"; 44 LocalResolveResult lrr = Resolve<LocalResolveResult>(program.Replace("where e", "where $e$")); 45 Assert.AreEqual("System.String", lrr.Type.ReflectionName); 46 lrr = Resolve<LocalResolveResult>(program.Replace("select e", "select $e$")); 47 Assert.AreEqual("System.String", lrr.Type.ReflectionName); 48 lrr = Resolve<LocalResolveResult>(program.Replace("from e", "from $e$")); 49 Assert.AreEqual("System.String", lrr.Type.ReflectionName); 50 51 lrr = Resolve<LocalResolveResult>(program.Replace("r.ToString", "$r$.ToString")); 52 Assert.AreEqual("System.Collections.Generic.IEnumerable", lrr.Type.FullName); 53 Assert.AreEqual("System.String", ((ParameterizedType)lrr.Type).TypeArguments[0].FullName); 54 } 55 56 [Test] 57 public void Group() 58 { 59 string program = @"using System; using System.Linq; 60class TestClass { 61 void Test(string[] input) { 62 var r = from e in input 63 group e.ToUpper() by e.Length; 64 $r$.ToString(); 65 } 66} 67"; 68 LocalResolveResult lrr = Resolve<LocalResolveResult>(program); 69 Assert.AreEqual("System.Collections.Generic.IEnumerable", lrr.Type.FullName); 70 ParameterizedType rt = (ParameterizedType)((ParameterizedType)lrr.Type).TypeArguments[0]; 71 Assert.AreEqual("System.Linq.IGrouping", rt.FullName); 72 Assert.AreEqual("System.Int32", rt.TypeArguments[0].FullName); 73 Assert.AreEqual("System.String", rt.TypeArguments[1].FullName); 74 } 75 76 [Test] 77 public void QueryableGroup() 78 { 79 string program = @"using System; using System.Linq; 80class TestClass { 81 void Test(IQueryable<string> input) { 82 var r = from e in input 83 group e.ToUpper() by e.Length; 84 $r$.ToString(); 85 } 86} 87"; 88 LocalResolveResult lrr = Resolve<LocalResolveResult>(program); 89 Assert.AreEqual("System.Linq.IQueryable", lrr.Type.FullName); 90 ParameterizedType rt = (ParameterizedType)((ParameterizedType)lrr.Type).TypeArguments[0]; 91 Assert.AreEqual("System.Linq.IGrouping", rt.FullName); 92 Assert.AreEqual("System.Int32", rt.TypeArguments[0].FullName); 93 Assert.AreEqual("System.String", rt.TypeArguments[1].FullName); 94 } 95 96 [Test] 97 public void Parenthesized() 98 { 99 string program = @"using System; using System.Linq; 100class TestClass { 101 void Test(string[] input) { 102 $(from e in input select e.Length)$.ToArray(); 103 } 104} 105"; 106 var rr = Resolve<ConversionResolveResult>(program); 107 Assert.IsTrue(rr.Conversion.IsIdentityConversion); 108 Assert.AreEqual("System.Collections.Generic.IEnumerable", rr.Type.FullName); 109 Assert.AreEqual("System.Int32", ((ParameterizedType)rr.Type).TypeArguments[0].FullName); 110 } 111 112 [Test] 113 public void SelectReturnType() 114 { 115 string program = @"using System; 116class TestClass { static void M() { 117 (from a in new XYZ() $select a.ToUpper()$).ToString(); 118}} 119class XYZ { 120 public int Select<U>(Func<string, U> f) { return 42; } 121}"; 122 var rr = Resolve<CSharpInvocationResolveResult>(program); 123 Assert.AreEqual("XYZ.Select", rr.Member.FullName); 124 Assert.AreEqual("System.Int32", rr.Type.FullName); 125 } 126 127 [Test] 128 public void Continuation() 129 { 130 string program = @"using System; using System.Linq; 131class TestClass { 132 void Test(string[] input) { 133 var r = from x in input 134 select x.GetHashCode() into x 135 where x == 42 136 select x * x; 137 r.ToString(); 138 } 139} 140"; 141 LocalResolveResult lrr = Resolve<LocalResolveResult>(program.Replace("from x", "from $x$")); 142 Assert.AreEqual("System.String", lrr.Type.ReflectionName); 143 lrr = Resolve<LocalResolveResult>(program.Replace("select x.G", "select $x$.G")); 144 Assert.AreEqual("System.String", lrr.Type.ReflectionName); 145 lrr = Resolve<LocalResolveResult>(program.Replace("into x", "into $x$")); 146 Assert.AreEqual("System.Int32", lrr.Type.ReflectionName); 147 lrr = Resolve<LocalResolveResult>(program.Replace("where x", "where $x$")); 148 Assert.AreEqual("System.Int32", lrr.Type.ReflectionName); 149 lrr = Resolve<LocalResolveResult>(program.Replace("select x * x", "select x * $x$")); 150 Assert.AreEqual("System.Int32", lrr.Type.ReflectionName); 151 152 lrr = Resolve<LocalResolveResult>(program.Replace("r.ToString", "$r$.ToString")); 153 Assert.AreEqual("System.Collections.Generic.IEnumerable`1[[System.Int32]]", lrr.Type.ReflectionName); 154 } 155 156 [Test] 157 public void OrderingWithSelectCall() 158 { 159 string program = @"using System; using System.Linq; 160class TestClass { 161 void Test(string[] input) { 162 $var$ r = from x in input 163 orderby x.Length 164 select x + x; 165 } 166} 167"; 168 TypeResolveResult rr = Resolve<TypeResolveResult>(program); 169 Assert.AreEqual("System.Collections.Generic.IEnumerable`1[[System.String]]", rr.Type.ReflectionName); 170 } 171 172 [Test] 173 public void OrderingWithoutSelectCall() 174 { 175 string program = @"using System; using System.Linq; 176class TestClass { 177 void Test(string[] input) { 178 $var$ r = from x in input 179 orderby x.Length 180 select x; 181 } 182} 183"; 184 TypeResolveResult rr = Resolve<TypeResolveResult>(program); 185 Assert.AreEqual("System.Linq.IOrderedEnumerable`1[[System.String]]", rr.Type.ReflectionName); 186 } 187 188 [Test] 189 public void OrderingWithSelectCallDueToSecondRangeVariable1() 190 { 191 string program = @"using System; using System.Linq; 192class TestClass { 193 void Test(string[] input) { 194 $var$ r = from x in input 195 from y in input 196 orderby x.Length 197 select x; 198 } 199} 200"; 201 TypeResolveResult rr = Resolve<TypeResolveResult>(program); 202 Assert.AreEqual("System.Collections.Generic.IEnumerable`1[[System.String]]", rr.Type.ReflectionName); 203 } 204 205 [Test] 206 public void OrderingWithSelectCallDueToSecondRangeVariable2() 207 { 208 string program = @"using System; using System.Linq; 209class TestClass { 210 void Test(string[] input) { 211 $var$ r = from x in input 212 join y in input on x equals y 213 orderby x.Length 214 select x; 215 } 216} 217"; 218 TypeResolveResult rr = Resolve<TypeResolveResult>(program); 219 Assert.AreEqual("System.Collections.Generic.IEnumerable`1[[System.String]]", rr.Type.ReflectionName); 220 } 221 222 [Test] 223 public void OrderingWithSelectCallDueToSecondRangeVariable3() 224 { 225 string program = @"using System; using System.Linq; 226class TestClass { 227 void Test(string[] input) { 228 $var$ r = from x in input 229 join y in input on x equals y into g 230 orderby x.Length 231 select x; 232 } 233} 234"; 235 TypeResolveResult rr = Resolve<TypeResolveResult>(program); 236 Assert.AreEqual("System.Collections.Generic.IEnumerable`1[[System.String]]", rr.Type.ReflectionName); 237 } 238 239 [Test] 240 public void OrderingWithSelectCallDueToSecondRangeVariable4() 241 { 242 string program = @"using System; using System.Linq; 243class TestClass { 244 void Test(string[] input) { 245 $var$ r = from x in input 246 let y = x 247 orderby x.Length 248 select x; 249 } 250} 251"; 252 TypeResolveResult rr = Resolve<TypeResolveResult>(program); 253 Assert.AreEqual("System.Collections.Generic.IEnumerable`1[[System.String]]", rr.Type.ReflectionName); 254 } 255 256 [Test] 257 public void DegenerateQuery() 258 { 259 string program = @"using System; using System.Linq; 260class TestClass { 261 void Test(string[] input) { 262 $var$ r = from x in input select x; 263 } 264} 265"; 266 TypeResolveResult rr = Resolve<TypeResolveResult>(program); 267 Assert.AreEqual("System.Collections.Generic.IEnumerable`1[[System.String]]", rr.Type.ReflectionName); 268 } 269 270 [Test] 271 public void GroupJoinWithCustomMethod() 272 { 273 string program = @"using System; 274using System.Collections.Generic; 275class TestClass { static void M(long [] args) { 276 var q = (from a in new XYZ() join b in args on a equals b into g select g); 277}} 278class XYZ { 279 public XYZ GroupJoin<T, R>(IEnumerable<T> f, Func<string, object> key1, Func<T, object> key2, Func<string, decimal, R> s) { return this; } 280 public int Select<U>(Func<string, U> f) { return 42; } 281}"; 282 var local = Resolve<LocalResolveResult>(program.Replace("into g", "into $g$")); 283 Assert.AreEqual("System.Decimal", local.Type.FullName); 284 285 local = Resolve<LocalResolveResult>(program.Replace("select g", "select $g$")); 286 Assert.AreEqual("System.Decimal", local.Type.FullName); 287 288 var trr = Resolve<TypeResolveResult>(program.Replace("var", "$var$")); 289 Assert.AreEqual("XYZ", trr.Type.FullName); // because 'Select' is done as part of GroupJoin() 290 } 291 292 [Test] 293 public void GroupJoinWithOverloadedCustomMethod() 294 { 295 string program = @"using System; 296using System.Collections.Generic; 297class TestClass 298{ 299 static void M(string[] args) 300 { 301 var q = (from a in new XYZ() $join b in args on a equals b into g$ select g.ToUpper()); 302 } 303} 304class XYZ 305{ 306 public int GroupJoin(IEnumerable<string> f, Func<string, object> key1, Func<string, object> key2, Func<string, int, int> s) { return 0; } 307 public decimal GroupJoin(IEnumerable<string> f, Func<string, object> key1, Func<string, object> key2, Func<string, string, string> s) { return 0; } 308}"; 309 var rr = Resolve<CSharpInvocationResolveResult>(program); 310 Assert.IsFalse(rr.IsError); 311 Assert.AreEqual("GroupJoin", rr.Member.Name); 312 Assert.AreEqual("System.Decimal", rr.Type.FullName); 313 314 rr = Resolve<CSharpInvocationResolveResult>(program.Replace("g.ToUpper()", "g.CompareTo(42)")); 315 Assert.IsFalse(rr.IsError); 316 Assert.AreEqual("GroupJoin", rr.Member.Name); 317 Assert.AreEqual("System.Int32", rr.Type.FullName); 318 } 319 320 [Test] 321 public void GroupWithQueryContinuation() 322 { 323 string program = @"using System; using System.Linq; 324class TestClass 325{ 326 static void M(string[] args) 327 { 328 var query = 329 from w in ""one to three"".Split() 330 group w by w.Length into g 331 orderby g.Key descending 332 select new { g.Key, Count = g.Count(), Avg = g.Average ($w$ => w.Length) }; 333 } 334}"; 335 var rr = Resolve<LocalResolveResult>(program); 336 Assert.AreEqual("System.String", rr.Type.FullName); 337 } 338 339 [Test] 340 public void SelectManyInvocation() 341 { 342 string program = @"using System; using System.Linq; 343class TestClass 344{ 345 static void M(string[] args) 346 { 347 var query = from w in args $from c in w$ select c - '0'; 348 } 349}"; 350 var rr = Resolve<CSharpInvocationResolveResult>(program); 351 Assert.IsFalse(rr.IsError); 352 Assert.AreEqual("SelectMany", rr.Member.Name); 353 Assert.AreEqual(3, rr.Member.Parameters.Count); 354 var typeArguments = ((SpecializedMethod)rr.Member).TypeArguments; 355 Assert.AreEqual(3, typeArguments.Count); 356 Assert.AreEqual("System.String", typeArguments[0].ReflectionName, "TSource"); 357 Assert.AreEqual("System.Char", typeArguments[1].ReflectionName, "TCollection"); 358 Assert.AreEqual("System.Int32", typeArguments[2].ReflectionName, "TResult"); 359 } 360 361 [Test] 362 public void SelectManyInvocationWithTransparentIdentifier() 363 { 364 string program = @"using System; using System.Linq; 365class TestClass 366{ 367 static void M(string[] args) 368 { 369 var query = from w in args $from c in w$ orderby c select c - '0'; 370 } 371}"; 372 var rr = Resolve<CSharpInvocationResolveResult>(program); 373 Assert.IsFalse(rr.IsError); 374 Assert.AreEqual("SelectMany", rr.Member.Name); 375 Assert.AreEqual(3, rr.Member.Parameters.Count); 376 var typeArguments = ((SpecializedMethod)rr.Member).TypeArguments; 377 Assert.AreEqual(3, typeArguments.Count); 378 Assert.AreEqual("System.String", typeArguments[0].ReflectionName, "TSource"); 379 Assert.AreEqual("System.Char", typeArguments[1].ReflectionName, "TCollection"); 380 Assert.AreEqual(TypeKind.Anonymous, typeArguments[2].Kind, "TResult"); 381 } 382 383 [Test] 384 public void FromClauseDoesNotResolveToSourceVariable() 385 { 386 string program = @"using System; using System.Linq; 387class TestClass { 388 static void M(string[] args) { 389 var query = $from w in args$ select int.Parse(w); 390 }}"; 391 var rr = Resolve<ConversionResolveResult>(program); 392 Assert.AreEqual("System.String[]", rr.Type.ReflectionName); 393 Assert.AreEqual(Conversion.IdentityConversion, rr.Conversion); 394 } 395 } 396}