TextDiagnosticPrinter.cpp: Show diagnostics as far as possible even with invalid PresomedLoc, instead of just silencing it.

FileManager.cpp: Allow virtual files in nonexistent directories.
FileManager.cpp: Close FileDescriptor for virtual files that correspond to actual files.
FileManager.cpp: Enable virtual files to be created even for files that were flagged as NON_EXISTENT_FILE, e.g. by a prior (unsuccessful) addFile().

ASTReader.cpp: Read a PCH even if the original source files cannot be found.

Add a test for reading a PCH of a file that has been removed and diagnostics referencing that file.

llvm-svn: 124374
This commit is contained in:
Axel Naumann 2011-01-27 10:55:51 +00:00
parent 284c48fff6
commit 63fbaeda29
4 changed files with 135 additions and 78 deletions

View File

@ -350,18 +350,17 @@ FileManager::getVirtualFile(llvm::StringRef Filename, off_t Size,
FileEntries.GetOrCreateValue(Filename);
// See if there is already an entry in the map.
if (NamedFileEnt.getValue())
return NamedFileEnt.getValue() == NON_EXISTENT_FILE
? 0 : NamedFileEnt.getValue();
if (NamedFileEnt.getValue() && NamedFileEnt.getValue() != NON_EXISTENT_FILE)
return NamedFileEnt.getValue();
++NumFileCacheMisses;
// By default, initialize it to invalid.
NamedFileEnt.setValue(NON_EXISTENT_FILE);
// We allow the directory to not exist. If it does exist we store it.
//
const DirectoryEntry *DirInfo = getDirectoryFromFile(*this, Filename);
if (DirInfo == 0) // Directory doesn't exist, file can't exist.
return 0;
FileEntry *UFE = new FileEntry();
VirtualFileEntries.push_back(UFE);
@ -381,8 +380,13 @@ FileManager::getVirtualFile(llvm::StringRef Filename, off_t Size,
// newly-created file entry.
int FileDescriptor = -1;
struct stat StatBuf;
if (getStatValue(InterndFileName, StatBuf, &FileDescriptor))
if (getStatValue(InterndFileName, StatBuf, &FileDescriptor)) {
// If the stat process opened the file, close it to avoid a FD leak.
if (FileDescriptor != -1)
close(FileDescriptor);
return UFE;
}
UFE->FD = FileDescriptor;
llvm::SmallString<128> FilePath(UFE->Name);

View File

@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Lex/Lexer.h"
@ -139,8 +140,9 @@ void TextDiagnosticPrinter::HighlightRange(const CharSourceRange &R,
(SourceLine[EndColNo-1] == ' ' || SourceLine[EndColNo-1] == '\t'))
--EndColNo;
// If the start/end passed each other, then we are trying to highlight a range
// that just exists in whitespace, which must be some sort of other bug.
// If the start/end passed each other, then we are trying to highlight a
// range that just exists in whitespace, which must be some sort of other
// bug.
assert(StartColNo <= EndColNo && "Trying to highlight whitespace??");
}
@ -781,78 +783,94 @@ void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level,
if (Info.getLocation().isValid()) {
const SourceManager &SM = Info.getSourceManager();
PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation());
if (PLoc.isInvalid())
return;
unsigned LineNo = PLoc.getLine();
// First, if this diagnostic is not in the main file, print out the
// "included from" lines.
if (LastWarningLoc != PLoc.getIncludeLoc()) {
LastWarningLoc = PLoc.getIncludeLoc();
PrintIncludeStack(LastWarningLoc, SM);
StartOfLocationInfo = OS.tell();
}
// Compute the column number.
if (DiagOpts->ShowLocation && PLoc.isValid()) {
if (DiagOpts->ShowColors)
OS.changeColor(savedColor, true);
// Emit a Visual Studio compatible line number syntax.
if (LangOpts && LangOpts->Microsoft) {
OS << PLoc.getFilename() << '(' << LineNo << ')';
OS << " : ";
} else {
OS << PLoc.getFilename() << ':' << LineNo << ':';
if (DiagOpts->ShowColumn)
if (unsigned ColNo = PLoc.getColumn())
OS << ColNo << ':';
}
if (DiagOpts->ShowSourceRanges && Info.getNumRanges()) {
FileID CaretFileID =
SM.getFileID(SM.getInstantiationLoc(Info.getLocation()));
bool PrintedRange = false;
for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) {
// Ignore invalid ranges.
if (!Info.getRange(i).isValid()) continue;
SourceLocation B = Info.getRange(i).getBegin();
SourceLocation E = Info.getRange(i).getEnd();
B = SM.getInstantiationLoc(B);
E = SM.getInstantiationLoc(E);
// If the End location and the start location are the same and are a
// macro location, then the range was something that came from a macro
// expansion or _Pragma. If this is an object-like macro, the best we
// can do is to highlight the range. If this is a function-like
// macro, we'd also like to highlight the arguments.
if (B == E && Info.getRange(i).getEnd().isMacroID())
E = SM.getInstantiationRange(Info.getRange(i).getEnd()).second;
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);
// If the start or end of the range is in another file, just discard
// it.
if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
continue;
// Add in the length of the token, so that we cover multi-char tokens.
unsigned TokSize = 0;
if (Info.getRange(i).isTokenRange())
TokSize = Lexer::MeasureTokenLength(E, SM, *LangOpts);
OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':'
<< SM.getColumnNumber(BInfo.first, BInfo.second) << '-'
<< SM.getLineNumber(EInfo.first, EInfo.second) << ':'
<< (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) << '}';
PrintedRange = true;
if (PLoc.isInvalid()) {
// At least print the file name if available:
FileID FID = SM.getFileID(Info.getLocation());
if (!FID.isInvalid()) {
const FileEntry* FE = SM.getFileEntryForID(FID);
if (FE && FE->getName()) {
OS << FE->getName();
if (FE->getDevice() == 0 && FE->getInode() == 0
&& FE->getFileMode() == 0) {
// in PCH is a guess, but a good one:
OS << " (in PCH)";
}
OS << ": ";
}
}
} else {
unsigned LineNo = PLoc.getLine();
if (PrintedRange)
OS << ':';
// First, if this diagnostic is not in the main file, print out the
// "included from" lines.
if (LastWarningLoc != PLoc.getIncludeLoc()) {
LastWarningLoc = PLoc.getIncludeLoc();
PrintIncludeStack(LastWarningLoc, SM);
StartOfLocationInfo = OS.tell();
}
// Compute the column number.
if (DiagOpts->ShowLocation && PLoc.isValid()) {
if (DiagOpts->ShowColors)
OS.changeColor(savedColor, true);
// Emit a Visual Studio compatible line number syntax.
if (LangOpts && LangOpts->Microsoft) {
OS << PLoc.getFilename() << '(' << LineNo << ')';
OS << " : ";
} else {
OS << PLoc.getFilename() << ':' << LineNo << ':';
if (DiagOpts->ShowColumn)
if (unsigned ColNo = PLoc.getColumn())
OS << ColNo << ':';
}
if (DiagOpts->ShowSourceRanges && Info.getNumRanges()) {
FileID CaretFileID =
SM.getFileID(SM.getInstantiationLoc(Info.getLocation()));
bool PrintedRange = false;
for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) {
// Ignore invalid ranges.
if (!Info.getRange(i).isValid()) continue;
SourceLocation B = Info.getRange(i).getBegin();
SourceLocation E = Info.getRange(i).getEnd();
B = SM.getInstantiationLoc(B);
E = SM.getInstantiationLoc(E);
// If the End location and the start location are the same and are a
// macro location, then the range was something that came from a
// macro expansion or _Pragma. If this is an object-like macro, the
// best we can do is to highlight the range. If this is a
// function-like macro, we'd also like to highlight the arguments.
if (B == E && Info.getRange(i).getEnd().isMacroID())
E = SM.getInstantiationRange(Info.getRange(i).getEnd()).second;
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);
// If the start or end of the range is in another file, just discard
// it.
if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
continue;
// Add in the length of the token, so that we cover multi-char
// tokens.
unsigned TokSize = 0;
if (Info.getRange(i).isTokenRange())
TokSize = Lexer::MeasureTokenLength(E, SM, *LangOpts);
OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':'
<< SM.getColumnNumber(BInfo.first, BInfo.second) << '-'
<< SM.getLineNumber(EInfo.first, EInfo.second) << ':'
<< (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize)
<< '}';
PrintedRange = true;
}
if (PrintedRange)
OS << ':';
}
}
OS << ' ';
if (DiagOpts->ShowColors)

View File

@ -1232,6 +1232,9 @@ ASTReader::ASTReadResult ASTReader::ReadSLocEntryRecord(unsigned ID) {
std::string Filename(BlobStart, BlobStart + BlobLen);
MaybeAddSystemRootToFilename(Filename);
const FileEntry *File = FileMgr.getFile(Filename);
if (File == 0)
File = FileMgr.getVirtualFile(Filename, (off_t)Record[4],
(time_t)Record[5]);
if (File == 0) {
std::string ErrorStr = "could not find file '";
ErrorStr += Filename;

View File

@ -0,0 +1,32 @@
// Test reading of PCH without original input files.
// Generate the PCH, removing the original file:
// RUN: echo 'struct S{char c; int i; }; void foo() {}' > %t.h
// RUN: echo 'template <typename T> void tf() { T::foo(); }' >> %t.h
// RUN: echo '#define RETURN return &i' >> %t.h
// RUN: %clang_cc1 -x c++ -emit-pch -o %t.h.pch %t.h
// RUN: rm %t.h
// Check diagnostic with location in original source:
// RUN: %clang_cc1 -include-pch %t.h.pch -Wpadded -emit-obj %s 2> %t.stderr
// RUN: grep 'bytes to align' %t.stderr
// Check diagnostic with 2nd location in original source:
// RUN: not %clang_cc1 -DREDECL -include-pch %t.h.pch -emit-obj %s 2> %t.stderr
// RUN: grep 'previous definition is here' %t.stderr
// Check diagnostic with instantiation location in original source:
// RUN: not %clang_cc1 -DINSTANTIATION -include-pch %t.h.pch -emit-obj %s 2> %t.stderr
// RUN: grep 'cannot be used prior to' %t.stderr
void qq(S*) {}
#ifdef REDECL
float foo() {return 0f;}
#endif
#ifdef INSTANTIATION
void f() {
tf<int>();
}
#endif