예제 #1
0
        private DirectiveTriviaSyntax ParsePragmaDirective(SyntaxToken hash, SyntaxToken pragma, bool isActive)
        {
            if (isActive)
            {
                pragma = CheckFeatureAvailability(pragma, MessageID.IDS_FeaturePragma);
            }

            bool hasError = false;
            if (this.CurrentToken.ContextualKind == SyntaxKind.WarningKeyword)
            {
                var warning = this.EatContextualToken(SyntaxKind.WarningKeyword);
                SyntaxToken style;
                if (this.CurrentToken.Kind == SyntaxKind.DisableKeyword || this.CurrentToken.Kind == SyntaxKind.RestoreKeyword)
                {
                    style = this.EatToken();

                    var ids = new SeparatedSyntaxListBuilder<ExpressionSyntax>(10);
                    while (this.CurrentToken.Kind != SyntaxKind.EndOfDirectiveToken)
                    {
                        SyntaxToken id;
                        ExpressionSyntax idExpression;

                        if (this.CurrentToken.Kind == SyntaxKind.NumericLiteralToken)
                        {
                            // Previous versions of the compiler used to report a warning (CS1691)
                            // whenever an unrecognized warning code was supplied in a #pragma directive
                            // (or via /nowarn /warnaserror flags on the command line).
                            // Going forward, we won't generate any warning in such cases. This will make
                            // maintenance of backwards compatibility easier (we no longer need to worry
                            // about breaking existing projects / command lines if we deprecate / remove
                            // an old warning code).
                            id = this.EatToken();
                            idExpression = SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, id);
                        }
                        else if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken)
                        {
                            // Lexing / parsing of identifiers inside #pragma warning directives is identical
                            // to that inside #define directives except that very long identifiers inside #define
                            // are truncated to 128 characters to maintain backwards compatibility with previous
                            // versions of the compiler. (See TruncateIdentifier() below.)
                            // Since support for identifiers inside #pragma warning directives is new, 
                            // we don't have any backwards compatibility constraints. So we can preserve the
                            // identifier exactly as it appears in source.
                            id = this.EatToken();
                            idExpression = SyntaxFactory.IdentifierName(id);
                        }
                        else
                        {
                            id = this.EatToken(SyntaxKind.NumericLiteralToken, ErrorCode.WRN_IdentifierOrNumericLiteralExpected, reportError: isActive);
                            idExpression = SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, id);
                        }

                        hasError = hasError || id.ContainsDiagnostics;
                        ids.Add(idExpression);

                        if (this.CurrentToken.Kind != SyntaxKind.CommaToken)
                        {
                            break;
                        }

                        ids.AddSeparator(this.EatToken());
                    }

                    var end = this.ParseEndOfDirective(hasError || !isActive, afterPragma: true);
                    return SyntaxFactory.PragmaWarningDirectiveTrivia(hash, pragma, warning, style, ids.ToList(), end, isActive);
                }
                else
                {
                    style = this.EatToken(SyntaxKind.DisableKeyword, ErrorCode.WRN_IllegalPPWarning, reportError: isActive);
                    var end = this.ParseEndOfDirective(ignoreErrors: true, afterPragma: true);
                    return SyntaxFactory.PragmaWarningDirectiveTrivia(hash, pragma, warning, style, default(SeparatedSyntaxList<ExpressionSyntax>), end, isActive);
                }
            }
            else if (this.CurrentToken.Kind == SyntaxKind.ChecksumKeyword)
            {
                var checksum = this.EatToken();
                var file = this.EatToken(SyntaxKind.StringLiteralToken, ErrorCode.WRN_IllegalPPChecksum, reportError: isActive);
                var guid = this.EatToken(SyntaxKind.StringLiteralToken, ErrorCode.WRN_IllegalPPChecksum, reportError: isActive && !file.IsMissing);
                if (isActive && !guid.IsMissing)
                {
                    Guid tmp;
                    if (!Guid.TryParse(guid.ValueText, out tmp))
                    {
                        guid = this.AddError(guid, ErrorCode.WRN_IllegalPPChecksum);
                    }
                }

                var bytes = this.EatToken(SyntaxKind.StringLiteralToken, ErrorCode.WRN_IllegalPPChecksum, reportError: isActive && !guid.IsMissing);
                if (isActive && !bytes.IsMissing)
                {
                    if (bytes.ValueText.Length % 2 != 0)
                    {
                        bytes = this.AddError(bytes, ErrorCode.WRN_IllegalPPChecksum);
                    }
                    else
                    {
                        foreach (char c in bytes.ValueText)
                        {
                            if (!SyntaxFacts.IsHexDigit(c))
                            {
                                bytes = this.AddError(bytes, ErrorCode.WRN_IllegalPPChecksum);
                                break;
                            }
                        }
                    }
                }

                hasError = file.ContainsDiagnostics | guid.ContainsDiagnostics | bytes.ContainsDiagnostics;
                var eod = this.ParseEndOfDirective(ignoreErrors: hasError, afterPragma: true);
                return SyntaxFactory.PragmaChecksumDirectiveTrivia(hash, pragma, checksum, file, guid, bytes, eod, isActive);
            }
            else
            {
                var warning = this.EatToken(SyntaxKind.WarningKeyword, ErrorCode.WRN_IllegalPragma, reportError: isActive);
                var style = this.EatToken(SyntaxKind.DisableKeyword, reportError: false);
                var eod = this.ParseEndOfDirective(ignoreErrors: true, afterPragma: true);
                return SyntaxFactory.PragmaWarningDirectiveTrivia(hash, pragma, warning, style, default(SeparatedSyntaxList<ExpressionSyntax>), eod, isActive);
            }
        }
