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
This commit is contained in:
Reid Kleckner 2017-05-22 21:42:58 +00:00
parent b238cb8fbc
commit eb00ee07be
9 changed files with 66 additions and 104 deletions

View File

@ -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; }

View File

@ -101,8 +101,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);

View File

@ -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<LineEntry> &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<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!");
@ -281,40 +255,13 @@ 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<FileID, unsigned> 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<SrcMgr::FileInfo&>(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<FileID, unsigned> LocInfo = getDecomposedExpansionLoc(Loc);
bool Invalid = false;
@ -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;

View File

@ -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();
}

View File

@ -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);
}

View File

@ -477,7 +477,7 @@ void Preprocessor::HandlePragmaSystemHeader(Token &SysHeaderTok) {
// Create a line note with this information.
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.

View File

@ -0,0 +1,2 @@
#line 1 "foo.h"
foo();

View File

@ -0,0 +1 @@
foo();

View File

@ -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 <noline.h>
#include <line.h>
// 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