[clang] bf6986d - [clang] GCC directive extension extension: Hash NNN lines

Nathan Sidwell via cfe-commits cfe-commits at lists.llvm.org
Tue Nov 9 07:31:33 PST 2021


Author: Nathan Sidwell
Date: 2021-11-09T07:31:03-08:00
New Revision: bf6986d99eaa68ad850eb335cb3f98fe6406ccd0

URL: https://github.com/llvm/llvm-project/commit/bf6986d99eaa68ad850eb335cb3f98fe6406ccd0
DIFF: https://github.com/llvm/llvm-project/commit/bf6986d99eaa68ad850eb335cb3f98fe6406ccd0.diff

LOG: [clang] GCC directive extension extension: Hash NNN lines

Some time back I extended GCC's '# NNN' line marker semantics.
Specifically popping to a blank filename will restore the filename to
that of the popped-to include.  Restore to line 5 of including file
(escaped BOL #'s to avoid git eliding them):

\# 5 "" 2

Added documentation for this line control extension.

This was useful in developing modules tests, but turned out to also be
useful with machine-generated source code.  Specifically, a generated
include file that itself includes fragments from elsewhere.  The
ability to pop to the generated include file -- with its full path
prefix -- is useful for diagnostic & debug purposes.  For instance
something like:

// Machine generated -- DO NOT EDIT
Type Var = {
\# 7 "encoded.dsl" 1 // push to snippet-container
{snippet, of, code}
\# 6 " 2 // Restore to machined-generated source
,
};

// user-code
...
\#include "dsl.h"
...

That pop to "" will restore the filename to '..includepath../dsl.h',
which is better than restoring to plain "dsl.h".

Differential Revision: https://reviews.llvm.org/D113425

Added: 
    

Modified: 
    clang/docs/LanguageExtensions.rst
    clang/lib/Basic/SourceManager.cpp
    clang/lib/Lex/PPDirectives.cpp
    clang/test/Preprocessor/line-directive.c

Removed: 
    


################################################################################
diff  --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index e3df9fd09960e..363cf7c4e7435 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -4038,6 +4038,52 @@ in user headers or code. This is controlled by ``-Wpedantic-macros``. Final
 macros will always warn on redefinition, including situations with identical
 bodies and in system headers.
 
+Line Control
+============
+
+Clang supports an extension for source line control, which takes the
+form of a preprocessor directive starting with an unsigned integral
+constant. In addition to the standard ``#line`` directive, this form
+allows control of an include stack and header file type, which is used
+in issuing diagnostics. These lines are emitted in preprocessed
+output.
+
+.. code-block:: c
+  # <line:number> <filename:string> <header-type:numbers>
+
+The filename is optional, and if unspecified indicates no change in
+source filename. The header-type is an optional, whitespace-delimited,
+sequence of magic numbers as follows.
+
+* ``1`:` Push the current source file name onto the include stack and
+  enter a new file.
+
+* ``2``: Pop the include stack and return to the specified file. If
+  the filename is ``""``, the name popped from the include stack is
+  used. Otherwise there is no requirement that the specified filename
+  matches the current source when originally pushed.
+
+* ``3``: Enter a system-header region. System headers often contain
+  implementation-specific source that would normally emit a diagnostic.
+
+* ``4``: Enter an implicit ``extern "C"`` region. This is not required on
+  modern systems where system headers are C++-aware.
+
+At most a single ``1`` or ``2`` can be present, and values must be in
+ascending order.
+
+Examples are:
+
+.. code-block:: c
+
+   # 57 // Advance (or return) to line 57 of the current source file		
+   # 57 "frob" // Set to line 57 of "frob"
+   # 1 "foo.h" 1 // Enter "foo.h" at line 1
+   # 59 "main.c" 2 // Leave current include and return to "main.c"
+   # 1 "/usr/include/stdio.h" 1 3 // Enter a system header
+   # 60 "" 2 // return to "main.c"
+   # 1 "/usr/ancient/header.h" 1 4 // Enter an implicit extern "C" header
+
 Extended Integer Types
 ======================
 

diff  --git a/clang/lib/Basic/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp
index 8cba379aa0f80..c2e7b684cfd8e 100644
--- a/clang/lib/Basic/SourceManager.cpp
+++ b/clang/lib/Basic/SourceManager.cpp
@@ -207,28 +207,30 @@ void LineTableInfo::AddLineNote(FileID FID, unsigned Offset, unsigned LineNo,
                                 SrcMgr::CharacteristicKind FileKind) {
   std::vector<LineEntry> &Entries = LineEntries[FID];
 
-  // An unspecified FilenameID means use the last filename if available, or the
-  // main source file otherwise.
-  if (FilenameID == -1 && !Entries.empty())
-    FilenameID = Entries.back().FilenameID;
-
   assert((Entries.empty() || Entries.back().FileOffset < Offset) &&
          "Adding line entries out of order!");
 
   unsigned IncludeOffset = 0;
-  if (EntryExit == 0) {  // No #include stack change.
-    IncludeOffset = Entries.empty() ? 0 : Entries.back().IncludeOffset;
-  } else if (EntryExit == 1) {
+  if (EntryExit == 1) {
+    // Push #include
     IncludeOffset = Offset-1;
-  } else if (EntryExit == 2) {
-    assert(!Entries.empty() && Entries.back().IncludeOffset &&
-       "PPDirectives should have caught case when popping empty include stack");
-
-    // Get the include loc of the last entries' include loc as our include loc.
-    IncludeOffset = 0;
-    if (const LineEntry *PrevEntry =
-          FindNearestLineEntry(FID, Entries.back().IncludeOffset))
+  } else {
+    const auto *PrevEntry = Entries.empty() ? nullptr : &Entries.back();
+    if (EntryExit == 2) {
+      // Pop #include
+      assert(PrevEntry && PrevEntry->IncludeOffset &&
+             "PPDirectives should have caught case when popping empty include "
+             "stack");
+      PrevEntry = FindNearestLineEntry(FID, PrevEntry->IncludeOffset);
+    }
+    if (PrevEntry) {
       IncludeOffset = PrevEntry->IncludeOffset;
+      if (FilenameID == -1) {
+        // An unspecified FilenameID means use the previous (or containing)
+        // filename if available, or the main source file otherwise.
+        FilenameID = PrevEntry->FilenameID;
+      }
+    }
   }
 
   Entries.push_back(LineEntry::get(Offset, LineNo, FilenameID, FileKind,

diff  --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 1c2439ac91021..79cfc7fcd5fd6 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -1451,11 +1451,15 @@ void Preprocessor::HandleDigitDirective(Token &DigitTok) {
       DiscardUntilEndOfDirective();
       return;
     }
-    FilenameID = SourceMgr.getLineTableFilenameID(Literal.GetString());
 
     // If a filename was present, read any flags that are present.
     if (ReadLineMarkerFlags(IsFileEntry, IsFileExit, FileKind, *this))
       return;
+
+    // Exiting to an empty string means pop to the including file, so leave
+    // FilenameID as -1 in that case.
+    if (!(IsFileExit && Literal.GetString().empty()))
+      FilenameID = SourceMgr.getLineTableFilenameID(Literal.GetString());
   }
 
   // Create a line note with this information.

diff  --git a/clang/test/Preprocessor/line-directive.c b/clang/test/Preprocessor/line-directive.c
index 2ebe87e4680d0..d0f615005e9e9 100644
--- a/clang/test/Preprocessor/line-directive.c
+++ b/clang/test/Preprocessor/line-directive.c
@@ -1,6 +1,18 @@
 // RUN: %clang_cc1 -std=c99 -fsyntax-only -verify -pedantic %s
 // RUN: not %clang_cc1 -E %s 2>&1 | grep 'blonk.c:92:2: error: ABC'
 // RUN: not %clang_cc1 -E %s 2>&1 | grep 'blonk.c:93:2: error: DEF'
+// RUN: not %clang_cc1 -E %s 2>&1 | grep 'line-directive.c:11:2: error: MAIN7'
+// RUN: not %clang_cc1 -E %s 2>&1 | grep 'enter-1:118:2: error: ENTER1'
+// RUN: not %clang_cc1 -E %s 2>&1 | grep 'main:121:2: error: MAIN2'
+
+// expected-error at +1{{cannot pop empty include stack}}
+# 20 "" 2
+
+// a push/pop before any other line control
+# 10 "enter-0" 1
+# 11 "" 2 // pop to main file
+#error MAIN7
+// expected-error at -1{{MAIN7}}
 
 #line 'a'            // expected-error {{#line directive requires a positive integer argument}}
 #line 0              // expected-warning {{#line directive with zero argument is a GNU extension}}
@@ -103,4 +115,12 @@ _LINE__ == 52 ? 1: -1];  /* line marker is location of first _ */
 #line 0 "line-directive.c" // expected-warning {{#line directive with zero argument is a GNU extension}}
 undefined t; // expected-error {{unknown type name 'undefined'}}
 
-
+# 115 "main"
+# 116 "enter-1" 1
+# 117 "enter-2" 1
+# 118 "" 2 // pop to enter-1
+#error ENTER1
+// expected-error at -1{{ENTER1}}
+# 121 "" 2 // pop to "main"
+#error MAIN2
+// expected-error at -1{{MAIN2}}


        


More information about the cfe-commits mailing list