예제 #2
0
        public CSharpSyntaxNode ParseDirective(
            bool isActive,
            bool endIsActive,
            bool isAfterFirstTokenInFile,
            bool isAfterNonWhitespaceOnLine)
        {
            var hashPosition = lexer.TextWindow.Position;
            var hash = this.EatToken(SyntaxKind.HashToken, false);
            if (isAfterNonWhitespaceOnLine)
            {
                hash = this.AddError(hash, ErrorCode.ERR_BadDirectivePlacement);
            }

            // The behavior of these directives when isActive is false is somewhat complicated.
            // The key functions in the native compiler are ScanPreprocessorIfSection and
            // ScanAndIgnoreDirective in CSourceData::CPreprocessor.
            // Key points:
            //   1) #error, #warning, #line, and #pragma have no effect and produce no diagnostics.
            //   2) #if, #else, #elif, #endif, #region, and #endregion must still nest correctly.
            //   3) #define and #undef produce diagnostics but have no effect.
            // #reference, #load and #! are new, but they do not require nesting behavior, so we'll
            // ignore their diagnostics (as in (1) above).

            CSharpSyntaxNode result;
            SyntaxKind contextualKind = this.CurrentToken.ContextualKind;
            switch (contextualKind)
            {
                case SyntaxKind.IfKeyword:
                    result = this.ParseIfDirective(hash, this.EatContextualToken(contextualKind), isActive);
                    break;

                case SyntaxKind.ElifKeyword:
                    result = this.ParseElifDirective(hash, this.EatContextualToken(contextualKind), isActive, endIsActive);
                    break;

                case SyntaxKind.ElseKeyword:
                    result = this.ParseElseDirective(hash, this.EatContextualToken(contextualKind), isActive, endIsActive);
                    break;

                case SyntaxKind.EndIfKeyword:
                    result = this.ParseEndIfDirective(hash, this.EatContextualToken(contextualKind), isActive, endIsActive);
                    break;

                case SyntaxKind.RegionKeyword:
                    result = this.ParseRegionDirective(hash, this.EatContextualToken(contextualKind), isActive);
                    break;

                case SyntaxKind.EndRegionKeyword:
                    result = this.ParseEndRegionDirective(hash, this.EatContextualToken(contextualKind), isActive);
                    break;

                case SyntaxKind.DefineKeyword:
                case SyntaxKind.UndefKeyword:
                    result = this.ParseDefineOrUndefDirective(hash, this.EatContextualToken(contextualKind), isActive, isAfterFirstTokenInFile && !isAfterNonWhitespaceOnLine);
                    break;

                case SyntaxKind.ErrorKeyword:
                case SyntaxKind.WarningKeyword:
                    result = this.ParseErrorOrWarningDirective(hash, this.EatContextualToken(contextualKind), isActive);
                    break;

                case SyntaxKind.LineKeyword:
                    result = this.ParseLineDirective(hash, this.EatContextualToken(contextualKind), isActive);
                    break;

                case SyntaxKind.PragmaKeyword:
                    result = this.ParsePragmaDirective(hash, this.EatContextualToken(contextualKind), isActive);
                    break;

                case SyntaxKind.ReferenceKeyword:
                    result = this.ParseReferenceDirective(hash, this.EatContextualToken(contextualKind), isActive, isAfterFirstTokenInFile && !isAfterNonWhitespaceOnLine);
                    break;

                case SyntaxKind.LoadKeyword:
                    result = this.ParseLoadDirective(hash, this.EatContextualToken(contextualKind), isActive, isAfterFirstTokenInFile && !isAfterNonWhitespaceOnLine);
                    break;

                case SyntaxKind.NullableKeyword:
                    result = this.ParseNullableDirective(hash, this.EatContextualToken(contextualKind), isActive);
                    break;

                default:
                    if (lexer.Options.Kind == SourceCodeKind.Script && contextualKind == SyntaxKind.ExclamationToken && hashPosition == 0 && !hash.HasTrailingTrivia)
                    {
                        result = this.ParseShebangDirective(hash, this.EatToken(SyntaxKind.ExclamationToken), isActive);
                    }
                    else
                    {
                        var id = this.EatToken(SyntaxKind.IdentifierToken, false);
                        var end = this.ParseEndOfDirective(ignoreErrors: true);
                        if (!isAfterNonWhitespaceOnLine)
                        {
                            if (!id.IsMissing)
                            {
                                id = this.AddError(id, ErrorCode.ERR_PPDirectiveExpected);
                            }
                            else
                            {
                                hash = this.AddError(hash, ErrorCode.ERR_PPDirectiveExpected);
                            }
                        }

                        result = SyntaxFactory.BadDirectiveTrivia(hash, id, end, isActive);
                    }

                    break;
            }

            return result;
        }
