diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -4038,6 +4038,52 @@ 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 + # + +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 --- a/clang/lib/Basic/SourceManager.cpp +++ b/clang/lib/Basic/SourceManager.cpp @@ -207,28 +207,30 @@ SrcMgr::CharacteristicKind FileKind) { std::vector &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 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -1451,11 +1451,15 @@ 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 --- 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@+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@-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 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@-1{{ENTER1}} +# 121 "" 2 // pop to "main" +#error MAIN2 +// expected-error@-1{{MAIN2}}