add parsing and constraint enforcement for GNU line marker directives.

llvm-svn: 63003
This commit is contained in:
Chris Lattner 2009-01-26 06:19:46 +00:00
parent e9193283c6
commit 76e689636b
4 changed files with 178 additions and 45 deletions

View File

@ -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")

View File

@ -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);

View File

@ -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,6 +555,51 @@ TryAgain:
// Okay, we're done parsing the directive.
}
/// 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)) {
PP.Diag(DigitTok, DiagID);
if (DigitTok.isNot(tok::eom))
PP.DiscardUntilEndOfDirective();
return true;
}
llvm::SmallString<64> IntegerBuffer;
IntegerBuffer.resize(DigitTok.getLength());
const char *DigitTokBegin = &IntegerBuffer[0];
unsigned ActualLength = PP.getSpelling(DigitTok, DigitTokBegin);
NumericLiteralParser Literal(DigitTokBegin, DigitTokBegin+ActualLength,
DigitTok.getLocation(), PP);
if (Literal.hadError)
return true; // Error already emitted.
if (Literal.isFloatingLiteral() || Literal.isImaginary) {
PP.Diag(DigitTok, DiagID);
return true;
}
// Parse the integer literal into Result.
llvm::APInt APVal(32, 0);
if (Literal.GetIntegerValue(APVal)) {
// Overflow parsing integer literal.
PP.Diag(DigitTok, DiagID);
return true;
}
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;
}
return false;
}
/// HandleLineDirective - Handle #line directive: C99 6.10.4. The two
/// acceptable forms are:
/// # line digit-sequence
@ -566,46 +610,13 @@ void Preprocessor::HandleLineDirective(Token &Tok) {
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);
unsigned LineNo;
if (GetLineValue(DigitTok, LineNo, diag::err_pp_line_requires_integer, *this))
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.
// 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,

View File

@ -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}}