File: Simplification\CSharpSimplificationService.NodesAndTokensToReduceComputer.cs
Web Access
Project: src\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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Simplification;
 
namespace Microsoft.CodeAnalysis.CSharp.Simplification;
 
internal partial class CSharpSimplificationService
{
    private class NodesAndTokensToReduceComputer : CSharpSyntaxRewriter
    {
        private readonly List<NodeOrTokenToReduce> _nodesAndTokensToReduce = [];
        private readonly Func<SyntaxNodeOrToken, bool> _isNodeOrTokenOutsideSimplifySpans;
 
        private bool _simplifyAllDescendants;
        private bool _insideSpeculatedNode;
 
        /// <summary>
        /// Computes a list of nodes and tokens that need to be reduced in the given syntax root.
        /// </summary>
        public static ImmutableArray<NodeOrTokenToReduce> Compute(SyntaxNode root, Func<SyntaxNodeOrToken, bool> isNodeOrTokenOutsideSimplifySpans)
        {
            var reduceNodeComputer = new NodesAndTokensToReduceComputer(isNodeOrTokenOutsideSimplifySpans);
            reduceNodeComputer.Visit(root);
            return [.. reduceNodeComputer._nodesAndTokensToReduce];
        }
 
        private NodesAndTokensToReduceComputer(Func<SyntaxNodeOrToken, bool> isNodeOrTokenOutsideSimplifySpans)
            : base(visitIntoStructuredTrivia: true)
        {
            _isNodeOrTokenOutsideSimplifySpans = isNodeOrTokenOutsideSimplifySpans;
            _simplifyAllDescendants = false;
            _insideSpeculatedNode = false;
        }
 
        public override SyntaxNode Visit(SyntaxNode node)
        {
            if (node == null)
            {
                return node;
            }
 
            if (_isNodeOrTokenOutsideSimplifySpans(node))
            {
                if (_simplifyAllDescendants)
                {
                    // One of the ancestor node is within a simplification span, but this node is outside all simplification spans.
                    // Add DoNotSimplifyAnnotation to node to ensure it doesn't get simplified.
                    return node.WithAdditionalAnnotations(SimplificationHelpers.DoNotSimplifyAnnotation);
                }
                else
                {
                    return node;
                }
            }
 
            var savedSimplifyAllDescendants = _simplifyAllDescendants;
            _simplifyAllDescendants = _simplifyAllDescendants || node.HasAnnotation(Simplifier.Annotation);
 
            if (!_insideSpeculatedNode && SpeculationAnalyzer.CanSpeculateOnNode(node))
            {
                if (_simplifyAllDescendants || node.DescendantNodesAndTokens(s_containsAnnotations, descendIntoTrivia: true).Any(s_hasSimplifierAnnotation))
                {
                    _insideSpeculatedNode = true;
                    var rewrittenNode = base.Visit(node);
                    _nodesAndTokensToReduce.Add(new NodeOrTokenToReduce(rewrittenNode, _simplifyAllDescendants, node));
                    _insideSpeculatedNode = false;
                }
            }
            else if (node.ContainsAnnotations || savedSimplifyAllDescendants)
            {
                node = base.Visit(node);
            }
 
            _simplifyAllDescendants = savedSimplifyAllDescendants;
            return node;
        }
 
        public override SyntaxToken VisitToken(SyntaxToken token)
        {
            if (_isNodeOrTokenOutsideSimplifySpans(token))
            {
                if (_simplifyAllDescendants)
                {
                    // One of the ancestor node is within a simplification span, but this token is outside all simplification spans.
                    // Add DoNotSimplifyAnnotation to token to ensure it doesn't get simplified.
                    return token.WithAdditionalAnnotations(SimplificationHelpers.DoNotSimplifyAnnotation);
                }
                else
                {
                    return token;
                }
            }
 
            var savedSimplifyAllDescendants = _simplifyAllDescendants;
            _simplifyAllDescendants = _simplifyAllDescendants || token.HasAnnotation(Simplifier.Annotation);
 
            if (_simplifyAllDescendants && !_insideSpeculatedNode && !token.IsKind(SyntaxKind.None))
            {
                _nodesAndTokensToReduce.Add(new NodeOrTokenToReduce(token, SimplifyAllDescendants: true, token));
            }
 
            if (token.ContainsAnnotations || savedSimplifyAllDescendants)
            {
                token = base.VisitToken(token);
            }
 
            _simplifyAllDescendants = savedSimplifyAllDescendants;
            return token;
        }
 
        public override SyntaxTrivia VisitTrivia(SyntaxTrivia trivia)
        {
            if (trivia.HasStructure)
            {
                var savedInsideSpeculatedNode = _insideSpeculatedNode;
                _insideSpeculatedNode = false;
                base.VisitTrivia(trivia);
                _insideSpeculatedNode = savedInsideSpeculatedNode;
            }
 
            return trivia;
        }
    }
}