Index: include/clang/Basic/SourceManager.h =================================================================== --- include/clang/Basic/SourceManager.h +++ include/clang/Basic/SourceManager.h @@ -1577,13 +1577,13 @@ return LHSLoaded; } - /// Return true if the Point is within Start and End. + /// Returns true if the point is within the start and end range. + /// + /// This check does a lexical check if the given point is located within a + /// range in one particular source file (regardless of whether the point is + /// located in the same inclusion span as the range). bool isPointWithin(SourceLocation Location, SourceLocation Start, - SourceLocation End) const { - return Location == Start || Location == End || - (isBeforeInTranslationUnit(Start, Location) && - isBeforeInTranslationUnit(Location, End)); - } + SourceLocation End) const; // Iterators over FileInfos. using fileinfo_iterator = Index: lib/Basic/SourceManager.cpp =================================================================== --- lib/Basic/SourceManager.cpp +++ lib/Basic/SourceManager.cpp @@ -2028,6 +2028,40 @@ return IBTUCacheOverflow; } +bool SourceManager::isPointWithin(SourceLocation Loc, SourceLocation Start, + SourceLocation End) const { + assert(Start.isValid() && End.isValid() && Loc.isValid() && + "Passed invalid source location!"); + + std::pair PointLoc = getDecomposedLoc(getSpellingLoc(Loc)); + + auto isOrderedInSameFile = + [&](SourceLocation RangeLoc, + llvm::function_ref Comparator) { + std::pair DecomposedRangeLoc = + getDecomposedLoc(getSpellingLoc(RangeLoc)); + // Ensure that we check for FileEntry equivalency to account for + // multiple inclusions. + if (PointLoc.first != DecomposedRangeLoc.first && + getFileEntryForID(PointLoc.first) != + getFileEntryForID(DecomposedRangeLoc.first)) + return false; + return Comparator(PointLoc.second, DecomposedRangeLoc.second); + }; + + // The point must be in the same file as the starting and end location. + // The point's offset must be >= than the starting location's offset and + // <= than the ending location's offset. + return isOrderedInSameFile(Start, + [](unsigned PointOffset, unsigned StartLocOffset) { + return PointOffset >= StartLocOffset; + }) && + isOrderedInSameFile(End, + [](unsigned PointOffset, unsigned EndLocOffset) { + return PointOffset <= EndLocOffset; + }); +} + /// Determines the order of 2 source locations in the translation unit. /// /// \returns true if LHS source location comes before RHS, false otherwise. Index: unittests/Basic/SourceManagerTest.cpp =================================================================== --- unittests/Basic/SourceManagerTest.cpp +++ unittests/Basic/SourceManagerTest.cpp @@ -377,6 +377,123 @@ EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(Macros[10].Loc, Macros[11].Loc)); } +TEST_F(SourceManagerTest, isPointWithin) { + const char *header = "int x;"; + + const char *main = "#include \n" + "#include \n"; + + std::unique_ptr HeaderBuf = + llvm::MemoryBuffer::getMemBuffer(header); + std::unique_ptr MainBuf = + llvm::MemoryBuffer::getMemBuffer(main); + SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(MainBuf))); + + const FileEntry *headerFile = + FileMgr.getVirtualFile("/test-header.h", HeaderBuf->getBufferSize(), 0); + SourceMgr.overrideFileContents(headerFile, std::move(HeaderBuf)); + + TrivialModuleLoader ModLoader; + MemoryBufferCache PCMCache; + HeaderSearch HeaderInfo(std::make_shared(), SourceMgr, + Diags, LangOpts, &*Target); + Preprocessor PP(std::make_shared(), Diags, LangOpts, + SourceMgr, PCMCache, HeaderInfo, ModLoader, + /*IILookup =*/nullptr, + /*OwnsHeaderSearch =*/false); + PP.Initialize(*Target); + + PP.EnterMainSourceFile(); + + std::vector Tokens; + while (1) { + Token tok; + PP.Lex(tok); + if (tok.is(tok::eof)) + break; + Tokens.push_back(tok); + } + + // Make sure we got the tokens that we expected. + ASSERT_EQ(6U, Tokens.size()); + + // Make sure the FileIDs are different. + SourceLocation L1 = Tokens[0].getLocation(); + SourceLocation L2 = Tokens[3].getLocation(); + ASSERT_NE(L1, L2); + + // The location of the 'int' in the first inclusion is lexically within the + // range of the 'int' in the second inclusion. + ASSERT_TRUE(SourceMgr.isPointWithin(L1, L2, L2)); + // The location of the 'int' in the second inclusion is lexically within the + // range of the 'int' in the first inclusion. + ASSERT_TRUE(SourceMgr.isPointWithin(L2, L1, L1)); + // The location of the 'x' in the second inclusion is lexically within the + // range of the 'int' in the first inclusion and ';' in the second inclusion. + ASSERT_TRUE(SourceMgr.isPointWithin(Tokens[4].getLocation(), L1, + Tokens[5].getLocation())); + // The location of the 'int' in the second inclusion is lexically outside of + // the 'x' in the first inclusion and ';' in the second inclusion. + ASSERT_FALSE(SourceMgr.isPointWithin(Tokens[3].getLocation(), + Tokens[1].getLocation(), + Tokens[5].getLocation())); +} + +TEST_F(SourceManagerTest, isPointWithinMacroSpelling) { + const char *main = "#define MYMACRO int x;\n" + "MYMACRO\n" + "MYMACRO\n"; + + std::unique_ptr MainBuf = + llvm::MemoryBuffer::getMemBuffer(main); + SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(MainBuf))); + + TrivialModuleLoader ModLoader; + MemoryBufferCache PCMCache; + HeaderSearch HeaderInfo(std::make_shared(), SourceMgr, + Diags, LangOpts, &*Target); + Preprocessor PP(std::make_shared(), Diags, LangOpts, + SourceMgr, PCMCache, HeaderInfo, ModLoader, + /*IILookup =*/nullptr, + /*OwnsHeaderSearch =*/false); + PP.Initialize(*Target); + + PP.EnterMainSourceFile(); + + std::vector Tokens; + while (1) { + Token tok; + PP.Lex(tok); + if (tok.is(tok::eof)) + break; + Tokens.push_back(tok); + } + + // Make sure we got the tokens that we expected. + ASSERT_EQ(6U, Tokens.size()); + + // Make sure the FileIDs are different. + SourceLocation L1 = Tokens[0].getLocation(); + SourceLocation L2 = Tokens[3].getLocation(); + ASSERT_NE(L1, L2); + + // The location of the 'int' in the first expansion is lexically within the + // range of the 'int' in the second expansion. + ASSERT_TRUE(SourceMgr.isPointWithin(L1, L2, L2)); + // The location of the 'int' in the second expansion is lexically within the + // range of the 'int' in the first expansion. + ASSERT_TRUE(SourceMgr.isPointWithin(L2, L1, L1)); + // The location of the 'x' in the second expansion is lexically within the + // range of the 'int' in the first expansion and ';' in the second expansion. + ASSERT_TRUE(SourceMgr.isPointWithin(Tokens[4].getLocation(), L1, + Tokens[5].getLocation())); + // The location of the 'int' in the second expansion is lexically outside of + // the 'x' in the first expansion and ';' in the second expansion. + ASSERT_FALSE(SourceMgr.isPointWithin(Tokens[3].getLocation(), + Tokens[1].getLocation(), + Tokens[5].getLocation())); +} + #endif } // anonymous namespace