|
8 | 8 | using System.Collections.Generic;
|
9 | 9 | using System.Globalization;
|
10 | 10 | using System.Linq;
|
| 11 | +using System.Threading; |
11 | 12 | using Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax;
|
12 | 13 | using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
|
13 | 14 | using Microsoft.CodeAnalysis.Text;
|
@@ -4572,5 +4573,90 @@ disabled text 2
|
4572 | 4573 | Assert.True(trivia.ContainsDiagnostics);
|
4573 | 4574 | Assert.Equal((int)ErrorCode.ERR_Merge_conflict_marker_encountered, trivia.Errors().Single().Code);
|
4574 | 4575 | }
|
| 4576 | + |
| 4577 | + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78593")] |
| 4578 | + public void TestDotPrefixedNumberStartingAtStartOfSlidingTextWindow() |
| 4579 | + { |
| 4580 | + // This test depends on the line endings for the file being \r\n to ensure the right contents lines up at |
| 4581 | + // the right locations. |
| 4582 | + // |
| 4583 | + // It specifically validates what happens when we see `.0` at the start of the |
| 4584 | + // sliding text window, where the lexer tries to peek back one char to see if this |
| 4585 | + // is actually `..0` (a range expr) or `.0` (a floating point number). |
| 4586 | + var code = Resources.DotPrefixedNumberStartingAtStartOfSlidingTextWindow; |
| 4587 | + if (!code.Contains("\r\n")) |
| 4588 | + code = code.Replace("\n", "\r\n"); |
| 4589 | + |
| 4590 | + var sourceText = SourceText.From(code); |
| 4591 | + |
| 4592 | + { |
| 4593 | + // Run a full parse, and validate the tree returned). |
| 4594 | + |
| 4595 | + using var lexer = new Lexer(sourceText, CSharpParseOptions.Default); |
| 4596 | + |
| 4597 | + // Ensure we have a normal window size, not some larger array that another test created and cached in |
| 4598 | + // the window pool |
| 4599 | + lexer.TextWindow.GetTestAccessor().SetDefaultCharacterWindow(); |
| 4600 | + |
| 4601 | + using var parser = new LanguageParser(lexer, oldTree: null, changes: null); |
| 4602 | + |
| 4603 | + Microsoft.CodeAnalysis.SyntaxTreeExtensions.VerifySource( |
| 4604 | + sourceText, parser.ParseCompilationUnit().CreateRed()); |
| 4605 | + } |
| 4606 | + |
| 4607 | + { |
| 4608 | + // Now, replicate the same conditions that hte parser runs through by driving the a new lexer here |
| 4609 | + // directly. That ensures that we are actually validating exactly the conditions that led to the bug |
| 4610 | + // (a dot token starting a number, right at the start of the character window). |
| 4611 | + var lexer = new Lexer(sourceText, CSharpParseOptions.Default); |
| 4612 | + |
| 4613 | + // Ensure we have a normal window size, not some larger array that another test created and cached in |
| 4614 | + // the window pool |
| 4615 | + lexer.TextWindow.GetTestAccessor().SetDefaultCharacterWindow(); |
| 4616 | + |
| 4617 | + var mode = LexerMode.Syntax; |
| 4618 | + for (var i = 0; i < 1326; i++) |
| 4619 | + lexer.Lex(ref mode); |
| 4620 | + |
| 4621 | + // Lexer will read from index 0 in the arrray. |
| 4622 | + Assert.Equal(0, lexer.TextWindow.Offset); |
| 4623 | + |
| 4624 | + // We have 205 real chars in the window |
| 4625 | + Assert.Equal(205, lexer.TextWindow.CharacterWindowCount); |
| 4626 | + |
| 4627 | + // The lexer is at position 10199 in the file. |
| 4628 | + Assert.Equal(10199, lexer.TextWindow.Position); |
| 4629 | + |
| 4630 | + /// The 205 characters represent the final part of the doc |
| 4631 | + Assert.Equal(lexer.TextWindow.Text.Length, lexer.TextWindow.Position + lexer.TextWindow.CharacterWindowCount); |
| 4632 | + |
| 4633 | + // We're at the start of a token. |
| 4634 | + Assert.Equal(lexer.TextWindow.LexemeStartPosition, lexer.TextWindow.Position); |
| 4635 | + |
| 4636 | + // Ensure that the lexer's window is starting with the next FP number (".03") right at |
| 4637 | + // the start of the window. |
| 4638 | + Assert.True(lexer.TextWindow.CharacterWindow is ['.', '0', '3', ',', ..], $"Start of window was '{new string(lexer.TextWindow.CharacterWindow, 0, 4)}'"); |
| 4639 | + |
| 4640 | + var token = lexer.Lex(ref mode); |
| 4641 | + Assert.Equal(SyntaxKind.NumericLiteralToken, token.Kind); |
| 4642 | + Assert.Equal(3, token.FullWidth); |
| 4643 | + Assert.Equal(".03", token.ToString()); |
| 4644 | + |
| 4645 | + // But we moved 3 characters forward. |
| 4646 | + Assert.Equal(3, lexer.TextWindow.Offset); |
| 4647 | + |
| 4648 | + // We still have 205 real chars in the window |
| 4649 | + Assert.Equal(205, lexer.TextWindow.CharacterWindowCount); |
| 4650 | + |
| 4651 | + // The lexer position has moved 3 characters forward as well. |
| 4652 | + Assert.Equal(10202, lexer.TextWindow.Position); |
| 4653 | + |
| 4654 | + // We're at the start of a token. |
| 4655 | + Assert.Equal(lexer.TextWindow.LexemeStartPosition, lexer.TextWindow.Position); |
| 4656 | + |
| 4657 | + // Character window didn't changee. |
| 4658 | + Assert.True(lexer.TextWindow.CharacterWindow is ['.', '0', '3', ',', ..], $"Start of window was '{new string(lexer.TextWindow.CharacterWindow, 0, 4)}'"); |
| 4659 | + } |
| 4660 | + } |
4575 | 4661 | }
|
4576 | 4662 | }
|
0 commit comments