[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