PageRenderTime 47ms CodeModel.GetById 16ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 1ms

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