From: Axel Naumann Date: Thu, 27 Jan 2011 10:55:51 +0000 (+0000) Subject: TextDiagnosticPrinter.cpp: Show diagnostics as far as possible even with invalid... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=04331169f04198eb769925fa17696a21989c9d8b;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@124374 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Basic/FileManager.cpp b/lib/Basic/FileManager.cpp index bb37c999d1..cbe90bfdc1 100644 --- a/lib/Basic/FileManager.cpp +++ b/lib/Basic/FileManager.cpp @@ -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); diff --git a/lib/Frontend/TextDiagnosticPrinter.cpp b/lib/Frontend/TextDiagnosticPrinter.cpp index 200054ab11..04c6a68023 100644 --- a/lib/Frontend/TextDiagnosticPrinter.cpp +++ b/lib/Frontend/TextDiagnosticPrinter.cpp @@ -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(); - } + 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(); - // 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 << ':'; + // 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(); } - 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 BInfo = SM.getDecomposedLoc(B); - std::pair 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 << ':'; + // 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 BInfo = SM.getDecomposedLoc(B); + std::pair 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) diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index b569f4b621..7e7bd253af 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -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; diff --git a/test/PCH/missing-file.cpp b/test/PCH/missing-file.cpp new file mode 100644 index 0000000000..f7d00e1aef --- /dev/null +++ b/test/PCH/missing-file.cpp @@ -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 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(); +} +#endif