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); } }
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; }
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); } }
private DirectiveTriviaSyntax ParseRegionDirective(SyntaxToken hash, SyntaxToken keyword, bool isActive) { return SyntaxFactory.RegionDirectiveTrivia(hash, keyword, this.ParseEndOfDirectiveWithOptionalPreprocessingMessage(), isActive); }