class ASTRecordLayout;
class BlockExpr;
class CharUnits;
+ class Diagnostic;
class Expr;
class ExternalASTSource;
class IdentifierTable;
TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl; }
- const char *getCommentForDecl(const Decl *D);
+ const char *getCommentForDecl(const Decl *D, Diagnostic &Diags);
// Builtin Types.
CanQualType VoidTy;
def err_target_unknown_abi : Error<"unknown target ABI '%0'">;
def err_target_invalid_feature : Error<"invalid target feature '%0'">;
+// Anywhere we open a file
+def err_cannot_open_file : Error<"cannot open file '%0': %1">, DefaultFatal;
+
}
#include "clang/Basic/SourceLocation.h"
#include "llvm/Support/Allocator.h"
#include "llvm/System/DataTypes.h"
+#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/DenseMap.h"
#include <vector>
#include <cassert>
namespace llvm {
class MemoryBuffer;
+class StringRef;
}
namespace clang {
+class Diagnostic;
class SourceManager;
class FileManager;
class FileEntry;
class LineTableInfo;
-
+
+/// \brief Class used as a return value by operations that return an
+/// \c llvm::MemoryBuffer.
+///
+/// Since not all source-manager routines that return buffers are guaranteed
+/// to succeed,
+class BufferResult {
+ struct FailureData;
+ llvm::PointerUnion<const llvm::MemoryBuffer *, FailureData *> Data;
+
+ // Cannot copy buffer result structures
+ BufferResult &operator=(const BufferResult &Other);
+
+public:
+ BufferResult(const BufferResult &Other);
+ BufferResult(const llvm::MemoryBuffer *Buffer) : Data(Buffer) { }
+ BufferResult(const char *FileName, llvm::StringRef ErrorStr,
+ const llvm::MemoryBuffer *Buffer = 0);
+ ~BufferResult();
+
+ // \brief Determine whether there was any failure when finding this buffer.
+ bool isInvalid() const;
+
+ /// \brief Retrieve the memory buffer that this result refers to. If an
+ /// error occurs, emits a diagnostic via the given diagnostics object and
+ /// may return NULL.
+ const llvm::MemoryBuffer *getBuffer(Diagnostic &Diags) const;
+
+ /// \brief Retrieve the memory buffer that this result refers to. If an error
+ /// occurs, provides the file name and a non-empty error string to indicate
+ /// what failed, and may return NULL.
+ const llvm::MemoryBuffer *getBuffer(llvm::StringRef &FileName,
+ std::string &Error) const;
+
+ // FIXME: TEMPORARY! Allows a buffer result to be interpreted as a buffer,
+ // which is very unsafe (but is used throughout Clang). Note that this will
+ // spit a diagnostic to standard error before returning the buffer.
+ operator const llvm::MemoryBuffer *() const;
+
+ // FIXME: TEMPORARY! Allows a buffer result to be interpreted like a smart
+ // pointer to a buffer, which is very unsafe. Note that this will emit a
+ // diagnostic to standard error before returning the buffer.
+ const llvm::MemoryBuffer * operator->() const { return *this; }
+};
+
/// SrcMgr - Public enums and private classes that are part of the
/// SourceManager implementation.
///
/// if SourceLineCache is non-null.
unsigned NumLines;
- /// getBuffer - Returns the memory buffer for the associated content. If
- /// there is an error opening this buffer the first time, this manufactures
- /// a temporary buffer and returns a non-empty error string.
- const llvm::MemoryBuffer *getBuffer(std::string *ErrorStr = 0) const;
+ /// getBuffer - Returns the memory buffer for the associated content.
+ BufferResult getBuffer() const;
/// getSize - Returns the size of the content encapsulated by this
/// ContentCache. This can be the size of the source file or the size of an
unsigned Offset = 0);
/// \brief Retrieve the memory buffer associated with the given file.
- const llvm::MemoryBuffer *getMemoryBufferForFile(const FileEntry *File);
+ BufferResult getMemoryBufferForFile(const FileEntry *File);
/// \brief Override the contents of the given source file by providing an
/// already-allocated buffer.
/// getBuffer - Return the buffer for the specified FileID. If there is an
/// error opening this buffer the first time, this manufactures a temporary
/// buffer and returns a non-empty error string.
- const llvm::MemoryBuffer *getBuffer(FileID FID, std::string *Error = 0) const{
- return getSLocEntry(FID).getFile().getContentCache()->getBuffer(Error);
+ BufferResult getBuffer(FileID FID) const{
+ return getSLocEntry(FID).getFile().getContentCache()->getBuffer();
}
/// getFileEntryForID - Returns the FileEntry record for the provided FileID.
/// getBufferData - Return a pointer to the start and end of the source buffer
/// data for the specified FileID.
- std::pair<const char*, const char*> getBufferData(FileID FID) const;
+ ///
+ /// If an error occurs while reading in the file, provides the file name
+ /// and a non-empty error string and returns a pair of NULL pointers.
+ std::pair<const char*, const char*> getBufferData(FileID FID,
+ llvm::StringRef &FileName,
+ std::string &Error) const;
+ /// getBufferData - Return a pointer to the start and end of the source buffer
+ /// data for the specified FileID.
+ ///
+ /// If an error occurs while reading in the file, emits a diagnostic to the
+ /// given \c Diagnostic object and returns a pair of NULL pointers.
+ std::pair<const char*, const char*> getBufferData(FileID FID,
+ Diagnostic &Diags) const;
+
//===--------------------------------------------------------------------===//
// SourceLocation manipulation methods.
/// (which requires a < after the Doxygen-comment delimiter). Otherwise,
/// we only return true when we find a non-member comment.
static bool
-isDoxygenComment(SourceManager &SourceMgr, SourceRange Comment,
- bool Member = false) {
+isDoxygenComment(SourceManager &SourceMgr, Diagnostic &Diags,
+ SourceRange Comment, bool Member = false) {
const char *BufferStart
- = SourceMgr.getBufferData(SourceMgr.getFileID(Comment.getBegin())).first;
+ = SourceMgr.getBufferData(SourceMgr.getFileID(Comment.getBegin()),
+ Diags).first;
+ if (!BufferStart)
+ return false;
+
const char *Start = BufferStart + SourceMgr.getFileOffset(Comment.getBegin());
const char* End = BufferStart + SourceMgr.getFileOffset(Comment.getEnd());
/// \brief Retrieve the comment associated with the given declaration, if
/// it has one.
-const char *ASTContext::getCommentForDecl(const Decl *D) {
+const char *ASTContext::getCommentForDecl(const Decl *D, Diagnostic &Diags) {
if (!D)
return 0;
std::pair<FileID, unsigned> DeclStartDecomp
= SourceMgr.getDecomposedLoc(DeclStartLoc);
const char *FileBufferStart
- = SourceMgr.getBufferData(DeclStartDecomp.first).first;
-
+ = SourceMgr.getBufferData(DeclStartDecomp.first, Diags).first;
+ if (!FileBufferStart)
+ return 0;
+
// First check whether we have a comment for a member.
if (LastComment != Comments.end() &&
!isa<TagDecl>(D) && !isa<NamespaceDecl>(D) &&
- isDoxygenComment(SourceMgr, *LastComment, true)) {
+ isDoxygenComment(SourceMgr, Diags, *LastComment, true)) {
std::pair<FileID, unsigned> LastCommentEndDecomp
= SourceMgr.getDecomposedLoc(LastComment->getEnd());
if (DeclStartDecomp.first == LastCommentEndDecomp.first &&
return 0;
// Check that we actually have a Doxygen comment.
- if (!isDoxygenComment(SourceMgr, *LastComment))
+ if (!isDoxygenComment(SourceMgr, Diags, *LastComment))
return 0;
// Compute the starting line for the declaration and for the end of the
}
// If this comment is not a Doxygen comment, we're done.
- if (!isDoxygenComment(SourceMgr, *FirstComment)) {
+ if (!isDoxygenComment(SourceMgr, Diags, *FirstComment)) {
++FirstComment;
break;
}
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/SourceManagerInternals.h"
+#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/System/Path.h"
#include <algorithm>
+#include <string>
+
using namespace clang;
using namespace SrcMgr;
using llvm::MemoryBuffer;
// SourceManager Helper Classes
//===----------------------------------------------------------------------===//
+struct BufferResult::FailureData {
+ const llvm::MemoryBuffer *Buffer;
+ const char *FileName;
+ std::string ErrorStr;
+};
+
+BufferResult::BufferResult(const BufferResult &Other) {
+ if (const llvm::MemoryBuffer *Buffer
+ = Other.Data.dyn_cast<const llvm::MemoryBuffer *>()) {
+ Data = Buffer;
+ return;
+ }
+
+ Data = new FailureData(*Other.Data.get<FailureData *>());
+}
+
+BufferResult::BufferResult(const char *FileName, llvm::StringRef ErrorStr,
+ const llvm::MemoryBuffer *Buffer) {
+ FailureData *FD = new FailureData;
+ FD->FileName = FileName;
+ FD->ErrorStr = ErrorStr;
+ FD->Buffer = Buffer;
+ Data = FD;
+}
+
+BufferResult::~BufferResult() {
+ if (FailureData *FD = Data.dyn_cast<FailureData *>())
+ delete FD;
+}
+
+bool BufferResult::isInvalid() const {
+ return Data.is<FailureData *>();
+}
+
+const llvm::MemoryBuffer *BufferResult::getBuffer(Diagnostic &Diags) const {
+ llvm::StringRef FileName;
+ std::string ErrorMsg;
+ const llvm::MemoryBuffer *Result = getBuffer(FileName, ErrorMsg);
+ if (!ErrorMsg.empty()) {
+ Diags.Report(diag::err_cannot_open_file)
+ << FileName << ErrorMsg;
+ }
+ return Result;
+}
+
+const llvm::MemoryBuffer *BufferResult::getBuffer(llvm::StringRef &FileName,
+ std::string &Error) const {
+ if (const llvm::MemoryBuffer *Buffer
+ = Data.dyn_cast<const llvm::MemoryBuffer *>())
+ return Buffer;
+
+ FailureData *Fail = Data.get<FailureData *>();
+ FileName = Fail->FileName;
+ Error = Fail->ErrorStr;
+ return Fail->Buffer;
+}
+
+BufferResult::operator const llvm::MemoryBuffer *() const {
+ llvm::StringRef FileName;
+ std::string ErrorMsg;
+ const llvm::MemoryBuffer *Result = getBuffer(FileName, ErrorMsg);
+ if (!ErrorMsg.empty()) {
+ fprintf(stderr, "error: cannot open file '%s': %s\n",
+ FileName.str().c_str(), ErrorMsg.c_str());
+ }
+
+ return Result;
+}
+
ContentCache::~ContentCache() {
delete Buffer;
}
Buffer = B;
}
-const llvm::MemoryBuffer *ContentCache::getBuffer(std::string *ErrorStr) const {
+BufferResult ContentCache::getBuffer() const {
// Lazily create the Buffer for ContentCaches that wrap files.
if (!Buffer && Entry) {
- Buffer = MemoryBuffer::getFile(Entry->getName(), ErrorStr,Entry->getSize());
+ std::string ErrorStr;
+ struct stat FileInfo;
+ Buffer = MemoryBuffer::getFile(Entry->getName(), &ErrorStr,
+ Entry->getSize(), &FileInfo);
// If we were unable to open the file, then we are in an inconsistent
// situation where the content cache referenced a file which no longer
char *Ptr = const_cast<char*>(Buffer->getBufferStart());
for (unsigned i = 0, e = Entry->getSize(); i != e; ++i)
Ptr[i] = FillStr[i % FillStr.size()];
+ return BufferResult(Entry->getName(), ErrorStr, Buffer);
+ } else {
+ // Check that the file's size and modification time is the same as
+ // in the file entry (which may have come from a stat cache).
+ // FIXME: Make these strings localizable.
+ if (FileInfo.st_size != Entry->getSize()) {
+ ErrorStr = "file has changed size since it was originally read";
+ return BufferResult(Entry->getName(), ErrorStr, Buffer);
+ } else if (FileInfo.st_mtime != Entry->getModificationTime()) {
+ ErrorStr = "file has been modified since it was originally read";
+ return BufferResult(Entry->getName(), ErrorStr, Buffer);
+ }
}
}
+
return Buffer;
}
return SourceLocation::getMacroLoc(NextOffset-(TokLength+1));
}
-const llvm::MemoryBuffer *
-SourceManager::getMemoryBufferForFile(const FileEntry *File) {
+BufferResult SourceManager::getMemoryBufferForFile(const FileEntry *File) {
const SrcMgr::ContentCache *IR = getOrCreateContentCache(File);
- if (IR == 0)
- return 0;
-
+ assert(IR && "getOrCreateContentCache() cannot return NULL");
return IR->getBuffer();
}
return false;
}
-/// getBufferData - Return a pointer to the start and end of the source buffer
-/// data for the specified FileID.
std::pair<const char*, const char*>
-SourceManager::getBufferData(FileID FID) const {
- const llvm::MemoryBuffer *Buf = getBuffer(FID);
+SourceManager::getBufferData(FileID FID, llvm::StringRef &FileName,
+ std::string &Error) const {
+ const llvm::MemoryBuffer *Buf = getBuffer(FID).getBuffer(FileName, Error);
+ if (!Error.empty())
+ return std::make_pair((const char *)0, (const char *)0);
return std::make_pair(Buf->getBufferStart(), Buf->getBufferEnd());
}
+std::pair<const char*, const char*>
+SourceManager::getBufferData(FileID FID, Diagnostic &Diags) const {
+ const llvm::MemoryBuffer *Buf = getBuffer(FID).getBuffer(Diags);
+ if (!Buf)
+ return std::make_pair((const char *)0, (const char *)0);
+ return std::make_pair(Buf->getBufferStart(), Buf->getBufferEnd());
+}
//===----------------------------------------------------------------------===//
// SourceLocation manipulation methods.
void RewriteObjC::RewriteInclude() {
SourceLocation LocStart = SM->getLocForStartOfFile(MainFileID);
- std::pair<const char*, const char*> MainBuf = SM->getBufferData(MainFileID);
+ std::pair<const char*, const char*> MainBuf = SM->getBufferData(MainFileID,
+ Diags);
+ if (!MainBuf.first)
+ return;
+
const char *MainBufStart = MainBuf.first;
const char *MainBufEnd = MainBuf.second;
size_t ImportLen = strlen("import");
}
void RewriteObjC::RewriteTabs() {
- std::pair<const char*, const char*> MainBuf = SM->getBufferData(MainFileID);
+ std::pair<const char*, const char*> MainBuf = SM->getBufferData(MainFileID,
+ Diags);
+ if (!MainBuf.first)
+ return;
+
const char *MainBufStart = MainBuf.first;
const char *MainBufEnd = MainBuf.second;
}
void RewriteObjC::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) {
- std::pair<const char*, const char*> MainBuf = SM->getBufferData(MainFileID);
+ std::pair<const char*, const char*> MainBuf = SM->getBufferData(MainFileID,
+ Diags);
+ if (!MainBuf.first)
+ return;
SourceLocation LocStart = PDecl->getLocStart();
unsigned FileOffset = LocInfo.second;
// Get information about the buffer it points into.
- std::pair<const char*, const char*> BufferInfo = SM.getBufferData(FID);
+ llvm::StringRef ErrorFileName;
+ std::string ErrorStr;
+ std::pair<const char*, const char*> BufferInfo = SM.getBufferData(FID,
+ ErrorFileName,
+ ErrorStr);
const char *BufStart = BufferInfo.first;
-
+ if (!BufStart)
+ return;
+
unsigned ColNo = SM.getColumnNumber(FID, FileOffset);
unsigned CaretEndColNo
= ColNo + Lexer::MeasureTokenLength(Loc, SM, *LangOpts);
// the token this macro expanded to.
Loc = SM.getInstantiationLoc(Loc);
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
- std::pair<const char *,const char *> Buffer = SM.getBufferData(LocInfo.first);
+ llvm::StringRef FileName;
+ std::string ErrorStr;
+ std::pair<const char *,const char *> Buffer = SM.getBufferData(LocInfo.first,
+ FileName,
+ ErrorStr);
+ if (!Buffer.first)
+ return 0;
+
const char *StrData = Buffer.first+LocInfo.second;
if (isWhitespace(StrData[0]))
}
// Get the MemoryBuffer for this FID, if it fails, we fail.
- const llvm::MemoryBuffer *InputFile =
- getSourceManager().getBuffer(FID, &ErrorStr);
- if (!ErrorStr.empty())
+ const llvm::MemoryBuffer *InputFile
+ = getSourceManager().getBuffer(FID).getBuffer(getDiagnostics());
+ if (!InputFile)
return true;
EnterSourceFileWithLexer(new Lexer(FID, InputFile, *this), CurDir);
SourceManager &SourceMgr = PP.getSourceManager();
FileID LocFileID = SourceMgr.getFileID(ResultTokLoc);
- const char *ScratchBufStart = SourceMgr.getBufferData(LocFileID).first;
+ const char *ScratchBufStart
+ = SourceMgr.getBufferData(LocFileID, PP.getDiagnostics()).first;
+ if (!ScratchBufStart)
+ return false;
// Make a lexer to lex this string from. Lex just this one token.
// Make a lexer object so that we lex and expand the paste result.
// Include the whole end token in the range.
EOffset += Lexer::MeasureTokenLength(E, R.getSourceMgr(), R.getLangOpts());
+ llvm::StringRef FileName;
+ std::string ErrorStr;
+ const char *BufferStart = SM.getBufferData(FID, FileName, ErrorStr).first;
+ if (!BufferStart) {
+ // FIXME: Add a diagnostic object somewhere?
+ fprintf(stderr, "error: cannot open file '%s': %s\n",
+ FileName.str().c_str(), ErrorStr.c_str());
+ return;
+ }
+
HighlightRange(R.getEditBuffer(FID), BOffset, EOffset,
- SM.getBufferData(FID).first, StartTag, EndTag);
+ BufferStart, StartTag, EndTag);
}
/// HighlightRange - This is the same as the above method, but takes
return I->second;
I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer()));
- std::pair<const char*, const char*> MB = SourceMgr->getBufferData(FID);
+ llvm::StringRef FileName;
+ std::string ErrorStr;
+
+ std::pair<const char*, const char*> MB
+ = SourceMgr->getBufferData(FID, FileName, ErrorStr);
+ if (!MB.first) {
+ // FIXME: Add a diagnostic object somewhere?
+ fprintf(stderr, "error: cannot open file '%s': %s\n",
+ FileName.str().c_str(), ErrorStr.c_str());
+ }
+
I->second.Initialize(MB.first, MB.second);
return I->second;
std::pair<FileID, unsigned> LocInfo =
SourceMgr.getDecomposedLoc(StrTokSpellingLoc);
std::pair<const char *,const char *> Buffer =
- SourceMgr.getBufferData(LocInfo.first);
+ SourceMgr.getBufferData(LocInfo.first, Diags);
+ if (!Buffer.first)
+ return StrTokSpellingLoc;
+
const char *StrData = Buffer.first+LocInfo.second;
// Create a langops struct and enable trigraphs. This is sufficient for
std::pair<FileID, unsigned> LocInfo
= CXXUnit->getSourceManager().getDecomposedLoc(Loc);
std::pair<const char *,const char *> Buffer
- = CXXUnit->getSourceManager().getBufferData(LocInfo.first);
+ = CXXUnit->getSourceManager().getBufferData(LocInfo.first,
+ CXXUnit->getPreprocessor().getDiagnostics());
+ if (!Buffer.first)
+ return createCXString("");
return createCXString(llvm::StringRef(Buffer.first+LocInfo.second,
CXTok.int_data[2]));
// Create a lexer
std::pair<const char *,const char *> Buffer
- = SourceMgr.getBufferData(BeginLocInfo.first);
+ = SourceMgr.getBufferData(BeginLocInfo.first,
+ CXXUnit->getPreprocessor().getDiagnostics());
+ if (!Buffer.first)
+ return;
+
Lexer Lex(SourceMgr.getLocForStartOfFile(BeginLocInfo.first),
CXXUnit->getASTContext().getLangOptions(),
Buffer.first, Buffer.first + BeginLocInfo.second, Buffer.second);
CXTok.int_data[0] = CXToken_Literal;
CXTok.ptr_data = (void *)Tok.getLiteralData();
} else if (Tok.is(tok::identifier)) {
- // Lookup the identifier to determine whether we have a
+ // Lookup the identifier to determine whether we have a keyword.
std::pair<FileID, unsigned> LocInfo
= SourceMgr.getDecomposedLoc(Tok.getLocation());
- const char *StartPos
- = CXXUnit->getSourceManager().getBufferData(LocInfo.first).first +
- LocInfo.second;
+ std::pair<const char *, const char *> Buf
+ = CXXUnit->getSourceManager().getBufferData(LocInfo.first,
+ CXXUnit->getPreprocessor().getDiagnostics());
+ if (!Buf.first)
+ return;
+
+ const char *StartPos= Buf.first + LocInfo.second;
IdentifierInfo *II
= CXXUnit->getPreprocessor().LookUpIdentifierInfo(Tok, StartPos);
CXTok.int_data[0] = II->getTokenID() == tok::identifier?