Skip to content

Commit 9d3437f

Browse files
committed
[ADT] [NFC] Add StringRef::detectEOL
This change moves EOL detection out of the clang::InclusionRewriter into llvm::StringRef so that it can be easily reused elsewhere. It also adds additional explicit test cases to verify the correct and expected return results. Reviewed By: dblaikie Differential Revision: https://reviews.llvm.org/D117626
1 parent 9900aca commit 9d3437f

File tree

3 files changed

+51
-18
lines changed

3 files changed

+51
-18
lines changed

clang/lib/Frontend/Rewrite/InclusionRewriter.cpp

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -251,28 +251,12 @@ bool InclusionRewriter::IsIfAtLocationTrue(SourceLocation Loc) const {
251251
return false;
252252
}
253253

254-
/// Detect the likely line ending style of \p FromFile by examining the first
255-
/// newline found within it.
256-
static StringRef DetectEOL(const MemoryBufferRef &FromFile) {
257-
// Detect what line endings the file uses, so that added content does not mix
258-
// the style. We need to check for "\r\n" first because "\n\r" will match
259-
// "\r\n\r\n".
260-
const char *Pos = strchr(FromFile.getBufferStart(), '\n');
261-
if (!Pos)
262-
return "\n";
263-
if (Pos - 1 >= FromFile.getBufferStart() && Pos[-1] == '\r')
264-
return "\r\n";
265-
if (Pos + 1 < FromFile.getBufferEnd() && Pos[1] == '\r')
266-
return "\n\r";
267-
return "\n";
268-
}
269-
270254
void InclusionRewriter::detectMainFileEOL() {
271255
Optional<MemoryBufferRef> FromFile = *SM.getBufferOrNone(SM.getMainFileID());
272256
assert(FromFile);
273257
if (!FromFile)
274258
return; // Should never happen, but whatever.
275-
MainEOL = DetectEOL(*FromFile);
259+
MainEOL = FromFile->getBuffer().detectEOL();
276260
}
277261

278262
/// Writes out bytes from \p FromFile, starting at \p NextToWrite and ending at
@@ -378,7 +362,7 @@ void InclusionRewriter::Process(FileID FileId,
378362
Lexer RawLex(FileId, FromFile, PP.getSourceManager(), PP.getLangOpts());
379363
RawLex.SetCommentRetentionState(false);
380364

381-
StringRef LocalEOL = DetectEOL(FromFile);
365+
StringRef LocalEOL = FromFile.getBuffer().detectEOL();
382366

383367
// Per the GNU docs: "1" indicates entering a new file.
384368
if (FileId == SM.getMainFileID() || FileId == PP.getPredefinesFileID())

llvm/include/llvm/ADT/StringRef.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,25 @@ namespace llvm {
877877
return ltrim(Chars).rtrim(Chars);
878878
}
879879

880+
/// Detect the line ending style of the string.
881+
///
882+
/// If the string contains a line ending, return the line ending character
883+
/// sequence that is detected. Otherwise return '\n' for unix line endings.
884+
///
885+
/// \return - The line ending character sequence.
886+
LLVM_NODISCARD
887+
StringRef detectEOL() const {
888+
size_t Pos = find('\r');
889+
if (Pos == npos) {
890+
// If there is no carriage return, assume unix
891+
return "\n";
892+
}
893+
if (Pos + 1 < Length && Data[Pos + 1] == '\n')
894+
return "\r\n"; // Windows
895+
if (Pos > 0 && Data[Pos - 1] == '\n')
896+
return "\n\r"; // You monster!
897+
return "\r"; // Classic Mac
898+
}
880899
/// @}
881900
};
882901

llvm/unittests/ADT/StringRefTest.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,36 @@ TEST(StringRefTest, GTestPrinter) {
11091109
EXPECT_EQ(R"("foo")", ::testing::PrintToString(StringRef("foo")));
11101110
}
11111111

1112+
TEST(StringRefTest, LFLineEnding) {
1113+
constexpr StringRef Cases[] = {"\nDoggo\nPupper", "Floofer\n", "Woofer"};
1114+
EXPECT_EQ(StringRef("\n"), Cases[0].detectEOL());
1115+
EXPECT_EQ(StringRef("\n"), Cases[1].detectEOL());
1116+
EXPECT_EQ(StringRef("\n"), Cases[2].detectEOL());
1117+
}
1118+
1119+
TEST(StringRefTest, CRLineEnding) {
1120+
constexpr StringRef Cases[] = {"\rDoggo\rPupper", "Floofer\r", "Woo\rfer\n"};
1121+
EXPECT_EQ(StringRef("\r"), Cases[0].detectEOL());
1122+
EXPECT_EQ(StringRef("\r"), Cases[1].detectEOL());
1123+
EXPECT_EQ(StringRef("\r"), Cases[2].detectEOL());
1124+
}
1125+
1126+
TEST(StringRefTest, CRLFLineEnding) {
1127+
constexpr StringRef Cases[] = {"\r\nDoggo\r\nPupper", "Floofer\r\n",
1128+
"Woofer\r\nSubWoofer\n"};
1129+
EXPECT_EQ(StringRef("\r\n"), Cases[0].detectEOL());
1130+
EXPECT_EQ(StringRef("\r\n"), Cases[1].detectEOL());
1131+
EXPECT_EQ(StringRef("\r\n"), Cases[2].detectEOL());
1132+
}
1133+
1134+
TEST(StringRefTest, LFCRLineEnding) {
1135+
constexpr StringRef Cases[] = {"\n\rDoggo\n\rPupper", "Floofer\n\r",
1136+
"Woofer\n\rSubWoofer\n"};
1137+
EXPECT_EQ(StringRef("\n\r"), Cases[0].detectEOL());
1138+
EXPECT_EQ(StringRef("\n\r"), Cases[1].detectEOL());
1139+
EXPECT_EQ(StringRef("\n\r"), Cases[2].detectEOL());
1140+
}
1141+
11121142
static_assert(std::is_trivially_copyable<StringRef>::value,
11131143
"trivially copyable");
11141144

0 commit comments

Comments
 (0)