PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Src/Compilers/CSharp/Test/Syntax/Diagnostics/LocationsTests.cs

https://github.com/EkardNT/Roslyn
C# | 426 lines | 366 code | 42 blank | 18 comment | 2 complexity | adaf9b3c61398a4c4b725015b3001eab 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. using System;
  3. using System.Collections.Immutable;
  4. using System.IO;
  5. using System.Linq;
  6. using Microsoft.CodeAnalysis.Text;
  7. using Roslyn.Test.Utilities;
  8. using Xunit;
  9. namespace Microsoft.CodeAnalysis.CSharp.UnitTests
  10. {
  11. public class LocationsTests : TestBase
  12. {
  13. private static readonly TestSourceResolver resolver = new TestSourceResolver();
  14. private class TestSourceResolver : SourceFileResolver
  15. {
  16. public TestSourceResolver()
  17. : base(ImmutableArray<string>.Empty, null)
  18. {
  19. }
  20. public override string NormalizePath(string path, string baseFilePath)
  21. {
  22. return string.Format("[{0};{1}]", path, baseFilePath);
  23. }
  24. }
  25. private void AssertMappedSpanEqual(
  26. SyntaxTree syntaxTree,
  27. string sourceText,
  28. string expectedPath,
  29. int expectedStartLine,
  30. int expectedStartOffset,
  31. int expectedEndLine,
  32. int expectedEndOffset,
  33. bool hasMappedPath)
  34. {
  35. var span = GetSpanIn(syntaxTree, sourceText);
  36. var mappedSpan = syntaxTree.GetMappedLineSpan(span);
  37. var actualDisplayPath = syntaxTree.GetDisplayPath(span, resolver);
  38. Assert.Equal(hasMappedPath, mappedSpan.HasMappedPath);
  39. Assert.Equal(expectedPath, mappedSpan.Path);
  40. if (expectedPath == "")
  41. {
  42. Assert.Equal("", actualDisplayPath);
  43. }
  44. else
  45. {
  46. Assert.Equal(string.Format("[{0};{1}]", expectedPath, hasMappedPath ? syntaxTree.FilePath : null), actualDisplayPath);
  47. }
  48. Assert.Equal(expectedStartLine, mappedSpan.StartLinePosition.Line);
  49. Assert.Equal(expectedStartOffset, mappedSpan.StartLinePosition.Character);
  50. Assert.Equal(expectedEndLine, mappedSpan.EndLinePosition.Line);
  51. Assert.Equal(expectedEndOffset, mappedSpan.EndLinePosition.Character);
  52. }
  53. private TextSpan GetSpanIn(SyntaxTree syntaxTree, string textToFind)
  54. {
  55. string s = syntaxTree.GetText().ToString();
  56. int index = s.IndexOf(textToFind);
  57. Assert.True(index >= 0, "textToFind not found in the tree");
  58. return new TextSpan(index, textToFind.Length);
  59. }
  60. [Fact]
  61. public void TestGetSourceLocationInFile()
  62. {
  63. string sampleProgram = @"class X {
  64. #line 20 ""d:\banana.cs""
  65. int x;
  66. }";
  67. SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "c:\\foo.cs");
  68. TextSpan xSpan = new TextSpan(sampleProgram.IndexOf("x;"), 2);
  69. TextSpan xToCloseBraceSpan = new TextSpan(xSpan.Start, sampleProgram.IndexOf("}") - xSpan.Start + 1);
  70. Location locX = new SourceLocation(syntaxTree, xSpan);
  71. Location locXToCloseBrace = new SourceLocation(syntaxTree, xToCloseBraceSpan);
  72. FileLinePositionSpan flpsX = locX.GetLineSpan();
  73. Assert.Equal("c:\\foo.cs", flpsX.Path);
  74. Assert.Equal(2, flpsX.StartLinePosition.Line);
  75. Assert.Equal(4, flpsX.StartLinePosition.Character);
  76. Assert.Equal(2, flpsX.EndLinePosition.Line);
  77. Assert.Equal(6, flpsX.EndLinePosition.Character);
  78. flpsX = locX.GetMappedLineSpan();
  79. Assert.Equal("d:\\banana.cs", flpsX.Path);
  80. Assert.Equal(19, flpsX.StartLinePosition.Line);
  81. Assert.Equal(4, flpsX.StartLinePosition.Character);
  82. Assert.Equal(19, flpsX.EndLinePosition.Line);
  83. Assert.Equal(6, flpsX.EndLinePosition.Character);
  84. FileLinePositionSpan flpsXToCloseBrace = locXToCloseBrace.GetLineSpan();
  85. Assert.Equal("c:\\foo.cs", flpsXToCloseBrace.Path);
  86. Assert.Equal(2, flpsXToCloseBrace.StartLinePosition.Line);
  87. Assert.Equal(4, flpsXToCloseBrace.StartLinePosition.Character);
  88. Assert.Equal(3, flpsXToCloseBrace.EndLinePosition.Line);
  89. Assert.Equal(1, flpsXToCloseBrace.EndLinePosition.Character);
  90. flpsXToCloseBrace = locXToCloseBrace.GetMappedLineSpan();
  91. Assert.Equal("d:\\banana.cs", flpsXToCloseBrace.Path);
  92. Assert.Equal(19, flpsXToCloseBrace.StartLinePosition.Line);
  93. Assert.Equal(4, flpsXToCloseBrace.StartLinePosition.Character);
  94. Assert.Equal(20, flpsXToCloseBrace.EndLinePosition.Line);
  95. Assert.Equal(1, flpsXToCloseBrace.EndLinePosition.Character);
  96. }
  97. [Fact]
  98. public void TestLineMapping1()
  99. {
  100. string sampleProgram = @"using System;
  101. class X {
  102. #line 20 ""banana.cs""
  103. int x;
  104. int y;
  105. #line 44
  106. int z;
  107. #line default
  108. int w;
  109. #line hidden
  110. int q;
  111. int f;
  112. #if false
  113. #line 17 ""d:\twing.cs""
  114. #endif
  115. int a;
  116. }";
  117. var resolver = new TestSourceResolver();
  118. SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "foo.cs");
  119. AssertMappedSpanEqual(syntaxTree, "ing Sy", "foo.cs", 0, 2, 0, 8, hasMappedPath: false);
  120. AssertMappedSpanEqual(syntaxTree, "class X", "foo.cs", 1, 0, 1, 7, hasMappedPath: false);
  121. AssertMappedSpanEqual(syntaxTree, "System;\r\nclass X", "foo.cs", 0, 6, 1, 7, hasMappedPath: false);
  122. AssertMappedSpanEqual(syntaxTree, "x;", "banana.cs", 19, 4, 19, 6, hasMappedPath: true);
  123. AssertMappedSpanEqual(syntaxTree, "y;", "banana.cs", 20, 4, 20, 6, hasMappedPath: true);
  124. AssertMappedSpanEqual(syntaxTree, "z;", "banana.cs", 43, 4, 43, 6, hasMappedPath: true);
  125. AssertMappedSpanEqual(syntaxTree, "w;", "foo.cs", 8, 4, 8, 6, hasMappedPath: false);
  126. AssertMappedSpanEqual(syntaxTree, "q;\r\nin", "foo.cs", 10, 4, 11, 2, hasMappedPath: false);
  127. AssertMappedSpanEqual(syntaxTree, "a;", "foo.cs", 15, 4, 15, 6, hasMappedPath: false);
  128. }
  129. [Fact]
  130. public void TestLineMapping2()
  131. {
  132. string sampleProgram = @"using System;
  133. class X {
  134. #line 20
  135. int x;
  136. #line hidden
  137. int y;
  138. #line 30 ""baz""
  139. int z;
  140. #line hidden
  141. int w;
  142. #line 40
  143. int v;
  144. }";
  145. var resolver = new TestSourceResolver();
  146. SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "c:\\foo.cs");
  147. AssertMappedSpanEqual(syntaxTree, "int x;", "c:\\foo.cs", 19, 0, 19, 6, hasMappedPath: false);
  148. AssertMappedSpanEqual(syntaxTree, "int y;", "c:\\foo.cs", 21, 0, 21, 6, hasMappedPath: false);
  149. AssertMappedSpanEqual(syntaxTree, "int z;", "baz", 29, 0, 29, 6, hasMappedPath: true);
  150. AssertMappedSpanEqual(syntaxTree, "int w;", "baz", 31, 0, 31, 6, hasMappedPath: true);
  151. AssertMappedSpanEqual(syntaxTree, "int v;", "baz", 39, 0, 39, 6, hasMappedPath: true);
  152. }
  153. [Fact]
  154. public void TestLineMapping_NoSyntaxTreePath()
  155. {
  156. string sampleProgram = @"using System;
  157. #line 20
  158. class X {}
  159. ";
  160. var resolver = new TestSourceResolver();
  161. AssertMappedSpanEqual(SyntaxFactory.ParseSyntaxTree(sampleProgram, path: ""), "class X {}", "", 19, 0, 19, 10, hasMappedPath: false);
  162. AssertMappedSpanEqual(SyntaxFactory.ParseSyntaxTree(sampleProgram, path: " "), "class X {}", " ", 19, 0, 19, 10, hasMappedPath: false);
  163. }
  164. [Fact]
  165. public void TestInvalidLineMapping()
  166. {
  167. string sampleProgram = @"using System;
  168. class X {
  169. int q;
  170. #line 0 ""firstdirective""
  171. int r;
  172. #line 20 ""seconddirective""
  173. int s;
  174. }";
  175. SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "filename.cs");
  176. AssertMappedSpanEqual(syntaxTree, "int q", "filename.cs", 2, 4, 2, 9, hasMappedPath: false);
  177. AssertMappedSpanEqual(syntaxTree, "int r", "filename.cs", 4, 4, 4, 9, hasMappedPath: false); // invalid #line args
  178. AssertMappedSpanEqual(syntaxTree, "int s", "seconddirective", 19, 4, 19, 9, hasMappedPath: true);
  179. }
  180. [Fact]
  181. public void TestLineMappingNoDirectives()
  182. {
  183. string sampleProgram = @"using System;
  184. class X {
  185. int x;
  186. }";
  187. SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "c:\\foo.cs");
  188. AssertMappedSpanEqual(syntaxTree, "ing Sy", "c:\\foo.cs", 0, 2, 0, 8, hasMappedPath: false);
  189. AssertMappedSpanEqual(syntaxTree, "class X", "c:\\foo.cs", 1, 0, 1, 7, hasMappedPath: false);
  190. AssertMappedSpanEqual(syntaxTree, "System;\r\nclass X", "c:\\foo.cs", 0, 6, 1, 7, hasMappedPath: false);
  191. AssertMappedSpanEqual(syntaxTree, "x;", "c:\\foo.cs", 2, 4, 2, 6, hasMappedPath: false);
  192. }
  193. [WorkItem(537005, "DevDiv")]
  194. [Fact]
  195. public void TestMissingTokenAtEndOfLine()
  196. {
  197. string sampleProgram = @"using System;
  198. using System.Collections.Generic;
  199. using System.Linq;
  200. class Program
  201. {
  202. static void Main(string[] args)
  203. {
  204. int x
  205. }
  206. }";
  207. SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "c:\\foo.cs");
  208. // verify missing semicolon diagnostic is on the same line
  209. var diags = syntaxTree.GetDiagnostics();
  210. Assert.Equal(1, diags.Count());
  211. var diag = diags.First();
  212. FileLinePositionSpan flps = diag.Location.GetLineSpan();
  213. // verify the diagnostic is positioned at the end of the line "int x" and has zero width
  214. Assert.Equal(flps, new FileLinePositionSpan("c:\\foo.cs", new LinePosition(8, 13), new LinePosition(8, 13)));
  215. sampleProgram = @"using System;
  216. using System.Collections.Generic;
  217. using System.Linq;
  218. class Program
  219. {
  220. static void Main(string[] args)
  221. {
  222. int x // dummy comment
  223. }
  224. }";
  225. syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "c:\\foo.cs");
  226. diags = syntaxTree.GetDiagnostics();
  227. diag = diags.First();
  228. flps = diag.Location.GetLineSpan();
  229. // verify missing semicolon diagnostic is on the same line and before the comment
  230. Assert.Equal(flps, new FileLinePositionSpan("c:\\foo.cs", new LinePosition(8, 13), new LinePosition(8, 13)));
  231. sampleProgram = @"using System;
  232. using System.Collections.Generic;
  233. using System.Linq;
  234. class Program
  235. {
  236. static void Main(string[] args)
  237. {
  238. int x /* dummy
  239. multiline
  240. comment*/
  241. }
  242. }";
  243. syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "c:\\foo.cs");
  244. diags = syntaxTree.GetDiagnostics();
  245. diag = diags.First();
  246. flps = diag.Location.GetLineSpan();
  247. // verify missing semicolon diagnostic is on the same line and before the comment
  248. Assert.Equal(flps, new FileLinePositionSpan("c:\\foo.cs", new LinePosition(8, 13), new LinePosition(8, 13)));
  249. }
  250. [WorkItem(537537, "DevDiv")]
  251. [Fact]
  252. public void TestDiagnosticSpanForIdentifierExpectedError()
  253. {
  254. string sampleProgram = @"using System;
  255. using System.Collections.Generic;
  256. using System.Linq;
  257. class Program
  258. {
  259. static void Main(string[] args)
  260. {
  261. string 2131;
  262. }
  263. }";
  264. SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "c:\\foo.cs");
  265. var diags = syntaxTree.GetDiagnostics();
  266. // verify missing identifier diagnostic has the correct span
  267. Assert.NotEmpty(diags);
  268. var diag = diags.First();
  269. FileLinePositionSpan flps = diag.Location.GetLineSpan();
  270. // verify the diagnostic width spans the entire token "2131"
  271. Assert.Equal(flps, new FileLinePositionSpan("c:\\foo.cs", new LinePosition(8, 15), new LinePosition(8, 19)));
  272. }
  273. [WorkItem(540077, "DevDiv")]
  274. [Fact]
  275. public void TestDiagnosticSpanForErrorAtLastToken()
  276. {
  277. string sampleProgram = @"using System;
  278. using System.Collections.Generic;
  279. using System.Linq;
  280. class C
  281. {
  282. int[] array = new int[
  283. }";
  284. SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "c:\\foo.cs");
  285. var diags = syntaxTree.GetDiagnostics();
  286. Assert.NotEmpty(diags);
  287. foreach (var diag in diags)
  288. {
  289. // verify the diagnostic span doesn't go past the text span
  290. Assert.InRange(diag.Location.SourceSpan.End, diag.Location.SourceSpan.Start, syntaxTree.GetText().Length);
  291. }
  292. }
  293. [WorkItem(537215, "DevDiv")]
  294. [Fact]
  295. public void TestLineMappingForErrors()
  296. {
  297. string sampleProgram = @"class
  298. end class";
  299. SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram);
  300. // The below commented lines work fine. Please uncomment once bug is fixed.
  301. // Expected(tree.GetDiagnostics()[0].Location.GetLineSpan("", false), "", 1, 4, 1, 5);
  302. // Expected(tree.GetDiagnostics()[1].Location.GetLineSpan("", false), "", 1, 4, 1, 5);
  303. // Expected(tree.GetDiagnostics()[2].Location.GetLineSpan("", false), "", 1, 9, 1, 9);
  304. // This line throws ArgumentOutOfRangeException.
  305. var span = syntaxTree.GetDiagnostics().ElementAt(3).Location.GetLineSpan();
  306. Assert.Equal(span, new FileLinePositionSpan("", new LinePosition(1, 9), new LinePosition(1, 9)));
  307. }
  308. [Fact]
  309. public void TestEqualSourceLocations()
  310. {
  311. string sampleProgram = @"class
  312. end class";
  313. SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram);
  314. SyntaxTree tree2 = SyntaxFactory.ParseSyntaxTree(sampleProgram);
  315. SourceLocation loc1 = new SourceLocation(syntaxTree, new TextSpan(3, 4));
  316. SourceLocation loc2 = new SourceLocation(syntaxTree, new TextSpan(3, 4));
  317. SourceLocation loc3 = new SourceLocation(syntaxTree, new TextSpan(3, 7));
  318. SourceLocation loc4 = new SourceLocation(tree2, new TextSpan(3, 4));
  319. Assert.Equal(loc1, loc2);
  320. Assert.Equal(loc1.GetHashCode(), loc2.GetHashCode());
  321. Assert.NotEqual(loc1, loc3);
  322. Assert.NotEqual(loc3, loc4);
  323. }
  324. [WorkItem(541612, "DevDiv")]
  325. [Fact]
  326. public void DiagnsoticsGetLineSpanForErrorinTryCatch()
  327. {
  328. string sampleProgram = @"
  329. class Program
  330. {
  331. static void Main(string[] args)
  332. {
  333. try
  334. {
  335. }
  336. ct
  337. }
  338. }";
  339. SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "c:\\foo.cs");
  340. var token = syntaxTree.GetCompilationUnitRoot().FindToken(sampleProgram.IndexOf("ct"));
  341. // Get the diagnostics from the ExpressionStatement Syntax node which is the current token's Parent's Parent
  342. var expressionDiags = syntaxTree.GetDiagnostics(token.Parent.Parent);
  343. Assert.DoesNotThrow(() => expressionDiags.First().Location.GetLineSpan());
  344. foreach (var diag in expressionDiags)
  345. {
  346. // verify the diagnostic span doesn't go past the text span
  347. Assert.InRange(diag.Location.SourceSpan.Start, 0, syntaxTree.GetText().Length);
  348. Assert.InRange(diag.Location.SourceSpan.End, 0, syntaxTree.GetText().Length);
  349. }
  350. }
  351. [Fact, WorkItem(537926, "DevDiv")]
  352. public void TestSourceLocationToString()
  353. {
  354. string sampleProgram = @"using System;
  355. class MainClass
  356. {
  357. static void Main()
  358. {
  359. #line 200
  360. int i; // CS0168 on line 200
  361. #line default
  362. char c; // CS0168 on line 9
  363. }
  364. }
  365. ";
  366. SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram);
  367. TextSpan span1 = new TextSpan(sampleProgram.IndexOf("i;"), 2);
  368. TextSpan span2 = new TextSpan(sampleProgram.IndexOf("c;"), 2);
  369. SourceLocation loc1 = new SourceLocation(syntaxTree, span1);
  370. SourceLocation loc2 = new SourceLocation(syntaxTree, span2);
  371. // GetDebuggerDisplay() is private
  372. // Assert.Equal("SourceLocation(@8:13)\"i;\"", loc1.GetDebuggerDisplay());
  373. Assert.Equal("SourceFile([91..93))", loc1.ToString());
  374. // Assert.Equal("SourceLocation(@10:14)\"c;\"", loc2.GetDebuggerDisplay());
  375. Assert.Equal("SourceFile([148..150))", loc2.ToString());
  376. }
  377. }
  378. }