]> granicus.if.org Git - clang/commitdiff
add parsing and constraint enforcement for GNU line marker directives.
authorChris Lattner <sabre@nondot.org>
Mon, 26 Jan 2009 06:19:46 +0000 (06:19 +0000)
committerChris Lattner <sabre@nondot.org>
Mon, 26 Jan 2009 06:19:46 +0000 (06:19 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@63003 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticKinds.def
include/clang/Lex/Preprocessor.h
lib/Lex/PPDirectives.cpp
test/Preprocessor/line-directive.c

index 09b24ad818419fc6baaadefb456664226691f5e5..5ff6728283c0c071a16026ce9ee4b6deb10f5f0b 100644 (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")
      
index 8081759e9655733d3a6e02bf619867d58ce068db..bf47bab29529fecb865713bb845a0be58c505722 100644 (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);
   
index 1c8f7f1f3a333e435091460954173180279a0323..5b45ddd35e9afd2a2d79920534d248d49e936830 100644 (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,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, 
index e1adc6116df3a8e15f6085b9a10f5032ac963254..3faac15c2720a80594b254d59dbd7c8bdcb35b5a 100644 (file)
 #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}}