Exemplo n.º 1
0
            /// <summary>
            /// Scan past the hole inside an interpolated string literal, leaving the current character on the '}' (if any)
            /// </summary>
            private void ScanInterpolatedStringLiteralHoleBalancedText(char endingChar, bool isHole, ref int colonPosition)
            {
                while (true)
                {
                    char ch = _lexer.TextWindow.PeekChar();

                    // Note: within a hole newlines are always allowed.  The restriction on if newlines are allowed or not
                    // is only within a text-portion of the interpolated string.
                    if (IsAtEnd(allowNewline: true))
                    {
                        // the caller will complain
                        return;
                    }

                    switch (ch)
                    {
                    case '#':
                        // preprocessor directives not allowed.
                        TrySetUnrecoverableError(_lexer.MakeError(_lexer.TextWindow.Position, 1, ErrorCode.ERR_SyntaxError, endingChar.ToString()));
                        _lexer.TextWindow.AdvanceChar();
                        continue;

                    case '$':
                        if (_lexer.TextWindow.PeekChar(1) == '"' || _lexer.TextWindow.PeekChar(1) == '@' && _lexer.TextWindow.PeekChar(2) == '"')
                        {
                            var discarded = default(TokenInfo);
                            _lexer.ScanInterpolatedStringLiteral(
                                isVerbatim: _lexer.TextWindow.PeekChar(1) == '@',
                                ref discarded);
                            continue;
                        }

                        goto default;

                    case ':':
                        // the first colon not nested within matching delimiters is the start of the format string
                        if (isHole)
                        {
                            Debug.Assert(colonPosition == 0);
                            colonPosition = _lexer.TextWindow.Position;
                            ScanFormatSpecifier();
                            return;
                        }

                        goto default;

                    case '}':
                    case ')':
                    case ']':
                        if (ch == endingChar)
                        {
                            return;
                        }

                        TrySetUnrecoverableError(_lexer.MakeError(_lexer.TextWindow.Position, 1, ErrorCode.ERR_SyntaxError, endingChar.ToString()));
                        goto default;

                    case '"' when RecoveringFromRunawayLexing():
                        // When recovering from mismatched delimiters, we consume the next
                        // quote character as the close quote for the interpolated string. In
                        // practice this gets us out of trouble in scenarios we've encountered.
                        // See, for example, https://github.com/dotnet/roslyn/issues/44789
                        return;

                    case '"':
                    case '\'':
                        // handle string or character literal inside an expression hole.
                        ScanInterpolatedStringLiteralNestedString();
                        continue;

                    case '@':
                        if (_lexer.TextWindow.PeekChar(1) == '"' && !RecoveringFromRunawayLexing())
                        {
                            // check for verbatim string inside an expression hole.
                            var nestedStringPosition = _lexer.TextWindow.Position;

                            // Note that this verbatim string may encounter an error (for example if it contains a
                            // new line and we don't allow that).  This should be reported to the user, but should
                            // not put us into an unrecoverable position.  We can clearly see that this was intended
                            // to be a normal verbatim string, so we can continue on an attempt to understand the
                            // outer interpolated string properly.
                            var discarded = default(TokenInfo);
                            var errorCode = _lexer.ScanVerbatimStringLiteral(ref discarded);
                            if (errorCode is ErrorCode code)
                            {
                                TrySetRecoverableError(_lexer.MakeError(nestedStringPosition, width: 2, code));
                            }

                            continue;
                        }
                        else if (_lexer.TextWindow.PeekChar(1) == '$' && _lexer.TextWindow.PeekChar(2) == '"')
                        {
                            var discarded = default(TokenInfo);
                            _lexer.ScanInterpolatedStringLiteral(isVerbatim: true, ref discarded);
                            continue;
                        }

                        goto default;

                    case '/':
                        switch (_lexer.TextWindow.PeekChar(1))
                        {
                        case '/':
                            _lexer.ScanToEndOfLine();
                            continue;

                        case '*':
                            _lexer.ScanMultiLineComment(out _);
                            continue;

                        default:
                            _lexer.TextWindow.AdvanceChar();
                            continue;
                        }

                    case '{':
                        // TODO: after the colon this has no special meaning.
                        ScanInterpolatedStringLiteralHoleBracketed('{', '}');
                        continue;

                    case '(':
                        // TODO: after the colon this has no special meaning.
                        ScanInterpolatedStringLiteralHoleBracketed('(', ')');
                        continue;

                    case '[':
                        // TODO: after the colon this has no special meaning.
                        ScanInterpolatedStringLiteralHoleBracketed('[', ']');
                        continue;

                    default:
                        // part of code in the expression hole
                        _lexer.TextWindow.AdvanceChar();
                        continue;
                    }
                }
            }
