Example #1
0
        public override void VisitForVariableBlockSyntax(ForVariableBlockSyntax syntax) =>
        this.Build(() => base.VisitForVariableBlockSyntax(syntax), children =>
        {
            Debug.Assert(children.Length == 5);

            ILinkedDocument openParen     = children[0];
            ILinkedDocument itemVariable  = children[1];
            ILinkedDocument comma         = children[2];
            ILinkedDocument indexVariable = children[3];
            ILinkedDocument closeParen    = children[4];

            return(Spread(Concat(openParen, itemVariable, comma), Concat(indexVariable, closeParen)));
        });
        protected override SyntaxBase ReplaceForSyntax(ForSyntax syntax)
        {
            syntax = (ForSyntax)base.ReplaceForSyntax(syntax);

            // look for range(0, length(<variable>))
            if (!IsLoopIteratorExpression(semanticModel, syntax.Expression, out var arrayVariable))
            {
                return(syntax);
            }

            if (syntax.VariableSection is not LocalVariableSyntax indexVariable)
            {
                return(syntax);
            }

            var arraySymbol      = semanticModel.GetSymbolInfo(arrayVariable);
            var arrayIndexSymbol = semanticModel.GetSymbolInfo(indexVariable);

            if (arraySymbol is null || arrayIndexSymbol is null)
            {
                return(syntax);
            }

            var arrayAccesses            = new HashSet <ArrayAccessSyntax>();
            var independentIndexAccesses = new HashSet <VariableAccessSyntax>();

            CallbackVisitor.Visit(syntax, child =>
            {
                if (child is ArrayAccessSyntax arrayAccess)
                {
                    if (semanticModel.GetSymbolInfo(arrayAccess.BaseExpression) == arraySymbol &&
                        semanticModel.GetSymbolInfo(arrayAccess.IndexExpression) == arrayIndexSymbol)
                    {
                        arrayAccesses.Add(arrayAccess);

                        // we don't want to count the VariableAccessSyntax under this particular node,
                        // so return false to skip visiting children.
                        return(false);
                    }
                }

                if (child is VariableAccessSyntax variableAccess)
                {
                    var accessSymbol = semanticModel.GetSymbolInfo(variableAccess);
                    if (accessSymbol == arrayIndexSymbol)
                    {
                        independentIndexAccesses.Add(variableAccess);
                    }
                }

                return(true);
            });

            if (!arrayAccesses.Any())
            {
                // nothing to really simplify here
                return(syntax);
            }

            var itemVarName = GetUniqueVariableNameForNewScope(syntax, "item");
            var forBody     = CallbackRewriter.Rewrite(syntax.Body, child =>
            {
                if (arrayAccesses.Contains(child))
                {
                    return(new VariableAccessSyntax(SyntaxFactory.CreateIdentifier(itemVarName)));
                }

                return(child);
            });

            SyntaxBase forVariableBlockSyntax;

            if (independentIndexAccesses.Any())
            {
                forVariableBlockSyntax = new ForVariableBlockSyntax(
                    SyntaxFactory.LeftParenToken,
                    new LocalVariableSyntax(SyntaxFactory.CreateIdentifier(itemVarName)),
                    SyntaxFactory.CommaToken,
                    new LocalVariableSyntax(SyntaxFactory.CreateIdentifier(arrayIndexSymbol.Name)),
                    SyntaxFactory.RightParenToken);
            }
            else
            {
                forVariableBlockSyntax = new LocalVariableSyntax(SyntaxFactory.CreateIdentifier(itemVarName));
            }

            var forExpression = new VariableAccessSyntax(SyntaxFactory.CreateIdentifier(arraySymbol.Name));

            return(new ForSyntax(
                       syntax.OpenSquare,
                       syntax.ForKeyword,
                       forVariableBlockSyntax,
                       syntax.InKeyword,
                       forExpression,
                       syntax.Colon,
                       forBody,
                       syntax.CloseSquare));
        }