Repository: dotnet/roslyn
File: src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs (C#)

Code before:
                }
            }
        }

        /// <summary>
        /// Visits the given argument if necessary and adds it to the final arguments list. It is expected that <paramref name="finalArguments"/> is being built in reverse order, due to the left-recursive
        /// nature of the binary tree that we're traversing.
        /// </summary>
        /// <remarks>
        /// This method may end up deciding that the passed argument doesn't need to be included in the concat argument list (if, for example, it's a null constant or an empty string), and not add it
        /// to <paramref name="finalArguments"/>. It will also fold consecutive constant strings or chars into a single string constant, to avoid unnecessary concatenation. It may also do other optimizations,
        /// such as deconstructing nested string.Concat calls.
        /// </remarks>
        private void VisitAndAddConcatArgumentInReverseOrder(BoundExpression argument, bool argumentAlreadyVisited, ArrayBuilder<BoundExpression> finalArguments, ref WellKnownConcatRelatedMethods wellKnownConcatOptimizationMethods)
        {
            Debug.Assert(argument is not BoundBinaryOperator { InterpolatedStringHandlerData: null } op || !IsBinaryStringConcatenation(op));
            if (!argumentAlreadyVisited)
            {
                argument = VisitExpression(argument);
            }

            var precedingArgument = finalArguments.Count > 0 ? finalArguments[^1] : null;
            var (singleConcatArgument, nestedConcatArguments) = SimplifyConcatArgument(argument, ref precedingArgument, ref wellKnownConcatOptimizationMethods);

            // We should only get one result from simplification; either a single argument to add, or multiple nested arguments to add, or the current argument was optimized away
            // This last option can either mean that the argument was truly empty, and was dropped entirely, or that it was merged into the preceding argument, in which case we updated that argument in place and don't need to add anything new
            if (singleConcatArgument is null && nestedConcatArguments.IsDefault)
            {
                if (precedingArgument is not null)
                {
                    // We may have merged the current argument into the preceding argument, so we need to update that in the final arguments list
                    // If we didn't do any merging, then this is a no-op
                    finalArguments[^1] = precedingArgument;
                }

                return;
            }

            Debug.Assert(precedingArgument is null || ReferenceEquals(precedingArgument, finalArguments[^1]));
            Debug.Assert(singleConcatArgument is null ^ nestedConcatArguments.IsDefault);

            if (singleConcatArgument is not null)
            {
                finalArguments.Add(singleConcatArgument);
            }
            else
            {
                for (int i = nestedConcatArguments.Length - 1; i >= 0; i--)
                {
                    VisitAndAddConcatArgumentInReverseOrder(nestedConcatArguments[i], argumentAlreadyVisited: true, finalArguments, ref wellKnownConcatOptimizationMethods);
                }

Review comment (suggestion):
>in which case we updated that argument in place and don't need to add anything new [](http://example.com/codeflow?start=160&length=82)

This looks somewhat misleading because we do need to update the builder #Closed

Code after:
            }
        }

        /// <summary>
        /// Visits the given argument if necessary and adds it to the final arguments list. It is expected that <paramref name="finalArguments"/> is being built in reverse order, due to the left-recursive
        /// nature of the binary tree that we're traversing.
        /// </summary>
        /// <remarks>
        /// This method may end up deciding that the passed argument doesn't need to be included in the concat argument list (if, for example, it's a null constant or an empty string), and not add it
        /// to <paramref name="finalArguments"/>. It will also fold consecutive constant strings or chars into a single string constant, to avoid unnecessary concatenation. It may also do other optimizations,
        /// such as deconstructing nested string.Concat calls.
        /// </remarks>
        private void VisitAndAddConcatArgumentInReverseOrder(BoundExpression argument, bool argumentAlreadyVisited, ArrayBuilder<BoundExpression> finalArguments, ref WellKnownConcatRelatedMethods wellKnownConcatOptimizationMethods)
        {
            Debug.Assert(argument is not BoundBinaryOperator { InterpolatedStringHandlerData: null } op || !IsBinaryStringConcatenation(op));
            if (!argumentAlreadyVisited)
            {
                argument = VisitExpression(argument);
            }

            var followingArgument = finalArguments.Count > 0 ? finalArguments[^1] : null;
            var (singleConcatArgument, nestedConcatArguments) = SimplifyConcatArgument(argument, ref followingArgument, ref wellKnownConcatOptimizationMethods);

            // We should only get one result from simplification; either a single argument to add, or multiple nested arguments to add, or the current argument was optimized away
            // This last option can either mean that the argument was truly empty and was dropped entirely, or that it was merged into the following argument, in which case we need to update the builder
            if (singleConcatArgument is null && nestedConcatArguments.IsDefault)
            {
                if (followingArgument is not null)
                {
                    // We may have merged the current argument into the following argument, so we need to update that in the final arguments list
                    // If we didn't do any merging, then this is a no-op
                    finalArguments[^1] = followingArgument;
                }

                return;
            }

            Debug.Assert((finalArguments.Count == 0 && followingArgument is null) || (finalArguments.Count != 0 && ReferenceEquals(followingArgument, finalArguments[^1])));
            Debug.Assert(singleConcatArgument is null ^ nestedConcatArguments.IsDefault);

            if (singleConcatArgument is not null)
            {
                finalArguments.Add(singleConcatArgument);
            }
            else
            {
                for (int i = nestedConcatArguments.Length - 1; i >= 0; i--)
                {
                    VisitAndAddConcatArgumentInReverseOrder(nestedConcatArguments[i], argumentAlreadyVisited: true, finalArguments, ref wellKnownConcatOptimizationMethods);
                }
            }