| File: RouteEmbeddedLanguage\Infrastructure\SyntaxNodeExtensions.cs | Web Access |
| Project: src\aspnetcore\src\Framework\AspNetCoreAnalyzers\src\Analyzers\Microsoft.AspNetCore.App.Analyzers.csproj (Microsoft.AspNetCore.App.Analyzers) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Linq; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure; internal static class SyntaxNodeExtensions { /// <summary> /// Look inside a trivia list for a skipped token that contains the given position. /// </summary> private static readonly Func<SyntaxTriviaList, int, SyntaxToken> FindSkippedTokenBackwardFunc = FindSkippedTokenBackward; public static SyntaxNode GetRequiredParent(this SyntaxNode node) => node.Parent ?? throw new InvalidOperationException("Node's parent was null"); public static SyntaxNode? GetParent(this SyntaxNode node, bool ascendOutOfTrivia) { var parent = node.Parent; if (parent == null && ascendOutOfTrivia) { if (node is IStructuredTriviaSyntax structuredTrivia) { parent = structuredTrivia.ParentTrivia.Token.Parent; } } return parent; } public static bool IsLiteralExpression([NotNullWhen(true)] this SyntaxNode? node) => node is LiteralExpressionSyntax; public static bool IsBinaryExpression([NotNullWhen(true)] this SyntaxNode? node) => node is BinaryExpressionSyntax; [return: NotNullIfNotNull("node")] public static SyntaxNode? WalkUpParentheses(this SyntaxNode? node) { while (node?.Parent?.RawKind == (int)SyntaxKind.ParenthesizedExpression) { node = node.Parent; } return node; } public static bool IsAnyInitializerExpression([NotNullWhen(true)] this SyntaxNode? node, [NotNullWhen(true)] out SyntaxNode? creationExpression) { if (node is InitializerExpressionSyntax { Parent: BaseObjectCreationExpressionSyntax or ArrayCreationExpressionSyntax or ImplicitArrayCreationExpressionSyntax }) { creationExpression = node.Parent; return true; } creationExpression = null; return false; } public static bool IsSimpleAssignmentStatement([NotNullWhen(true)] this SyntaxNode? statement) => statement is ExpressionStatementSyntax exprStatement && exprStatement.Expression.IsKind(SyntaxKind.SimpleAssignmentExpression); /// <summary> /// If the position is inside of token, return that token; otherwise, return the token to the left. /// </summary> public static SyntaxToken FindTokenOnLeftOfPosition( this SyntaxNode root, int position, bool includeSkipped = false, bool includeDirectives = false, bool includeDocumentationComments = false) { var findSkippedToken = includeSkipped ? FindSkippedTokenBackwardFunc : ((l, p) => default); var token = GetInitialToken(root, position, includeSkipped, includeDirectives, includeDocumentationComments); if (position <= token.SpanStart) { do { var skippedToken = findSkippedToken(token.LeadingTrivia, position); token = skippedToken.RawKind != 0 ? skippedToken : token.GetPreviousToken(includeZeroWidth: false, includeSkipped: includeSkipped, includeDirectives: includeDirectives, includeDocumentationComments: includeDocumentationComments); } while (position <= token.SpanStart && root.FullSpan.Start < token.SpanStart); } else if (token.Span.End < position) { var skippedToken = findSkippedToken(token.TrailingTrivia, position); token = skippedToken.RawKind != 0 ? skippedToken : token; } if (token.Span.Length == 0) { token = token.GetPreviousToken(); } return token; } private static SyntaxToken GetInitialToken( SyntaxNode root, int position, bool includeSkipped = false, bool includeDirectives = false, bool includeDocumentationComments = false) { return position < root.FullSpan.End || !(root is ICompilationUnitSyntax) ? root.FindToken(position, includeSkipped || includeDirectives || includeDocumentationComments) : root.GetLastToken(includeZeroWidth: true, includeSkipped: true, includeDirectives: true, includeDocumentationComments: true) .GetPreviousToken(includeZeroWidth: false, includeSkipped: includeSkipped, includeDirectives: includeDirectives, includeDocumentationComments: includeDocumentationComments); } /// <summary> /// Look inside a trivia list for a skipped token that contains the given position. /// </summary> private static SyntaxToken FindSkippedTokenBackward(SyntaxTriviaList triviaList, int position) { foreach (var trivia in Enumerable.Reverse(triviaList)) { if (trivia.HasStructure) { if (trivia.GetStructure() is ISkippedTokensTriviaSyntax skippedTokensTrivia) { foreach (var token in skippedTokensTrivia.Tokens) { if (token.Span.Length > 0 && token.SpanStart <= position) { return token; } } } } } return default; } }