From cdc677a3d1d0939a35e6da5b26ad5292e4c3b5d8 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 9 Jan 2025 18:14:54 +1100 Subject: [PATCH] Fix a couple of exceptions encountered when formatting documents with preprocessor directives --- .../Extensions/RazorSyntaxNodeExtensions.cs | 6 +++ .../Formatting/CSharpFormatter.cs | 12 ++++-- .../Formatting_NetFx/HtmlFormattingTest.cs | 40 +++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Extensions/RazorSyntaxNodeExtensions.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Extensions/RazorSyntaxNodeExtensions.cs index 6e33257c74e..d191e4df465 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Extensions/RazorSyntaxNodeExtensions.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Extensions/RazorSyntaxNodeExtensions.cs @@ -380,6 +380,12 @@ public static bool TryGetLinePositionSpanWithoutWhitespace(this SyntaxNode node, var startPositionSpan = GetLinePositionSpan(firstToken, source, node.SpanStart); var endPositionSpan = GetLinePositionSpan(lastToken, source, node.SpanStart); + if (endPositionSpan.End < startPositionSpan.Start) + { + linePositionSpan = default; + return false; + } + linePositionSpan = new LinePositionSpan(startPositionSpan.Start, endPositionSpan.End); return true; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Formatting/CSharpFormatter.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Formatting/CSharpFormatter.cs index 1ee75e5c00a..eefe355bdbb 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Formatting/CSharpFormatter.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Formatting/CSharpFormatter.cs @@ -106,9 +106,15 @@ static void ExtractTriviaAnnotations( var formattedTriviaList = formattedRoot.GetAnnotatedTrivia(MarkerId); foreach (var trivia in formattedTriviaList) { - // We only expect one annotation because we built the entire trivia with a single annotation. - var annotation = trivia.GetAnnotations(MarkerId).Single(); - if (!int.TryParse(annotation.Data, out var projectedIndex)) + // We only expect one annotation because we built the entire trivia with a single annotation, but + // we need to be defensive here. Annotations are a little hard to work with though, so apologies for + // the slightly odd method of validation. + using var enumerator = trivia.GetAnnotations(MarkerId).GetEnumerator(); + enumerator.MoveNext(); + var annotation = enumerator.Current; + // We shouldn't be able to enumerate any more, and we should be able to parse our data out of the annotation. + if (enumerator.MoveNext() || + !int.TryParse(annotation.Data, out var projectedIndex)) { // This shouldn't happen realistically unless someone messed with the annotations we added. // Let's ignore this annotation. diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/HtmlFormattingTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/HtmlFormattingTest.cs index 53272eea4e7..fb92bc1c740 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/HtmlFormattingTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/HtmlFormattingTest.cs @@ -440,6 +440,46 @@ @typeparam TValue } } + [FormattingTestFact] + public async Task PreprocessorDirectives() + { + await RunFormattingTestAsync( + input: """ +
+
+ @{ + #if DEBUG + } +
+ @{ + #endif + } +
+ + @code { + private object SomeModel {get;set;} + } + """, + expected: """ +
+
+ @{ + #if DEBUG + } +
+ @{ + #endif + + } +
+ + @code { + private object SomeModel { get; set; } + } + """, + allowDiagnostics: true); + } + private ImmutableArray GetComponents() { AdditionalSyntaxTrees.Add(Parse("""