| File: Simplification\Reducers\AbstractCSharpReducer.AbstractReductionRewriter.cs | Web Access |
| Project: src\roslyn\src\Workspaces\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Workspaces.csproj (Microsoft.CodeAnalysis.CSharp.Workspaces) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; namespace Microsoft.CodeAnalysis.CSharp.Simplification; internal abstract partial class AbstractCSharpReducer { protected abstract class AbstractReductionRewriter : CSharpSyntaxRewriter, IReductionRewriter { private readonly ObjectPool<IReductionRewriter> _pool; protected CSharpParseOptions? ParseOptions { get; private set; } protected CSharpSimplifierOptions? Options { get; private set; } protected CancellationToken CancellationToken { get; private set; } protected SemanticModel? SemanticModel { get; private set; } public bool HasMoreWork { get; private set; } // can be used to simplify whole subtrees while just annotating one syntax node. // This is e.g. useful in the name simplification, where a whole qualified name is annotated protected bool alwaysSimplify; private readonly HashSet<SyntaxNode> _processedParentNodes = []; protected AbstractReductionRewriter(ObjectPool<IReductionRewriter> pool) => _pool = pool; public void Initialize(ParseOptions parseOptions, SimplifierOptions options, CancellationToken cancellationToken) { Contract.ThrowIfNull(options); ParseOptions = (CSharpParseOptions)parseOptions; Options = (CSharpSimplifierOptions)options; CancellationToken = cancellationToken; } public void Dispose() { ParseOptions = null; Options = null; CancellationToken = CancellationToken.None; _processedParentNodes.Clear(); SemanticModel = null; HasMoreWork = false; alwaysSimplify = false; _pool.Free(this); } [MemberNotNull(nameof(Options), nameof(ParseOptions), nameof(SemanticModel))] public void RequireInitialized() { Contract.ThrowIfNull(ParseOptions); Contract.ThrowIfNull(Options); Contract.ThrowIfNull(SemanticModel); } private static SyntaxNode GetParentNode(SyntaxNode node) => node switch { ExpressionSyntax expression => GetParentNode(expression), PatternSyntax pattern => GetParentNode(pattern), CrefSyntax cref => GetParentNode(cref), _ => node.GetRequiredParent(), }; private static SyntaxNode GetParentNode(ExpressionSyntax expression) { // Walk all the way up the expression to the non-expression parent. Effectively, once we change an // expression *within* some larger expression context, we want to stop rewriting any further sibling // expressions as they could be affected by this change. SyntaxNode parent = expression; for (var current = (SyntaxNode)expression; current != null; current = current.Parent) { // if we're in an argument, walk up into that as well as the change in one argument can affect // other arguments in a call. if (current is ExpressionSyntax or ArgumentSyntax) parent = current; } return parent.GetRequiredParent(); } private static SyntaxNode GetParentNode(PatternSyntax pattern) { var lastPattern = pattern; for (SyntaxNode? current = pattern; current != null; current = current.Parent) { if (current is PatternSyntax currentPattern) { lastPattern = currentPattern; } } Contract.ThrowIfNull(lastPattern.Parent); return lastPattern.Parent; } private static SyntaxNode GetParentNode(CrefSyntax cref) { var topMostCref = cref .AncestorsAndSelf() .OfType<CrefSyntax>() .Last(); Contract.ThrowIfNull(topMostCref.Parent); return topMostCref.Parent; } protected SyntaxNode? SimplifyNode<TNode>( TNode node, SyntaxNode? newNode, Func<TNode, SemanticModel, CSharpSimplifierOptions, CancellationToken, SyntaxNode> simplifier) where TNode : SyntaxNode { var parentNode = GetParentNode(node); RequireInitialized(); this.CancellationToken.ThrowIfCancellationRequested(); if (!this.alwaysSimplify && !node.HasAnnotation(Simplifier.Annotation)) { return newNode; } if (node != newNode || _processedParentNodes.Contains(parentNode)) { this.HasMoreWork = true; return newNode; } if (!node.HasAnnotation(SimplificationHelpers.DoNotSimplifyAnnotation)) { var simplifiedNode = simplifier(node, this.SemanticModel, this.Options, this.CancellationToken); if (simplifiedNode != node) { _processedParentNodes.Add(parentNode); this.HasMoreWork = true; return simplifiedNode; } } return node; } protected SyntaxToken SimplifyToken(SyntaxToken token, Func<SyntaxToken, SemanticModel, CSharpSimplifierOptions, CancellationToken, SyntaxToken> simplifier) { RequireInitialized(); this.CancellationToken.ThrowIfCancellationRequested(); return token.HasAnnotation(Simplifier.Annotation) ? simplifier(token, this.SemanticModel, this.Options, this.CancellationToken) : token; } public override SyntaxNode VisitElementAccessExpression(ElementAccessExpressionSyntax node) { // Note that we prefer simplifying the argument list before the expression var argumentList = (BracketedArgumentListSyntax)this.Visit(node.ArgumentList); var expression = (ExpressionSyntax)this.Visit(node.Expression); return node.Update(expression, argumentList); } public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node) { // Note that we prefer simplifying the argument list before the expression var argumentList = (ArgumentListSyntax)this.Visit(node.ArgumentList); var expression = (ExpressionSyntax)this.Visit(node.Expression); return node.Update(expression, argumentList); } public SyntaxNodeOrToken VisitNodeOrToken(SyntaxNodeOrToken nodeOrToken, SemanticModel semanticModel, bool simplifyAllDescendants) { this.SemanticModel = semanticModel; this.alwaysSimplify = simplifyAllDescendants; this.HasMoreWork = false; _processedParentNodes.Clear(); if (nodeOrToken.IsNode) { return Visit(nodeOrToken.AsNode()); } else { return VisitToken(nodeOrToken.AsToken()); } } } }