Skip to content

Commit ca7db97

Browse files
authored
Handle diagnostic spans that cover an entire attribute value (#12302)
Fixes https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2596952 This is all my fault, in my previous PR I did not create good test data, so missed some logic for when the diagnostic spans the entire attribute value.
2 parents a6db5ba + cf3494e commit ca7db97

File tree

2 files changed

+65
-6
lines changed

2 files changed

+65
-6
lines changed

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Diagnostics/RazorTranslateDiagnosticsService.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,12 +422,31 @@ private static bool InAttributeContainingCSharp(
422422
return false;
423423
}
424424

425-
var owner = syntaxTree.FindInnermostNode(sourceText, diagnostic.Range.End);
425+
if (!sourceText.TryGetAbsoluteIndex(diagnostic.Range.End, out var absoluteIndex))
426+
{
427+
return false;
428+
}
429+
430+
var owner = syntaxTree.Root.FindInnermostNode(absoluteIndex);
426431
if (owner is null)
427432
{
428433
return false;
429434
}
430435

436+
// If the owner is the close quote of an attribute value, then we need to move to the previous position, as
437+
// the closing quote is actually owned by the whole attribute node. This will put us in the actual attribute
438+
// value node for sure, and as long as the diagnostic range isn't zero-width, shouldn't affect semantics.
439+
if (absoluteIndex > 0 &&
440+
diagnostic.Range.Start != diagnostic.Range.End &&
441+
owner is MarkupTextLiteralSyntax { LiteralTokens: [{ Content: "\"" or "'" }], Parent: MarkupTagHelperAttributeSyntax or MarkupAttributeBlockSyntax })
442+
{
443+
owner = syntaxTree.Root.FindInnermostNode(absoluteIndex - 1);
444+
if (owner is null)
445+
{
446+
return false;
447+
}
448+
}
449+
431450
var markupAttributeValue = owner.FirstAncestorOrSelf<RazorSyntaxNode>(static n =>
432451
(n.Parent is MarkupAttributeBlockSyntax block && n == block.Value) ||
433452
n is MarkupTagHelperAttributeValueSyntax or MarkupMiscAttributeContentSyntax);

src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentPullDiagnosticsTest.cs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -378,9 +378,10 @@ public Task FilterPropertyValueInCss()
378378
[Fact]
379379
public Task FilterPropertyNameInCss()
380380
{
381-
TestCode input = """
382-
<div style="{|CSS024:/|}****/"></div>
383-
<div style="@(someBool ? "width: 100%" : "width: 50%")">
381+
const string CSharpExpression = """@(someBool ? "width: 100%" : "width: 50%")""";
382+
TestCode input = $$"""
383+
<div style="{|CSS024:/****/|}"></div>
384+
<div style="{{CSharpExpression}}">
384385
385386
</div>
386387
@@ -398,12 +399,51 @@ public Task FilterPropertyNameInCss()
398399
new LspDiagnostic
399400
{
400401
Code = CSSErrorCodes.MissingPropertyName,
401-
Range = SourceText.From(input.Text).GetRange(new TextSpan(input.Text.IndexOf("/"), 1))
402+
Range = SourceText.From(input.Text).GetRange(new TextSpan(input.Text.IndexOf("/"), "/****/".Length))
402403
},
403404
new LspDiagnostic
404405
{
405406
Code = CSSErrorCodes.MissingPropertyName,
406-
Range = SourceText.From(input.Text).GetRange(new TextSpan(input.Text.IndexOf("@"), 1))
407+
Range = SourceText.From(input.Text).GetRange(new TextSpan(input.Text.IndexOf("@"), CSharpExpression.Length))
408+
},
409+
]
410+
}]);
411+
}
412+
413+
[Theory]
414+
[InlineData("", "\"")]
415+
[InlineData("", "'")]
416+
[InlineData("@onclick=\"Send\"", "\"")] // The @onclick makes the disabled attribute a TagHelperAttributeSyntax
417+
[InlineData("@onclick='Send'", "'")]
418+
public Task FilterBadAttributeValueInHtml(string extraTagContent, string quoteChar)
419+
{
420+
TestCode input = $$"""
421+
<button {{extraTagContent}} disabled={{quoteChar}}@(!EnableMyButton){{quoteChar}}>Send</button>
422+
<button disabled={{quoteChar}}{|HTML0209:ThisIsNotValid|}{{quoteChar}} />
423+
424+
@code
425+
{
426+
private bool EnableMyButton => true;
427+
428+
Task Send() =>
429+
Task.CompletedTask;
430+
}
431+
""";
432+
433+
return VerifyDiagnosticsAsync(input,
434+
htmlResponse: [new VSInternalDiagnosticReport
435+
{
436+
Diagnostics =
437+
[
438+
new LspDiagnostic
439+
{
440+
Code = HtmlErrorCodes.UnknownAttributeValueErrorCode,
441+
Range = SourceText.From(input.Text).GetRange(new TextSpan(input.Text.IndexOf("@("), "@(!EnableMyButton)".Length))
442+
},
443+
new LspDiagnostic
444+
{
445+
Code = HtmlErrorCodes.UnknownAttributeValueErrorCode,
446+
Range = SourceText.From(input.Text).GetRange(new TextSpan(input.Text.IndexOf("T"), "ThisIsNotValid".Length))
407447
},
408448
]
409449
}]);

0 commit comments

Comments
 (0)