예제 #3
0
        private DirectiveTriviaSyntax ParseErrorOrWarningDirective(SyntaxToken hash, SyntaxToken keyword, bool isActive)
        {
            var eod = this.ParseEndOfDirectiveWithOptionalPreprocessingMessage();
            bool isError = keyword.Kind == SyntaxKind.ErrorKeyword;
            if (isActive)
            {
                var triviaBuilder = new System.IO.StringWriter(System.Globalization.CultureInfo.InvariantCulture);
                int triviaWidth = 0;

                // whitespace and single line comments are trailing trivia on the keyword, the rest
                // of the error message is leading trivia on the eod.
                //
                bool skipping = true;
                foreach (var t in keyword.TrailingTrivia)
                {
                    if (skipping)
                    {
                        if (t.Kind == SyntaxKind.WhitespaceTrivia)
                        {
                            continue;
                        }

                        skipping = false;
                    }

                    t.WriteTo(triviaBuilder, leading: true, trailing: true);
                    triviaWidth += t.FullWidth;
                }

                foreach (var node in eod.LeadingTrivia)
                {
                    node.WriteTo(triviaBuilder, leading: true, trailing: true);
                    triviaWidth += node.FullWidth;
                }

                //relative to leading trivia of eod
                //could be negative if part of the error text comes from the trailing trivia of the keyword token
                int triviaOffset = eod.GetLeadingTriviaWidth() - triviaWidth;

                string errorText = triviaBuilder.ToString();
                eod = this.AddError(eod, triviaOffset, triviaWidth, isError ? ErrorCode.ERR_ErrorDirective : ErrorCode.WRN_WarningDirective, errorText);

                if (isError)
                {
                    if (errorText.Equals("version", StringComparison.Ordinal))
                    {
                        string version = CommonCompiler.GetProductVersion(typeof(CSharpCompiler));
                        var specified = this.Options.SpecifiedLanguageVersion;
                        var effective = specified.MapSpecifiedToEffectiveVersion();

                        var displayLanguageVersion = specified == effective ? specified.ToDisplayString() : $"{specified.ToDisplayString()} ({effective.ToDisplayString()})";

                        eod = this.AddError(eod, triviaOffset, triviaWidth, ErrorCode.ERR_CompilerAndLanguageVersion, version,
                            displayLanguageVersion);
                    }
                    else
                    {
                        const string versionMarker = "version:";
                        if (this.Options.LanguageVersion != LanguageVersion.Preview &&
                            errorText.StartsWith(versionMarker, StringComparison.Ordinal) &&
                            LanguageVersionFacts.TryParse(errorText.Substring(versionMarker.Length), out var languageVersion))
                        {
                            ErrorCode error = this.Options.LanguageVersion.GetErrorCode();
                            eod = this.AddError(eod, triviaOffset, triviaWidth, error, "version", new CSharpRequiredLanguageVersion(languageVersion));
                        }
                    }
                }
            }

            if (isError)
            {
                return SyntaxFactory.ErrorDirectiveTrivia(hash, keyword, eod, isActive);
            }
            else
            {
                return SyntaxFactory.WarningDirectiveTrivia(hash, keyword, eod, isActive);
            }
        }
예제 #4
0
 private DirectiveTriviaSyntax ParseRegionDirective(SyntaxToken hash, SyntaxToken keyword, bool isActive)
 {
     return SyntaxFactory.RegionDirectiveTrivia(hash, keyword, this.ParseEndOfDirectiveWithOptionalPreprocessingMessage(), isActive);
 }