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

Code before:
        {
            Debug.Assert(!node.IsMissing);
            return AddError(node, ErrorCode.WRN_ErrorOverride, MakeError(node, code, args), (int)code);
        }

        protected TNode AddError<TNode>(TNode nodeOrToken, ErrorCode code, params object[] args) where TNode : GreenNode
        {
            if (!nodeOrToken.IsMissing)
            {
                // We have a normal node or token that has actual SyntaxToken.Text within it (or the EOF token). Place
                // the diagnostic at the start (not full start) of that real node/token, with a width that encompasses
                // the entire normal width of the node or token.
                Debug.Assert(nodeOrToken.Width > 0 || nodeOrToken.RawKind is (int)SyntaxKind.EndOfFileToken);
                return WithAdditionalDiagnostics(nodeOrToken, MakeError(nodeOrToken, code, args));
            }

            // Note: nodeOrToken.IsMissing means this is either a MissingToken itself, or a node comprised
            // (transitively) only from MissingTokens.  Missing tokens are guaranteed to have no text.  But they are
            // allowed to have trivia.  This is a common pattern the parser will follow when it encounters unexpected
            // tokens.  It will make a missing token of the expected kind for the current location, then attach the 
            // unexpected tokens as missed tokens to it.

            // At this point, we have a node or token without real text in it.  The intuition we have here is that we
            // want to place the diagnostic on the token that "follows" this 'missing' entity.  There is a subtlety
            // here.  If the node or token contains skipped tokens, then we consider that skipped token the "following"
            // token, and we will want to placce the diagnostic on it.  Otherwise, we want to place it on the true 'next
            // token' the parser is currently pointing at.

            if (!nodeOrToken.ContainsSkippedText)
            {
                // Simple case this node/token does not contain any skipped text.  Place the diagnostic at the start of
                // the token that follows.
                var (offset, width) = this.GetDiagnosticSpanForMissingToken();
                return WithAdditionalDiagnostics(nodeOrToken, MakeError(nodeOrToken.FullWidth + offset, width, code, args));
            }
            else
            {
                // Complex case.  This node or token contains skipped text.  Place the diagnostic on the skipped text.
                var (offset, width) = getOffsetAndWidthOfSkippedToken();
                return WithAdditionalDiagnostics(nodeOrToken, MakeError(offset, width, code, args));
            }

            (int offset, int width) getOffsetAndWidthOfSkippedToken()
            {
                var offset = 0;

                // Walk all the children of this nodeOrToken (including itself).  Note: this does not walk into trivia.
                // We are looking for the first token that has skipped text.  When we find that token (which must exist,
                // based on the check above), we will place the diagnostic on the skipped token within that token.
                foreach (var child in nodeOrToken.EnumerateNodes())
                {

Review comment (suggestion):

```suggestion
            // token, and we will want to place the diagnostic on it.  Otherwise, we want to place it on the true 'next
```

Code after:
        {
            Debug.Assert(!node.IsMissing);
            return AddError(node, ErrorCode.WRN_ErrorOverride, MakeError(node, code, args), (int)code);
        }

        protected TNode AddError<TNode>(TNode nodeOrToken, ErrorCode code, params object[] args) where TNode : GreenNode
        {
            if (!nodeOrToken.IsMissing)
            {
                // We have a normal node or token that has actual SyntaxToken.Text within it (or the EOF token). Place
                // the diagnostic at the start (not full start) of that real node/token, with a width that encompasses
                // the entire normal width of the node or token.
                Debug.Assert(nodeOrToken.Width > 0 || nodeOrToken.RawKind is (int)SyntaxKind.EndOfFileToken);
                return WithAdditionalDiagnostics(nodeOrToken, MakeError(nodeOrToken, code, args));
            }

            // Note: nodeOrToken.IsMissing means this is either a MissingToken itself, or a node comprised
            // (transitively) only from MissingTokens.  Missing tokens are guaranteed to have no text.  But they are
            // allowed to have trivia.  This is a common pattern the parser will follow when it encounters unexpected
            // tokens.  It will make a missing token of the expected kind for the current location, then attach the 
            // unexpected tokens as missed tokens to it.

            // At this point, we have a node or token without real text in it.  The intuition we have here is that we
            // want to place the diagnostic on the token that "follows" this 'missing' entity.  There is a subtlety
            // here.  If the node or token contains skipped tokens, then we consider that skipped token the "following"
            // token, and we will want to place the diagnostic on it.  Otherwise, we want to place it on the true 'next
            // token' the parser is currently pointing at.

            if (!nodeOrToken.ContainsSkippedText)
            {
                // Simple case this node/token does not contain any skipped text.  Place the diagnostic at the start of
                // the token that follows.
                var (offset, width) = this.GetDiagnosticSpanForMissingToken();
                return WithAdditionalDiagnostics(nodeOrToken, MakeError(nodeOrToken.FullWidth + offset, width, code, args));
            }
            else
            {
                // Complex case.  This node or token contains skipped text.  Place the diagnostic on the skipped text.
                var (offset, width) = getOffsetAndWidthOfSkippedToken();
                return WithAdditionalDiagnostics(nodeOrToken, MakeError(offset, width, code, args));
            }

            (int offset, int width) getOffsetAndWidthOfSkippedToken()
            {
                var offset = 0;

                // Walk all the children of this nodeOrToken (including itself).  Note: this does not walk into trivia.
                // We are looking for the first token that has skipped text.  When we find that token (which must exist,
                // based on the check above), we will place the diagnostic on the skipped token within that token.
                foreach (var child in nodeOrToken.EnumerateNodes())
                {