Repository: dotnet/roslyn
File: src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs (C#)

Code before:
        }

        /// <summary>
        /// Converts skippedSyntax node into all its constituent tokens (and their constituent trivias) and adds these
        /// all as trivia on the target token.  For example, given <c>token1-token2</c>, then target will have
        /// <c>leading_trivia1-token1-trailing_trivia1-leading_trivia2-token2-trailing_trivia2-</c> added to it.
        /// <para/>
        /// 
        /// Also adds the first node-based error, or error on a missing-token, in depth-first preorder, found in the
        /// skipped syntax tree to the target token.  This ensures that we do not lose token/node errors found in
        /// skipped syntax.
        /// </summary>
        internal SyntaxToken AddSkippedSyntax(SyntaxToken target, GreenNode skippedSyntax, bool trailing)
        {
            var builder = new SyntaxListBuilder(4);

            int currentOffset;
            if (trailing)
            {
                // The normal offset for a node/token is its start (not full start).  So if we're placing the skipped
                // syntax at the end of the trivia, then the offset relative to the node/token start will be adjusted
                // forward by the width of the node/token plus the existing trailing trivia.
                currentOffset = target.Width + target.GetTrailingTriviaWidth();
                builder.Add(target.GetTrailingTrivia());
            }
            else
            {
                // The normal offset for a node/token is its start (not full start). So if we're placing the skipped
                // syntax at the start of the trivia, then the offset relative to the node/token start will be adjusted
                // backward by the width of the existing leading trivia plus the width of the skipped syntax we're
                // tacking on at the front.
                currentOffset = -target.GetLeadingTriviaWidth() - skippedSyntax.FullWidth;
            }

            // the error in we'll attach to the node
            SyntaxDiagnosticInfo diagnostic = null;
            int finalDiagnosticOffset = 0;

            foreach (var node in skippedSyntax.EnumerateNodes())
            {
                if (node is SyntaxToken token)
                {
                    // Strip the leading trivia of the token, and add it to the target's final trivia list.
                    builder.Add(token.GetLeadingTrivia());

                    if (token.Width > 0)
                    {
                        // Then add the token (stripped of its own trivia) to the target's final trivia list.

                        builder.Add(SyntaxFactory.SkippedTokensTrivia(
                            token.TokenWithLeadingTrivia(null).TokenWithTrailingTrivia(null)));

Review comment (bug):
Note: this "Also adds..." comment is now correct, but it indicates buggy behavior! Specifically, we might have a node/skipped token that has *warnings and errors* on it.  But we'd only copy the first over, which might just be warning.  So the error would be lost.

We should either:
1. ensure that we copy over an error if it exists, overwriting any warnings we found along the way.
2. just copy over *everything*.

'2' actually seems saner, as it means not losing anything.   But it might be the case that when we recover from a big error recovery scan, we might report a ton of errors.



Code after:
        }

        /// <summary>
        /// Converts skippedSyntax node into all its constituent tokens (and their constituent trivias) and adds these
        /// all as trivia on the target token.  For example, given <c>token1-token2</c>, then target will have
        /// <c>leading_trivia1-token1-trailing_trivia1-leading_trivia2-token2-trailing_trivia2-</c> added to it.
        /// <para/>
        /// 
        /// Also adds the first node-based error, or error on a missing-token, in depth-first preorder, found in the
        /// skipped syntax tree to the target token.  This ensures that we do not lose token/node errors found in
        /// skipped syntax.
        /// 
        /// Note: This behavior could technically lead to buggy behavior.  Specifically, because we only take the first
        /// diagnostic we find, we might miss a more relevant diagnostic later in the tree.  For example, we might
        /// preserve a 'warning' while missing an error.
        /// 
        /// We should either:
        /// 
        /// 1. ensure that we copy over an error if it exists, overwriting any warnings we found along the way.
        /// 
        /// 2. just copy over everything.  This seems saner, as it means not losing anything. But it might be the case
        /// that when we recover from a big error recovery scan, we might report a ton of errors.
        ///
        /// For now, we do neither, and just take the first error/warning we find.  This can/should be revisited later
        /// if we discover it means we're losing important diagnostics.
        /// </summary>
        internal SyntaxToken AddSkippedSyntax(SyntaxToken target, GreenNode skippedSyntax, bool trailing)
        {
            var builder = new SyntaxListBuilder(4);

            int currentOffset;
            if (trailing)
            {
                // The normal offset for a node/token is its start (not full start).  So if we're placing the skipped
                // syntax at the end of the trivia, then the offset relative to the node/token start will be adjusted
                // forward by the width of the node/token plus the existing trailing trivia.
                currentOffset = target.Width + target.GetTrailingTriviaWidth();
                builder.Add(target.GetTrailingTrivia());
            }
            else
            {
                // The normal offset for a node/token is its start (not full start). So if we're placing the skipped
                // syntax at the start of the trivia, then the offset relative to the node/token start will be adjusted
                // backward by the width of the existing leading trivia plus the width of the skipped syntax we're
                // tacking on at the front.
                currentOffset = -target.GetLeadingTriviaWidth() - skippedSyntax.FullWidth;
            }

            // the error in we'll attach to the node
            SyntaxDiagnosticInfo diagnostic = null;
            int finalDiagnosticOffset = 0;