Exemplo n.º 2
0
            /// <summary>
            /// Scan past the hole inside an interpolated string literal, leaving the current character on the '}' (if any)
            /// </summary>
            private void ScanInterpolatedStringLiteralHoleBalancedText(char endingChar, bool isHole, out Range colonRange)
            {
                colonRange = default;
                while (true)
                {
                    char ch = _lexer.TextWindow.PeekChar();

                    // Note: within a hole newlines are always allowed.  The restriction on if newlines are allowed or not
                    // is only within a text-portion of the interpolated string.
                    if (IsAtEnd(allowNewline: true))
                    {
                        // the caller will complain
                        return;
                    }

                    switch (ch)
                    {
                    case '#':
                        // preprocessor directives not allowed.
                        TrySetUnrecoverableError(_lexer.MakeError(_lexer.TextWindow.Position, 1, ErrorCode.ERR_SyntaxError, endingChar.ToString()));
                        _lexer.TextWindow.AdvanceChar();
                        continue;

                    case '$':
                    {
                        var discarded = default(TokenInfo);
                        if (_lexer.TryScanInterpolatedString(ref discarded))
                        {
                            continue;
                        }

                        goto default;
                    }

                    case ':':
                        // the first colon not nested within matching delimiters is the start of the format string
                        if (isHole)
                        {
                            Debug.Assert(colonRange.Equals(default(Range)));
                            colonRange = new Range(_lexer.TextWindow.Position, _lexer.TextWindow.Position + 1);
                            ScanFormatSpecifier();
                            return;
                        }

                        goto default;

                    case '}':
                    case ')':
                    case ']':
                        if (ch == endingChar)
                        {
                            return;
                        }

                        TrySetUnrecoverableError(_lexer.MakeError(_lexer.TextWindow.Position, 1, ErrorCode.ERR_SyntaxError, endingChar.ToString()));
                        goto default;

                    case '"':
                        if (RecoveringFromRunawayLexing())
                        {
                            // When recovering from mismatched delimiters, we consume the next
                            // quote character as the close quote for the interpolated string. In
                            // practice this gets us out of trouble in scenarios we've encountered.
                            // See, for example, https://github.com/dotnet/roslyn/issues/44789
                            return;
                        }

                        // handle string literal inside an expression hole.
                        ScanInterpolatedStringLiteralNestedString();
                        continue;

                    case '\'':
                        // handle character literal inside an expression hole.
                        ScanInterpolatedStringLiteralNestedString();
                        continue;

                    case '@':
                    {
                        var discarded = default(TokenInfo);
                        if (_lexer.TryScanAtStringToken(ref discarded))
                        {
                            continue;
                        }

                        // Wasn't an @"" or @$"" string.  Just consume this as normal code.
                        goto default;
                    }

                    case '/':
                        switch (_lexer.TextWindow.PeekChar(1))
                        {
                        case '/':
                            _lexer.ScanToEndOfLine();
                            continue;

                        case '*':
                            _lexer.ScanMultiLineComment(out _);
                            continue;

                        default:
                            _lexer.TextWindow.AdvanceChar();
                            continue;
                        }

                    case '{':
                        // TODO: after the colon this has no special meaning.
                        ScanInterpolatedStringLiteralHoleBracketed('{', '}');
                        continue;

                    case '(':
                        // TODO: after the colon this has no special meaning.
                        ScanInterpolatedStringLiteralHoleBracketed('(', ')');
                        continue;

                    case '[':
                        // TODO: after the colon this has no special meaning.
                        ScanInterpolatedStringLiteralHoleBracketed('[', ']');
                        continue;

                    default:
                        // part of code in the expression hole
                        _lexer.TextWindow.AdvanceChar();
                        continue;
                    }
                }
            }