From 423b886e9ffce508e56df0bc48c33947a76b5dc6 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 8 Jan 2026 11:35:56 +1100 Subject: [PATCH 1/2] Filter our html diagnostics when a tag helper attribute spans multiple lines --- .../RazorTranslateDiagnosticsService.cs | 14 +++-- .../CohostDocumentPullDiagnosticsTest.cs | 52 +++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs index 57c508300c6..5795590505f 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs @@ -453,13 +453,19 @@ private static bool InAttributeContainingCSharp( if (markupAttributeValue is not null) { - if (!processedAttributes.TryGetValue(markupAttributeValue.Span, out var doesAttributeContainNonMarkup)) + if (!processedAttributes.TryGetValue(markupAttributeValue.Span, out var shouldFilterDiagnostic)) { - doesAttributeContainNonMarkup = CheckIfAttributeContainsNonMarkupNodes(markupAttributeValue); - processedAttributes.Add(markupAttributeValue.Span, doesAttributeContainNonMarkup); + // If a compoennt attribute is spread across multiple lines, it's not valid Html so the Html server can't be expected to reason + // about the contents correctly + shouldFilterDiagnostic = markupAttributeValue is MarkupTagHelperAttributeValueSyntax && + markupAttributeValue.GetLinePositionSpan(syntaxTree.Source).SpansMultipleLines(); + + // Similarly, if the attribute value contains non-markup, the Html could report false positives + shouldFilterDiagnostic |= CheckIfAttributeContainsNonMarkupNodes(markupAttributeValue); + processedAttributes.Add(markupAttributeValue.Span, shouldFilterDiagnostic); } - return doesAttributeContainNonMarkup; + return shouldFilterDiagnostic; } return false; diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentPullDiagnosticsTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentPullDiagnosticsTest.cs index 563c963418c..29eb46212af 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentPullDiagnosticsTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentPullDiagnosticsTest.cs @@ -420,6 +420,58 @@ public Task FilterPropertyNameInCss() }]); } + [Fact] + public Task FilterFromMultilineComponentAttributes() + { + var firstLine = "Hello this is a"; + TestCode input = $$""" + + + @code + { + [Parameter] + public string Title { get; set; } + } + """; + + return VerifyDiagnosticsAsync(input, + htmlResponse: [new VSInternalDiagnosticReport + { + Diagnostics = + [ + new LspDiagnostic + { + Code = HtmlErrorCodes.MismatchedAttributeQuotesErrorCode, + Range = SourceText.From(input.Text).GetRange(new TextSpan(input.Text.IndexOf(firstLine), firstLine.Length)) + }, + ] + }]); + } + + [Fact] + public Task DontFilterFromMultilineHtmlAttributes() + { + var firstLine = "Hello this is a"; + TestCode input = $$""" +
+ """; + + return VerifyDiagnosticsAsync(input, + htmlResponse: [new VSInternalDiagnosticReport + { + Diagnostics = + [ + new LspDiagnostic + { + Code = HtmlErrorCodes.MismatchedAttributeQuotesErrorCode, + Range = SourceText.From(input.Text).GetRange(new TextSpan(input.Text.IndexOf(firstLine), firstLine.Length)) + }, + ] + }]); + } + [Theory] [InlineData("", "\"")] [InlineData("", "'")] From fe05bbd258c3e4720feb6313ac7ffe6eccc31a0d Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 9 Jan 2026 07:45:02 +1100 Subject: [PATCH 2/2] Update src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs Co-authored-by: Chris Sienkiewicz --- .../Diagnostics/RazorTranslateDiagnosticsService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs index 5795590505f..ada0dc0e66e 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs @@ -455,7 +455,7 @@ private static bool InAttributeContainingCSharp( { if (!processedAttributes.TryGetValue(markupAttributeValue.Span, out var shouldFilterDiagnostic)) { - // If a compoennt attribute is spread across multiple lines, it's not valid Html so the Html server can't be expected to reason + // If a component attribute is spread across multiple lines, it's not valid Html so the Html server can't be expected to reason // about the contents correctly shouldFilterDiagnostic = markupAttributeValue is MarkupTagHelperAttributeValueSyntax && markupAttributeValue.GetLinePositionSpan(syntaxTree.Source).SpansMultipleLines();