From 359cc4475487ea0a660b3e8918334a2866e71c66 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 26 Jan 2009 05:29:08 +0000 Subject: [PATCH] parse and enforce required constraints on #line directives. Right now we just discard them. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@62999 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticKinds.def | 7 ++ include/clang/Lex/Preprocessor.h | 1 + lib/Lex/PPDirectives.cpp | 100 ++++++++++++++++++++---- test/Preprocessor/line-directive.c | 10 +++ 4 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 test/Preprocessor/line-directive.c diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index e9dbad94b6..09b24ad818 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -200,6 +200,13 @@ DIAG(err_pp_duplicate_name_in_arg_list, ERROR, "duplicate macro parameter name %0") DIAG(err_pp_stringize_not_parameter, ERROR, "'#' is not followed by a macro parameter") +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(ext_pp_line_too_big, EXTENSION, + "C requires #line number to be less than %0, allowed as extension") + DIAG(err_pp_malformed_ident, ERROR, "invalid #ident directive") DIAG(err_pp_unterminated_conditional, ERROR, diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 2954c676e8..8081759e96 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -691,6 +691,7 @@ private: /// 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 HandleUserDiagnosticDirective(Token &Tok, bool isWarning); void HandleIdentSCCSDirective(Token &Tok); diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index ec5b67fb78..4ab293b9b7 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -12,10 +12,12 @@ //===----------------------------------------------------------------------===// #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/LiteralSupport.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/MacroInfo.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceManager.h" +#include "llvm/ADT/APInt.h" using namespace clang; //===----------------------------------------------------------------------===// @@ -478,8 +480,7 @@ TryAgain: case tok::numeric_constant: // FIXME: implement # 7 line numbers! - DiscardUntilEndOfDirective(); - return; + return DiscardUntilEndOfDirective(); default: IdentifierInfo *II = Result.getIdentifierInfo(); if (II == 0) break; // Not an identifier. @@ -513,9 +514,7 @@ TryAgain: // C99 6.10.4 - Line Control. case tok::pp_line: - // FIXME: implement #line - DiscardUntilEndOfDirective(); - return; + return HandleLineDirective(Result); // C99 6.10.5 - Error Directive. case tok::pp_error: @@ -557,22 +556,95 @@ 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. + if (DigitTok.isNot(tok::numeric_constant)) { + Diag(DigitTok, diag::err_pp_line_requires_integer); + if (DigitTok.isNot(tok::eom)) + DiscardUntilEndOfDirective(); + return; + } + + // 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); + NumericLiteralParser Literal(DigitTokBegin, DigitTokBegin+ActualLength, + DigitTok.getLocation(), *this); + if (Literal.hadError) + return DiscardUntilEndOfDirective(); // a diagnostic was already reported. + + if (Literal.isFloatingLiteral() || Literal.isImaginary) { + Diag(DigitTok, diag::err_pp_line_requires_integer); + return; + } + + // Parse the integer literal into Result. + llvm::APInt Val(32, 0); + if (Literal.GetIntegerValue(Val)) { + // Overflow parsing integer literal. + Diag(DigitTok, diag::err_pp_line_requires_integer); + return DiscardUntilEndOfDirective(); + } + + // 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(); + } + + // C90 requires that the line # be less than 32767, and C99 ups the limit. + unsigned LineLimit = Features.C99 ? 2147483648U : 32768U; + if (LineNo >= LineLimit) + Diag(DigitTok, diag::ext_pp_line_too_big) << LineLimit; + + Token StrTok; + Lex(StrTok); + + // 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_line_invalid_filename); + DiscardUntilEndOfDirective(); + return; + } else { + // Verify that there is nothing after the string, other than EOM. + CheckEndOfDirective("#line"); + } + + // FIXME: do something with the #line info. +} + + void Preprocessor::HandleUserDiagnosticDirective(Token &Tok, bool isWarning) { + if (!CurLexer) + return CurPTHLexer->DiscardToEndOfLine(); + // Read the rest of the line raw. We do this because we don't want macros // to be expanded and we don't require that the tokens be valid preprocessing // tokens. For example, this is allowed: "#warning ` 'foo". GCC does // collapse multiple consequtive white space between tokens, but this isn't // specified by the standard. - - if (CurLexer) { - std::string Message = CurLexer->ReadToEndOfLine(); - unsigned DiagID = isWarning ? diag::pp_hash_warning : diag::err_pp_hash_error; - Diag(Tok, DiagID) << Message; - } - else { - CurPTHLexer->DiscardToEndOfLine(); - } + std::string Message = CurLexer->ReadToEndOfLine(); + if (isWarning) + Diag(Tok, diag::pp_hash_warning) << Message; + else + Diag(Tok, diag::err_pp_hash_error) << Message; } /// HandleIdentSCCSDirective - Handle a #ident/#sccs directive. diff --git a/test/Preprocessor/line-directive.c b/test/Preprocessor/line-directive.c new file mode 100644 index 0000000000..9b7950633b --- /dev/null +++ b/test/Preprocessor/line-directive.c @@ -0,0 +1,10 @@ +// RUN: clang -fsyntax-only -verify -pedantic %s + +#line 'a' // expected-error {{#line directive requires a positive integer argument}} +#line 0 // expected-error {{#line directive requires a positive integer argument}} +#line 2147483648 // expected-warning {{C requires #line number to be less than 2147483648, allowed as extension}} +#line 42 // ok +#line 42 'a' // expected-error {{nvalid filename for #line directive}} +#line 42 "foo/bar/baz.h" // ok + + -- 2.40.0