From 478a18ec47fdb7e0e580a2635648456e9db9ad4f Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 26 Jan 2009 06:19:46 +0000 Subject: [PATCH] add parsing and constraint enforcement for GNU line marker directives. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@63003 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticKinds.def | 6 + include/clang/Lex/Preprocessor.h | 11 +- lib/Lex/PPDirectives.cpp | 183 +++++++++++++++++++----- test/Preprocessor/line-directive.c | 13 ++ 4 files changed, 173 insertions(+), 40 deletions(-) diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 09b24ad818..5ff6728283 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -204,6 +204,12 @@ DIAG(err_pp_line_requires_integer, ERROR, "#line directive requires a positive integer argument") DIAG(err_pp_line_invalid_filename, ERROR, "invalid filename for #line directive") +DIAG(err_pp_linemarker_requires_integer, ERROR, + "line marker directive requires a positive integer argument") +DIAG(err_pp_linemarker_invalid_filename, ERROR, + "invalid filename for line marker directive") +DIAG(err_pp_linemarker_invalid_flag, ERROR, + "invalid flag line marker directive") DIAG(ext_pp_line_too_big, EXTENSION, "C requires #line number to be less than %0, allowed as extension") diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 8081759e96..bf47bab295 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -532,6 +532,11 @@ public: /// CheckEndOfDirective - Ensure that the next token is a tok::eom token. If /// not, emit a diagnostic and consume up until the eom. void CheckEndOfDirective(const char *Directive); + + /// DiscardUntilEndOfDirective - Read and discard all tokens remaining on the + /// current line until the tok::eom token is found. + void DiscardUntilEndOfDirective(); + private: void PushIncludeMacroStack() { @@ -566,10 +571,6 @@ private: /// #include. bool isInPrimaryFile() const; - /// DiscardUntilEndOfDirective - Read and discard all tokens remaining on the - /// current line until the tok::eom token is found. - void DiscardUntilEndOfDirective(); - /// ReadMacroName - Lex and validate a macro name, which occurs after a /// #define or #undef. This emits a diagnostic, sets the token kind to eom, /// and discards the rest of the macro line if the macro name is invalid. @@ -690,8 +691,8 @@ private: /// Handle*Directive - implement the various preprocessor directives. These /// should side-effect the current preprocessor object so that the next call /// to Lex() will return the appropriate token next. - void HandleLineDirective(Token &Tok); + void HandleDigitDirective(Token &Tok); void HandleUserDiagnosticDirective(Token &Tok, bool isWarning); void HandleIdentSCCSDirective(Token &Tok); diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index 1c8f7f1f3a..5b45ddd35e 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -478,9 +478,8 @@ TryAgain: LexUnexpandedToken(Result); goto TryAgain; - case tok::numeric_constant: - // FIXME: implement # 7 line numbers! - return DiscardUntilEndOfDirective(); + case tok::numeric_constant: // # 7 GNU line marker directive. + return HandleDigitDirective(Result); default: IdentifierInfo *II = Result.getIdentifierInfo(); if (II == 0) break; // Not an identifier. @@ -556,56 +555,68 @@ TryAgain: // Okay, we're done parsing the directive. } -/// HandleLineDirective - Handle #line directive: C99 6.10.4. The two -/// acceptable forms are: -/// # line digit-sequence -/// # line digit-sequence "s-char-sequence" -void Preprocessor::HandleLineDirective(Token &Tok) { - // Read the line # and string argument. Per C99 6.10.4p5, these tokens are - // expanded. - Token DigitTok; - Lex(DigitTok); - - // Verify that we get a number. +/// GetLineValue - Convert a numeric token into an unsigned value, emitting +/// Diagnostic DiagID if it is invalid, and returning the value in Val. +static bool GetLineValue(Token &DigitTok, unsigned &Val, + unsigned DiagID, Preprocessor &PP) { if (DigitTok.isNot(tok::numeric_constant)) { - Diag(DigitTok, diag::err_pp_line_requires_integer); + PP.Diag(DigitTok, DiagID); + if (DigitTok.isNot(tok::eom)) - DiscardUntilEndOfDirective(); - return; + PP.DiscardUntilEndOfDirective(); + return true; } - // Validate the number and convert it to an unsigned. llvm::SmallString<64> IntegerBuffer; IntegerBuffer.resize(DigitTok.getLength()); const char *DigitTokBegin = &IntegerBuffer[0]; - unsigned ActualLength = getSpelling(DigitTok, DigitTokBegin); + unsigned ActualLength = PP.getSpelling(DigitTok, DigitTokBegin); NumericLiteralParser Literal(DigitTokBegin, DigitTokBegin+ActualLength, - DigitTok.getLocation(), *this); + DigitTok.getLocation(), PP); if (Literal.hadError) - return DiscardUntilEndOfDirective(); // a diagnostic was already reported. + return true; // Error already emitted. if (Literal.isFloatingLiteral() || Literal.isImaginary) { - Diag(DigitTok, diag::err_pp_line_requires_integer); - return; + PP.Diag(DigitTok, DiagID); + return true; } // Parse the integer literal into Result. - llvm::APInt Val(32, 0); - if (Literal.GetIntegerValue(Val)) { + llvm::APInt APVal(32, 0); + if (Literal.GetIntegerValue(APVal)) { // Overflow parsing integer literal. - Diag(DigitTok, diag::err_pp_line_requires_integer); - return DiscardUntilEndOfDirective(); + PP.Diag(DigitTok, DiagID); + return true; } - - // Enforce C99 6.10.4p3: The digit sequence shall not specify zero, nor a - // number greater than 2147483647. - unsigned LineNo = Val.getZExtValue(); - if (LineNo == 0) { - Diag(DigitTok, diag::err_pp_line_requires_integer); - return DiscardUntilEndOfDirective(); + Val = APVal.getZExtValue(); + + // Reject 0, this is needed both by #line numbers and flags. + if (Val == 0) { + PP.Diag(DigitTok, DiagID); + PP.DiscardUntilEndOfDirective(); + return true; } - // C90 requires that the line # be less than 32767, and C99 ups the limit. + return false; +} + +/// HandleLineDirective - Handle #line directive: C99 6.10.4. The two +/// acceptable forms are: +/// # line digit-sequence +/// # line digit-sequence "s-char-sequence" +void Preprocessor::HandleLineDirective(Token &Tok) { + // Read the line # and string argument. Per C99 6.10.4p5, these tokens are + // expanded. + Token DigitTok; + Lex(DigitTok); + + // Validate the number and convert it to an unsigned. + unsigned LineNo; + if (GetLineValue(DigitTok, LineNo, diag::err_pp_line_requires_integer, *this)) + return; + + // Enforce C99 6.10.4p3: "The digit sequence shall not specify ... a + // number greater than 2147483647". C90 requires that the line # be <= 32767. unsigned LineLimit = Features.C99 ? 2147483648U : 32768U; if (LineNo >= LineLimit) Diag(DigitTok, diag::ext_pp_line_too_big) << LineLimit; @@ -629,6 +640,108 @@ void Preprocessor::HandleLineDirective(Token &Tok) { // FIXME: do something with the #line info. } +/// ReadLineMarkerFlags - Parse and validate any flags at the end of a GNU line +/// marker directive. +static bool ReadLineMarkerFlags(bool &IsFileEntry, bool &IsFileExit, + bool &IsSystemHeader, bool &IsExternCHeader, + Preprocessor &PP) { + unsigned FlagVal; + Token FlagTok; + PP.Lex(FlagTok); + if (FlagTok.is(tok::eom)) return false; + if (GetLineValue(FlagTok, FlagVal, diag::err_pp_linemarker_invalid_flag, PP)) + return true; + + if (FlagVal == 1) { + IsFileEntry = true; + + PP.Lex(FlagTok); + if (FlagTok.is(tok::eom)) return false; + if (GetLineValue(FlagTok, FlagVal, diag::err_pp_linemarker_invalid_flag,PP)) + return true; + } else if (FlagVal == 2) { + IsFileExit = true; + + PP.Lex(FlagTok); + if (FlagTok.is(tok::eom)) return false; + if (GetLineValue(FlagTok, FlagVal, diag::err_pp_linemarker_invalid_flag,PP)) + return true; + } + + // We must have 3 if there are still flags. + if (FlagVal != 3) { + PP.Diag(FlagTok, diag::err_pp_linemarker_invalid_flag); + return true; + } + + IsSystemHeader = true; + + PP.Lex(FlagTok); + if (FlagTok.is(tok::eom)) return false; + if (GetLineValue(FlagTok, FlagVal, diag::err_pp_linemarker_invalid_flag,PP)) + return true; + + // We must have 4 if there is yet another flag. + if (FlagVal != 4) { + PP.Diag(FlagTok, diag::err_pp_linemarker_invalid_flag); + return true; + } + + IsExternCHeader = true; + + PP.Lex(FlagTok); + if (FlagTok.is(tok::eom)) return false; + + // There are no more valid flags here. + PP.Diag(FlagTok, diag::err_pp_linemarker_invalid_flag); + return true; +} + +/// HandleDigitDirective - Handle a GNU line marker directive, whose syntax is +/// one of the following forms: +/// +/// # 42 +/// # 42 "file" ('1' | '2')? +/// # 42 "file" ('1' | '2')? '3' '4'? +/// +void Preprocessor::HandleDigitDirective(Token &DigitTok) { + // Validate the number and convert it to an unsigned. GNU does not have a + // line # limit other than it fit in 32-bits. + unsigned LineNo; + if (GetLineValue(DigitTok, LineNo, diag::err_pp_linemarker_requires_integer, + *this)) + return; + + Token StrTok; + Lex(StrTok); + + bool IsFileEntry = false, IsFileExit = false; + bool IsSystemHeader = false, IsExternCHeader = false; + + // If the StrTok is "eom", then it wasn't present. Otherwise, it must be a + // string followed by eom. + if (StrTok.is(tok::eom)) + ; // ok + else if (StrTok.isNot(tok::string_literal)) { + Diag(StrTok, diag::err_pp_linemarker_invalid_filename); + DiscardUntilEndOfDirective(); + return; + } else { + // If a filename was present, read any flags that are present. + if (ReadLineMarkerFlags(IsFileEntry, IsFileExit, + IsSystemHeader, IsExternCHeader, *this)) { + DiscardUntilEndOfDirective(); + return; + } + } + + // FIXME: do something with the #line info. + + + +} + + /// HandleUserDiagnosticDirective - Handle a #warning or #error directive. /// void Preprocessor::HandleUserDiagnosticDirective(Token &Tok, diff --git a/test/Preprocessor/line-directive.c b/test/Preprocessor/line-directive.c index e1adc6116d..3faac15c27 100644 --- a/test/Preprocessor/line-directive.c +++ b/test/Preprocessor/line-directive.c @@ -12,3 +12,16 @@ #define A 42 "foo" #line A +# 42 +# 42 "foo" +# 42 "foo" 1 3 +# 42 "foo" 2 3 +# 42 "foo" 2 3 4 +# 42 "foo" 3 4 + +# 'a' // expected-error {{invalid preprocessing directive}} +# 42 'f' // expected-error {{invalid filename for line marker directive}} +# 42 1 3 // expected-error {{invalid filename for line marker directive}} +# 42 "foo" 3 1 // expected-error {{invalid flag line marker directive}} +# 42 "foo" 42 // expected-error {{invalid flag line marker directive}} +# 42 "foo" 1 2 // expected-error {{invalid flag line marker directive}} -- 2.40.0