Index: include/llvm/Support/SourceMgr.h =================================================================== --- include/llvm/Support/SourceMgr.h +++ include/llvm/Support/SourceMgr.h @@ -57,6 +57,13 @@ /// The memory buffer for the file. std::unique_ptr Buffer; + /// Vector of pointers into Buffer that point to line-endings (lazily + /// populated). Once populated, the pointer to the '\n' that marks the + /// end of line number N from [1..] is located at EOLBytes[N-1]. Since + /// these pointers are in sorted (ascending) order, they can be + /// binary-searched for any given SMLoc. + mutable std::vector EOLBytes; + /// This is the location of the parent include, or null if at the top level. SMLoc IncludeLoc; }; @@ -67,10 +74,6 @@ // This is the list of directories we should search for include files in. std::vector IncludeDirectories; - /// This is a cache for line number queries, its implementation is really - /// private to SourceMgr.cpp. - mutable void *LineNoCache = nullptr; - DiagHandlerTy DiagHandler = nullptr; void *DiagContext = nullptr; @@ -80,7 +83,7 @@ SourceMgr() = default; SourceMgr(const SourceMgr &) = delete; SourceMgr &operator=(const SourceMgr &) = delete; - ~SourceMgr(); + ~SourceMgr() = default; void setIncludeDirs(const std::vector &Dirs) { IncludeDirectories = Dirs; Index: lib/Support/SourceMgr.cpp =================================================================== --- lib/Support/SourceMgr.cpp +++ lib/Support/SourceMgr.cpp @@ -36,24 +36,6 @@ static const size_t TabStop = 8; -namespace { - - struct LineNoCacheTy { - const char *LastQuery; - unsigned LastQueryBufferID; - unsigned LineNoOfQuery; - }; - -} // end anonymous namespace - -static LineNoCacheTy *getCache(void *Ptr) { - return (LineNoCacheTy*)Ptr; -} - -SourceMgr::~SourceMgr() { - delete getCache(LineNoCache); -} - unsigned SourceMgr::AddIncludeFile(const std::string &Filename, SMLoc IncludeLoc, std::string &IncludedFile) { @@ -91,40 +73,28 @@ BufferID = FindBufferContainingLoc(Loc); assert(BufferID && "Invalid Location!"); - const MemoryBuffer *Buff = getMemoryBuffer(BufferID); - - // Count the number of \n's between the start of the file and the specified - // location. - unsigned LineNo = 1; - - const char *BufStart = Buff->getBufferStart(); - const char *Ptr = BufStart; - - // If we have a line number cache, and if the query is to a later point in the - // same file, start searching from the last query location. This optimizes - // for the case when multiple diagnostics come out of one file in order. - if (LineNoCacheTy *Cache = getCache(LineNoCache)) - if (Cache->LastQueryBufferID == BufferID && - Cache->LastQuery <= Loc.getPointer()) { - Ptr = Cache->LastQuery; - LineNo = Cache->LineNoOfQuery; + auto &SB = getBufferInfo(BufferID); + const char *BufStart = SB.Buffer->getBufferStart(); + const char *Ptr = Loc.getPointer(); + + // Ensure EOLBytes is populated with pointers to all the '\n' bytes. + if (SB.EOLBytes.empty()) { + const char *BufEnd = SB.Buffer->getBufferEnd(); + for (const char *C = BufStart; C != BufEnd; ++C) { + if (*C == '\n') { + SB.EOLBytes.push_back(C); + } } + } - // Scan for the location being queried, keeping track of the number of lines - // we see. - for (; SMLoc::getFromPointer(Ptr) != Loc; ++Ptr) - if (*Ptr == '\n') ++LineNo; + // std::lower_bound returns the first EOL pointer that's not-less-than + // Ptr, meaning the EOL that _ends the line_ that Ptr is on (including if + // Ptr points to the EOL itself). If there's no such EOL, returns end(). + auto EOL = std::lower_bound(SB.EOLBytes.begin(), SB.EOLBytes.end(), Ptr); - // Allocate the line number cache if it doesn't exist. - if (!LineNoCache) - LineNoCache = new LineNoCacheTy(); + // Lines count from 1, so add 1 to the distance from the 0th line. + unsigned LineNo = 1 + (EOL - SB.EOLBytes.begin()); - // Update the line # cache. - LineNoCacheTy &Cache = *getCache(LineNoCache); - Cache.LastQueryBufferID = BufferID; - Cache.LastQuery = Ptr; - Cache.LineNoOfQuery = LineNo; - size_t NewlineOffs = StringRef(BufStart, Ptr-BufStart).find_last_of("\n\r"); if (NewlineOffs == StringRef::npos) NewlineOffs = ~(size_t)0; return std::make_pair(LineNo, Ptr-BufStart-NewlineOffs); Index: unittests/Support/SourceMgrTest.cpp =================================================================== --- unittests/Support/SourceMgrTest.cpp +++ unittests/Support/SourceMgrTest.cpp @@ -107,6 +107,86 @@ Output); } +TEST_F(SourceMgrTest, LocationAtEmptyBuffer) { + setMainBuffer("", "file.in"); + printMessage(getLoc(0), SourceMgr::DK_Error, "message", None, None); + + EXPECT_EQ("file.in:1:1: error: message\n" + "\n" + "^\n", + Output); +} + +TEST_F(SourceMgrTest, LocationJustOnSoleNewline) { + setMainBuffer("\n", "file.in"); + printMessage(getLoc(0), SourceMgr::DK_Error, "message", None, None); + + EXPECT_EQ("file.in:1:1: error: message\n" + "\n" + "^\n", + Output); +} + +TEST_F(SourceMgrTest, LocationJustAfterSoleNewline) { + setMainBuffer("\n", "file.in"); + printMessage(getLoc(1), SourceMgr::DK_Error, "message", None, None); + + EXPECT_EQ("file.in:2:1: error: message\n" + "\n" + "^\n", + Output); +} + +TEST_F(SourceMgrTest, LocationOnFirstLineOfMultiline) { + setMainBuffer("1234\n6789\n", "file.in"); + printMessage(getLoc(3), SourceMgr::DK_Error, "message", None, None); + + EXPECT_EQ("file.in:1:4: error: message\n" + "1234\n" + " ^\n", + Output); +} + +TEST_F(SourceMgrTest, LocationOnEOLOfFirstLineOfMultiline) { + setMainBuffer("1234\n6789\n", "file.in"); + printMessage(getLoc(4), SourceMgr::DK_Error, "message", None, None); + + EXPECT_EQ("file.in:1:5: error: message\n" + "1234\n" + " ^\n", + Output); +} + +TEST_F(SourceMgrTest, LocationOnSecondLineOfMultiline) { + setMainBuffer("1234\n6789\n", "file.in"); + printMessage(getLoc(5), SourceMgr::DK_Error, "message", None, None); + + EXPECT_EQ("file.in:2:1: error: message\n" + "6789\n" + "^\n", + Output); +} + +TEST_F(SourceMgrTest, LocationOnSecondLineOfMultilineNoSecondEOL) { + setMainBuffer("1234\n6789", "file.in"); + printMessage(getLoc(5), SourceMgr::DK_Error, "message", None, None); + + EXPECT_EQ("file.in:2:1: error: message\n" + "6789\n" + "^\n", + Output); +} + +TEST_F(SourceMgrTest, LocationOnEOLOfSecondSecondLineOfMultiline) { + setMainBuffer("1234\n6789\n", "file.in"); + printMessage(getLoc(9), SourceMgr::DK_Error, "message", None, None); + + EXPECT_EQ("file.in:2:5: error: message\n" + "6789\n" + " ^\n", + Output); +} + TEST_F(SourceMgrTest, BasicRange) { setMainBuffer("aaa bbb\nccc ddd\n", "file.in"); printMessage(getLoc(4), SourceMgr::DK_Error, "message", getRange(4, 3), None);