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
            private void ScanInterpolatedStringLiteralNestedVerbatimString()
            {
                var discarded = default(TokenInfo);

                lexer.ScanVerbatimStringLiteral(ref discarded, allowNewlines: allowNewlines);
            }
Exemplo n.º 3
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();

                    // See if we ran into a disallowed new line.  If so, this is recoverable, so just skip past it but
                    // give a good message about the issue.  This will prevent a lot of cascading issues with the
                    // remainder of the interpolated string that comes on the following lines.
                    var allowNewLines = _isVerbatim && _allowNewlines;
                    if (!allowNewLines && SyntaxFacts.IsNewLine(ch))
                    {
                        TrySetRecoverableError(_lexer.MakeError(_lexer.TextWindow.Position, width: 0, ErrorCode.ERR_NewlinesAreNotAllowedInsideANonVerbatimInterpolatedString));
                    }

                    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) == '"')
                        {
                            bool isVerbatimSubstring = _lexer.TextWindow.PeekChar(1) == '@';
                            var  interpolations      = (ArrayBuilder <Interpolation>?)null;
                            var  info             = default(TokenInfo);
                            bool wasVerbatim      = _isVerbatim;
                            bool wasAllowNewlines = _allowNewlines;
                            try
                            {
                                _isVerbatim     = isVerbatimSubstring;
                                _allowNewlines &= _isVerbatim;
                                ScanInterpolatedStringLiteralTop(interpolations, ref info, closeQuoteMissing: out _);
                            }
                            finally
                            {
                                _isVerbatim    = wasVerbatim;
                                _allowNewlines = wasAllowNewlines;
                            }
                            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, _allowNewlines);
                            if (errorCode is ErrorCode code)
                            {
                                TrySetRecoverableError(_lexer.MakeError(nestedStringPosition, width: 2, code));
                            }

                            continue;
                        }
                        else if (_lexer.TextWindow.PeekChar(1) == '$' && _lexer.TextWindow.PeekChar(2) == '"')
                        {
                            _lexer.CheckFeatureAvailability(MessageID.IDS_FeatureAltInterpolatedVerbatimStrings);
                            var  interpolations   = (ArrayBuilder <Interpolation>?)null;
                            var  info             = default(TokenInfo);
                            bool wasVerbatim      = _isVerbatim;
                            bool wasAllowNewlines = _allowNewlines;
                            try
                            {
                                _isVerbatim    = true;
                                _allowNewlines = true;
                                ScanInterpolatedStringLiteralTop(interpolations, ref info, closeQuoteMissing: out _);
                            }
                            finally
                            {
                                _isVerbatim    = wasVerbatim;
                                _allowNewlines = wasAllowNewlines;
                            }
                            continue;
                        }

                        goto default;

                    case '/':
                        switch (_lexer.TextWindow.PeekChar(1))
                        {
                        case '/':
                            if (!_isVerbatim || !_allowNewlines)
                            {
                                // error: single-line comment not allowed in an interpolated string.
                                // report the error but keep going for good error recovery.
                                TrySetRecoverableError(_lexer.MakeError(_lexer.TextWindow.Position, 2, ErrorCode.ERR_SingleLineCommentInExpressionHole));
                            }

                            _lexer.TextWindow.AdvanceChar();         // skip /
                            _lexer.TextWindow.AdvanceChar();         // skip /
                            while (!IsAtEnd(allowNewline: false))
                            {
                                _lexer.TextWindow.AdvanceChar();         // skip // comment character
                            }

                            continue;

                        case '*':
                            // check for and scan /* comment */
                            ScanInterpolatedStringLiteralNestedComment();
                            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;
                    }
                }
            }