From eb00ee07beff4fab367fa16b8074ba9856469f52 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Mon, 22 May 2017 21:42:58 +0000 Subject: [PATCH] Give files from #line the characteristics of the current file This allows #line directives to appear in system headers that have code that clang would normally warn on. This is compatible with GCC, which is easy to test by running `gcc -E`. Fixes PR30752 llvm-svn: 303582 --- clang/include/clang/Basic/SourceManager.h | 3 +- clang/include/clang/Basic/SourceManagerInternals.h | 2 - clang/lib/Basic/SourceManager.cpp | 93 ++++------------------ clang/lib/Frontend/FrontendAction.cpp | 3 +- clang/lib/Lex/PPDirectives.cpp | 42 +++++----- clang/lib/Lex/Pragma.cpp | 4 +- .../test/Frontend/Inputs/SystemHeaderPrefix/line.h | 2 + .../Frontend/Inputs/SystemHeaderPrefix/noline.h | 1 + clang/test/Frontend/system-header-line-directive.c | 20 +++++ 9 files changed, 66 insertions(+), 104 deletions(-) create mode 100644 clang/test/Frontend/Inputs/SystemHeaderPrefix/line.h create mode 100644 clang/test/Frontend/Inputs/SystemHeaderPrefix/noline.h create mode 100644 clang/test/Frontend/system-header-line-directive.c diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h index 6960ea6..eda8029 100644 --- a/clang/include/clang/Basic/SourceManager.h +++ b/clang/include/clang/Basic/SourceManager.h @@ -1399,10 +1399,9 @@ public: /// specified by Loc. /// /// If FilenameID is -1, it is considered to be unspecified. - void AddLineNote(SourceLocation Loc, unsigned LineNo, int FilenameID); void AddLineNote(SourceLocation Loc, unsigned LineNo, int FilenameID, bool IsFileEntry, bool IsFileExit, - bool IsSystemHeader, bool IsExternCHeader); + SrcMgr::CharacteristicKind FileKind); /// \brief Determine if the source manager has a line table. bool hasLineTable() const { return LineTable != nullptr; } diff --git a/clang/include/clang/Basic/SourceManagerInternals.h b/clang/include/clang/Basic/SourceManagerInternals.h index e65c97b..9403dea 100644 --- a/clang/include/clang/Basic/SourceManagerInternals.h +++ b/clang/include/clang/Basic/SourceManagerInternals.h @@ -102,8 +102,6 @@ public: unsigned getNumFilenames() const { return FilenamesByID.size(); } void AddLineNote(FileID FID, unsigned Offset, - unsigned LineNo, int FilenameID); - void AddLineNote(FileID FID, unsigned Offset, unsigned LineNo, int FilenameID, unsigned EntryExit, SrcMgr::CharacteristicKind FileKind); diff --git a/clang/lib/Basic/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp index 156aa0b..c5cff74 100644 --- a/clang/lib/Basic/SourceManager.cpp +++ b/clang/lib/Basic/SourceManager.cpp @@ -183,48 +183,22 @@ unsigned LineTableInfo::getLineTableFilenameID(StringRef Name) { return IterBool.first->second; } -/// AddLineNote - Add a line note to the line table that indicates that there -/// is a \#line at the specified FID/Offset location which changes the presumed -/// location to LineNo/FilenameID. -void LineTableInfo::AddLineNote(FileID FID, unsigned Offset, - unsigned LineNo, int FilenameID) { - std::vector &Entries = LineEntries[FID]; - - assert((Entries.empty() || Entries.back().FileOffset < Offset) && - "Adding line entries out of order!"); - - SrcMgr::CharacteristicKind Kind = SrcMgr::C_User; - unsigned IncludeOffset = 0; - - if (!Entries.empty()) { - // If this is a '#line 4' after '#line 42 "foo.h"', make sure to remember - // that we are still in "foo.h". - if (FilenameID == -1) - FilenameID = Entries.back().FilenameID; - - // If we are after a line marker that switched us to system header mode, or - // that set #include information, preserve it. - Kind = Entries.back().FileKind; - IncludeOffset = Entries.back().IncludeOffset; - } - - Entries.push_back(LineEntry::get(Offset, LineNo, FilenameID, Kind, - IncludeOffset)); -} - -/// AddLineNote This is the same as the previous version of AddLineNote, but is -/// used for GNU line markers. If EntryExit is 0, then this doesn't change the -/// presumed \#include stack. If it is 1, this is a file entry, if it is 2 then -/// this is a file exit. FileKind specifies whether this is a system header or -/// extern C system header. -void LineTableInfo::AddLineNote(FileID FID, unsigned Offset, - unsigned LineNo, int FilenameID, - unsigned EntryExit, +/// Add a line note to the line table that indicates that there is a \#line or +/// GNU line marker at the specified FID/Offset location which changes the +/// presumed location to LineNo/FilenameID. If EntryExit is 0, then this doesn't +/// change the presumed \#include stack. If it is 1, this is a file entry, if +/// it is 2 then this is a file exit. FileKind specifies whether this is a +/// system header or extern C system header. +void LineTableInfo::AddLineNote(FileID FID, unsigned Offset, unsigned LineNo, + int FilenameID, unsigned EntryExit, SrcMgr::CharacteristicKind FileKind) { - assert(FilenameID != -1 && "Unspecified filename should use other accessor"); - 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!"); @@ -281,47 +255,20 @@ unsigned SourceManager::getLineTableFilenameID(StringRef Name) { return getLineTable().getLineTableFilenameID(Name); } - /// AddLineNote - Add a line note to the line table for the FileID and offset /// specified by Loc. If FilenameID is -1, it is considered to be /// unspecified. void SourceManager::AddLineNote(SourceLocation Loc, unsigned LineNo, - int FilenameID) { - std::pair LocInfo = getDecomposedExpansionLoc(Loc); - - bool Invalid = false; - const SLocEntry &Entry = getSLocEntry(LocInfo.first, &Invalid); - if (!Entry.isFile() || Invalid) - return; - - const SrcMgr::FileInfo &FileInfo = Entry.getFile(); - - // Remember that this file has #line directives now if it doesn't already. - const_cast(FileInfo).setHasLineDirectives(); - - getLineTable().AddLineNote(LocInfo.first, LocInfo.second, LineNo, FilenameID); -} - -/// AddLineNote - Add a GNU line marker to the line table. -void SourceManager::AddLineNote(SourceLocation Loc, unsigned LineNo, int FilenameID, bool IsFileEntry, - bool IsFileExit, bool IsSystemHeader, - bool IsExternCHeader) { - // If there is no filename and no flags, this is treated just like a #line, - // which does not change the flags of the previous line marker. - if (FilenameID == -1) { - assert(!IsFileEntry && !IsFileExit && !IsSystemHeader && !IsExternCHeader && - "Can't set flags without setting the filename!"); - return AddLineNote(Loc, LineNo, FilenameID); - } - + bool IsFileExit, + SrcMgr::CharacteristicKind FileKind) { std::pair LocInfo = getDecomposedExpansionLoc(Loc); bool Invalid = false; const SLocEntry &Entry = getSLocEntry(LocInfo.first, &Invalid); if (!Entry.isFile() || Invalid) return; - + const SrcMgr::FileInfo &FileInfo = Entry.getFile(); // Remember that this file has #line directives now if it doesn't already. @@ -329,14 +276,6 @@ void SourceManager::AddLineNote(SourceLocation Loc, unsigned LineNo, (void) getLineTable(); - SrcMgr::CharacteristicKind FileKind; - if (IsExternCHeader) - FileKind = SrcMgr::C_ExternCSystem; - else if (IsSystemHeader) - FileKind = SrcMgr::C_System; - else - FileKind = SrcMgr::C_User; - unsigned EntryExit = 0; if (IsFileEntry) EntryExit = 1; diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp index 1fbb2b0..874c1b6 100644 --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -252,7 +252,8 @@ static SourceLocation ReadOriginalFileName(CompilerInstance &CI, if (AddLineNote) CI.getSourceManager().AddLineNote( - LineNoLoc, LineNo, SourceMgr.getLineTableFilenameID(InputFile)); + LineNoLoc, LineNo, SourceMgr.getLineTableFilenameID(InputFile), false, + false, SrcMgr::C_User); return T.getLocation(); } diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 0534aeb..030717b 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -1171,18 +1171,26 @@ void Preprocessor::HandleLineDirective() { CheckEndOfDirective("line", true); } - SourceMgr.AddLineNote(DigitTok.getLocation(), LineNo, FilenameID); + // Take the file kind of the file containing the #line directive. #line + // directives are often used for generated sources from the same codebase, so + // the new file should generally be classified the same way as the current + // file. This is visible in GCC's pre-processed output, which rewrites #line + // to GNU line markers. + SrcMgr::CharacteristicKind FileKind = + SourceMgr.getFileCharacteristic(DigitTok.getLocation()); + + SourceMgr.AddLineNote(DigitTok.getLocation(), LineNo, FilenameID, false, + false, FileKind); if (Callbacks) Callbacks->FileChanged(CurPPLexer->getSourceLocation(), - PPCallbacks::RenameFile, - SrcMgr::C_User); + PPCallbacks::RenameFile, FileKind); } /// 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, + SrcMgr::CharacteristicKind &FileKind, Preprocessor &PP) { unsigned FlagVal; Token FlagTok; @@ -1233,7 +1241,7 @@ static bool ReadLineMarkerFlags(bool &IsFileEntry, bool &IsFileExit, return true; } - IsSystemHeader = true; + FileKind = SrcMgr::C_System; PP.Lex(FlagTok); if (FlagTok.is(tok::eod)) return false; @@ -1247,7 +1255,7 @@ static bool ReadLineMarkerFlags(bool &IsFileEntry, bool &IsFileExit, return true; } - IsExternCHeader = true; + FileKind = SrcMgr::C_ExternCSystem; PP.Lex(FlagTok); if (FlagTok.is(tok::eod)) return false; @@ -1277,14 +1285,15 @@ void Preprocessor::HandleDigitDirective(Token &DigitTok) { Lex(StrTok); bool IsFileEntry = false, IsFileExit = false; - bool IsSystemHeader = false, IsExternCHeader = false; int FilenameID = -1; + SrcMgr::CharacteristicKind FileKind = SrcMgr::C_User; // If the StrTok is "eod", then it wasn't present. Otherwise, it must be a // string followed by eod. - if (StrTok.is(tok::eod)) - ; // ok - else if (StrTok.isNot(tok::string_literal)) { + if (StrTok.is(tok::eod)) { + // Treat this like "#line NN", which doesn't change file characteristics. + FileKind = SourceMgr.getFileCharacteristic(DigitTok.getLocation()); + } else if (StrTok.isNot(tok::string_literal)) { Diag(StrTok, diag::err_pp_linemarker_invalid_filename); return DiscardUntilEndOfDirective(); } else if (StrTok.hasUDSuffix()) { @@ -1303,15 +1312,13 @@ void Preprocessor::HandleDigitDirective(Token &DigitTok) { FilenameID = SourceMgr.getLineTableFilenameID(Literal.GetString()); // If a filename was present, read any flags that are present. - if (ReadLineMarkerFlags(IsFileEntry, IsFileExit, - IsSystemHeader, IsExternCHeader, *this)) + if (ReadLineMarkerFlags(IsFileEntry, IsFileExit, FileKind, *this)) return; } // Create a line note with this information. - SourceMgr.AddLineNote(DigitTok.getLocation(), LineNo, FilenameID, - IsFileEntry, IsFileExit, - IsSystemHeader, IsExternCHeader); + SourceMgr.AddLineNote(DigitTok.getLocation(), LineNo, FilenameID, IsFileEntry, + IsFileExit, FileKind); // If the preprocessor has callbacks installed, notify them of the #line // change. This is used so that the line marker comes out in -E mode for @@ -1322,11 +1329,6 @@ void Preprocessor::HandleDigitDirective(Token &DigitTok) { Reason = PPCallbacks::EnterFile; else if (IsFileExit) Reason = PPCallbacks::ExitFile; - SrcMgr::CharacteristicKind FileKind = SrcMgr::C_User; - if (IsExternCHeader) - FileKind = SrcMgr::C_ExternCSystem; - else if (IsSystemHeader) - FileKind = SrcMgr::C_System; Callbacks->FileChanged(CurPPLexer->getSourceLocation(), Reason, FileKind); } diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index 99d5618..2d078a4 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -475,9 +475,9 @@ void Preprocessor::HandlePragmaSystemHeader(Token &SysHeaderTok) { // Emit a line marker. This will change any source locations from this point // forward to realize they are in a system header. // Create a line note with this information. - SourceMgr.AddLineNote(SysHeaderTok.getLocation(), PLoc.getLine()+1, + SourceMgr.AddLineNote(SysHeaderTok.getLocation(), PLoc.getLine() + 1, FilenameID, /*IsEntry=*/false, /*IsExit=*/false, - /*IsSystem=*/true, /*IsExternC=*/false); + SrcMgr::C_System); } /// HandlePragmaDependency - Handle \#pragma GCC dependency "foo" blah. diff --git a/clang/test/Frontend/Inputs/SystemHeaderPrefix/line.h b/clang/test/Frontend/Inputs/SystemHeaderPrefix/line.h new file mode 100644 index 0000000..c4c15e5 --- /dev/null +++ b/clang/test/Frontend/Inputs/SystemHeaderPrefix/line.h @@ -0,0 +1,2 @@ +#line 1 "foo.h" +foo(); diff --git a/clang/test/Frontend/Inputs/SystemHeaderPrefix/noline.h b/clang/test/Frontend/Inputs/SystemHeaderPrefix/noline.h new file mode 100644 index 0000000..a280f9a --- /dev/null +++ b/clang/test/Frontend/Inputs/SystemHeaderPrefix/noline.h @@ -0,0 +1 @@ +foo(); diff --git a/clang/test/Frontend/system-header-line-directive.c b/clang/test/Frontend/system-header-line-directive.c new file mode 100644 index 0000000..16dde643 --- /dev/null +++ b/clang/test/Frontend/system-header-line-directive.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -Wall %s -isystem %S/Inputs/SystemHeaderPrefix -verify +// RUN: %clang_cc1 %s -E -o - -isystem %S/Inputs/SystemHeaderPrefix | FileCheck %s +#include +#include + +// This tests that "#line" directives in system headers preserve system +// header-ness just like GNU line markers that don't have filenames. This was +// PR30752. + +// expected-no-diagnostics + +// CHECK: # {{[0-9]+}} "{{.*}}system-header-line-directive.c" 2 +// CHECK: # 1 "{{.*}}noline.h" 1 3 +// CHECK: foo(); +// CHECK: # 4 "{{.*}}system-header-line-directive.c" 2 +// CHECK: # 1 "{{.*}}line.h" 1 3 +// The "3" below indicates that "foo.h" is considered a system header. +// CHECK: # 1 "foo.h" 3 +// CHECK: foo(); +// CHECK: # {{[0-9]+}} "{{.*}}system-header-line-directive.c" 2 -- 2.7.4