-
Notifications
You must be signed in to change notification settings - Fork 199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove more LocateOwner usage #9132
Changes from 4 commits
8e2f7a7
304bf37
9fdffc1
c3afa5c
fae78d4
e875dab
3ee49ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions; | ||
using Microsoft.AspNetCore.Razor.LanguageServer.Protocol; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Razor.Workspaces.Extensions; | ||
using Microsoft.Extensions.Logging; | ||
using Diagnostic = Microsoft.VisualStudio.LanguageServer.Protocol.Diagnostic; | ||
using DiagnosticSeverity = Microsoft.VisualStudio.LanguageServer.Protocol.DiagnosticSeverity; | ||
|
@@ -120,7 +121,7 @@ private static Diagnostic[] FilterHTMLDiagnostics( | |
|
||
var filteredDiagnostics = unmappedDiagnostics | ||
.Where(d => | ||
!InCSharpLiteral(d, sourceText, syntaxTree, logger) && | ||
!InCSharpLiteral(d, sourceText, syntaxTree) && | ||
!InAttributeContainingCSharp(d, sourceText, syntaxTree, processedAttributes, logger) && | ||
!AppliesToTagHelperTagName(d, sourceText, syntaxTree, logger) && | ||
!ShouldFilterHtmlDiagnosticBasedOnErrorCode(d, sourceText, syntaxTree, logger)) | ||
|
@@ -162,23 +163,34 @@ private Diagnostic[] MapDiagnostics( | |
private static bool InCSharpLiteral( | ||
Diagnostic d, | ||
SourceText sourceText, | ||
RazorSyntaxTree syntaxTree, | ||
ILogger logger) | ||
RazorSyntaxTree syntaxTree) | ||
{ | ||
if (d.Range is null) | ||
{ | ||
return false; | ||
} | ||
|
||
var owner = syntaxTree.GetOwner(sourceText, d.Range.End, logger); | ||
if (owner is null) | ||
|
||
var owner = syntaxTree.Root.FindNode(d.Range.AsRazorTextSpan(sourceText), getInnermostNodeForTie: true); | ||
if (IsCsharpKind(owner)) | ||
{ | ||
return false; | ||
return true; | ||
} | ||
|
||
var isCSharp = owner.Kind is SyntaxKind.CSharpExpressionLiteral or SyntaxKind.CSharpStatementLiteral or SyntaxKind.CSharpEphemeralTextLiteral; | ||
if (owner is CSharpImplicitExpressionSyntax implicitExpressionSyntax | ||
&& implicitExpressionSyntax.Body is CSharpImplicitExpressionBodySyntax bodySyntax | ||
&& bodySyntax.CSharpCode is CSharpCodeBlockSyntax codeBlock) | ||
{ | ||
return codeBlock.Children.Count == 1 | ||
&& IsCsharpKind(codeBlock.Children[0]); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this whole thing is possibly:
though I'm not sure thats actually readable in any way, so this is not a suggestion to change it necessarily, I just enjoy these trivia challenges. Also since when did the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This was from using the Benefit of this style is for diffing, so you only see the line being added/removed, and don't mess with others. Similar to wrapping arguments with static void Method(
arg1
, arg2
, arg3 Although for methods and enums I'd prefer to just leave a trailing comma if possible There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Similar to " is not the argument you think it is 😛 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally, I prefer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
return false; | ||
|
||
return isCSharp; | ||
static bool IsCsharpKind([NotNullWhen(true)] SyntaxNode? node) | ||
=> node?.Kind is SyntaxKind.CSharpExpressionLiteral | ||
or SyntaxKind.CSharpStatementLiteral | ||
or SyntaxKind.CSharpEphemeralTextLiteral; | ||
} | ||
|
||
private static bool AppliesToTagHelperTagName( | ||
|
@@ -201,7 +213,7 @@ private static bool AppliesToTagHelperTagName( | |
return false; | ||
} | ||
|
||
var owner = syntaxTree.GetOwner(sourceText, diagnostic.Range.End, logger); | ||
var owner = syntaxTree.FindInnermostNode(sourceText, diagnostic.Range.End, logger); | ||
|
||
var startOrEndTag = owner?.FirstAncestorOrSelf<RazorSyntaxNode>(n => n is MarkupTagHelperStartTagSyntax || n is MarkupTagHelperEndTagSyntax); | ||
if (startOrEndTag is null) | ||
|
@@ -246,7 +258,7 @@ private static bool ShouldFilterHtmlDiagnosticBasedOnErrorCode(Diagnostic diagno | |
static bool IsCSharpInStyleBlock(Diagnostic diagnostic, SourceText sourceText, RazorSyntaxTree syntaxTree, ILogger logger) | ||
{ | ||
// C# in a style block causes diagnostics because the HTML background document replaces C# with "~" | ||
var owner = syntaxTree.GetOwner(sourceText, diagnostic.Range.Start, logger); | ||
var owner = syntaxTree.FindInnermostNode(sourceText, diagnostic.Range.Start, logger); | ||
if (owner is null) | ||
{ | ||
return false; | ||
|
@@ -262,7 +274,7 @@ static bool IsCSharpInStyleBlock(Diagnostic diagnostic, SourceText sourceText, R | |
// but we don't currently have a system to accomplish that | ||
static bool IsAnyFilteredTooFewElementsError(Diagnostic diagnostic, SourceText sourceText, RazorSyntaxTree syntaxTree, ILogger logger) | ||
{ | ||
var owner = syntaxTree.GetOwner(sourceText, diagnostic.Range.Start, logger); | ||
var owner = syntaxTree.FindInnermostNode(sourceText, diagnostic.Range.Start, logger); | ||
if (owner is null) | ||
{ | ||
return false; | ||
|
@@ -291,7 +303,7 @@ static bool IsAnyFilteredTooFewElementsError(Diagnostic diagnostic, SourceText s | |
// but we don't currently have a system to accomplish that | ||
static bool IsHtmlWithBangAndMatchingTags(Diagnostic diagnostic, SourceText sourceText, RazorSyntaxTree syntaxTree, ILogger logger) | ||
{ | ||
var owner = syntaxTree.GetOwner(sourceText, diagnostic.Range.Start, logger); | ||
var owner = syntaxTree.FindInnermostNode(sourceText, diagnostic.Range.Start, logger); | ||
if (owner is null) | ||
{ | ||
return false; | ||
|
@@ -319,7 +331,7 @@ static bool IsAnyFilteredInvalidNestingError(Diagnostic diagnostic, SourceText s | |
|
||
static bool IsInvalidNestingWarningWithinComponent(Diagnostic diagnostic, SourceText sourceText, RazorSyntaxTree syntaxTree, ILogger logger) | ||
{ | ||
var owner = syntaxTree.GetOwner(sourceText, diagnostic.Range.Start, logger); | ||
var owner = syntaxTree.FindInnermostNode(sourceText, diagnostic.Range.Start, logger); | ||
if (owner is null) | ||
{ | ||
return false; | ||
|
@@ -334,7 +346,7 @@ static bool IsInvalidNestingWarningWithinComponent(Diagnostic diagnostic, Source | |
// but we don't currently have a system to accomplish that | ||
static bool IsInvalidNestingFromBody(Diagnostic diagnostic, SourceText sourceText, RazorSyntaxTree syntaxTree, ILogger logger) | ||
{ | ||
var owner = syntaxTree.GetOwner(sourceText, diagnostic.Range.Start, logger); | ||
var owner = syntaxTree.FindInnermostNode(sourceText, diagnostic.Range.Start, logger); | ||
if (owner is null) | ||
{ | ||
return false; | ||
|
@@ -371,7 +383,7 @@ private static bool InAttributeContainingCSharp( | |
return false; | ||
} | ||
|
||
var owner = syntaxTree.GetOwner(sourceText, diagnostic.Range.End, logger); | ||
var owner = syntaxTree.FindInnermostNode(sourceText, diagnostic.Range.End, logger); | ||
if (owner is null) | ||
{ | ||
return false; | ||
|
@@ -497,7 +509,8 @@ private bool TryRemapRudeEditRange(Range diagnosticRange, RazorCodeDocument code | |
// semi-intelligent way. | ||
|
||
var syntaxTree = codeDocument.GetSyntaxTree(); | ||
var owner = syntaxTree.GetOwner(sourceText, diagnosticRange, logger: _logger); | ||
var span = diagnosticRange.AsRazorTextSpan(codeDocument.GetSourceText()); | ||
var owner = syntaxTree.Root.FindNode(span, getInnermostNodeForTie: true); | ||
|
||
switch (owner?.Kind) | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
using System; | ||
using System.Diagnostics; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using Microsoft.AspNetCore.Razor.Language; | ||
using Microsoft.AspNetCore.Razor.Language.Syntax; | ||
using Microsoft.AspNetCore.Razor.LanguageServer.Formatting; | ||
|
@@ -328,5 +329,60 @@ public static int GetTrailingWhitespaceLength(this SyntaxNode node, FormattingCo | |
} | ||
|
||
public static SyntaxNode? FindInnermostNode(this SyntaxNode node, int index, bool includeWhitespace = false) | ||
=> node.FindToken(index, includeWhitespace)?.Parent; | ||
{ | ||
var token = node.FindToken(index, includeWhitespace); | ||
|
||
// If the index is EOF but the node has index-1, | ||
// then try to get a token to the left of the index. | ||
// patterns like | ||
// <button></button>$$ | ||
// should get the button node instead of the razor document (which is the parent | ||
// of the EOF token) | ||
if (token.Kind == SyntaxKind.EndOfFile && node.Span.Contains(index - 1)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this is the right answer. Would love thoughts on this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i guess this makes sense. if there were whitespace at the end, i think the compiler method would do the same thing. Maybe @333fred has opinions though There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes. This is the type of heuristic that I would expect the IDE to need to do, but with |
||
{ | ||
token = node.FindToken(index - 1, includeWhitespace); | ||
} | ||
|
||
return token.Parent; | ||
} | ||
|
||
public static SyntaxNode? FindNode(this SyntaxNode @this, Language.Syntax.TextSpan span, bool includeWhitespace = false, bool getInnermostNodeForTie = false) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shamelessly copied from Roslyn. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks scary, and seems like it only has one caller, which is surprising and doesn't fill me with joy, but the fact that its copied from Roslyn means I'm going to assume its fine :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I should probably add a lot of tests There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was wondering about that, and also wondering if this should move down to the compiler, next to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can certainly move this down to the compiler, but yes the initial There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC, |
||
{ | ||
if (!@this.FullSpan.Contains(span)) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(span)); | ||
} | ||
|
||
var node = @this.FindToken(span.Start, includeWhitespace) | ||
.Parent | ||
!.FirstAncestorOrSelf<SyntaxNode>(a => a.FullSpan.Contains(span)); | ||
|
||
node.AssumeNotNull(); | ||
|
||
// Tie-breaking. | ||
if (!getInnermostNodeForTie) | ||
{ | ||
var cuRoot = node.Ancestors().Last(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @333fred: Should there be a cheaper way to retrieve the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was surprised there wasn't as well when I implemented |
||
|
||
while (true) | ||
{ | ||
var parent = node.Parent; | ||
// NOTE: We care about FullSpan equality, but FullWidth is cheaper and equivalent. | ||
if (parent == null || parent.FullWidth != node.FullWidth) | ||
{ | ||
break; | ||
} | ||
|
||
// prefer child over compilation unit | ||
if (parent == cuRoot) | ||
{ | ||
break; | ||
} | ||
|
||
node = parent; | ||
} | ||
} | ||
|
||
return node; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, I added another
FindNode
usage!There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, I can't explain why it doesn't find the
CSharpCodeBlockSyntax
with the same span. This could be a difference in how Roslyn and Razor make trees/how FindToken works.My lack of understanding also means I don't think I can make a good comment. I just know it works and all the tests are passing