string_literal_test.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/lex/string_literal.h"
  5. #include <gmock/gmock.h>
  6. #include <gtest/gtest.h>
  7. #include "common/check.h"
  8. #include "toolchain/diagnostics/diagnostic_emitter.h"
  9. #include "toolchain/lex/test_helpers.h"
  10. namespace Carbon::Lex {
  11. namespace {
  12. class StringLiteralTest : public ::testing::Test {
  13. protected:
  14. StringLiteralTest() : error_tracker(ConsoleDiagnosticConsumer()) {}
  15. auto Lex(llvm::StringRef text) -> StringLiteral {
  16. std::optional<StringLiteral> result = StringLiteral::Lex(text);
  17. CARBON_CHECK(result);
  18. EXPECT_EQ(result->text(), text);
  19. return *result;
  20. }
  21. auto Parse(llvm::StringRef text) -> llvm::StringRef {
  22. StringLiteral token = Lex(text);
  23. Testing::SingleTokenDiagnosticTranslator translator(text);
  24. DiagnosticEmitter<const char*> emitter(translator, error_tracker);
  25. return token.ComputeValue(allocator, emitter);
  26. }
  27. llvm::BumpPtrAllocator allocator;
  28. ErrorTrackingDiagnosticConsumer error_tracker;
  29. };
  30. TEST_F(StringLiteralTest, StringLiteralBounds) {
  31. llvm::StringLiteral valid[] = {
  32. R"("")",
  33. R"('''
  34. ''')",
  35. R"('''
  36. "foo"
  37. ''')",
  38. // Lex """-delimited block string literals for error recovery.
  39. R"("""
  40. """)",
  41. R"("""
  42. "foo"
  43. """)",
  44. // Escaped terminators don't end the string.
  45. R"("\"")",
  46. R"("\\")",
  47. R"("\\\"")",
  48. R"('''
  49. \'''
  50. ''')",
  51. R"('''
  52. '\''
  53. ''')",
  54. R"('''
  55. ''\'
  56. ''')",
  57. R"('''
  58. ''\
  59. ''')",
  60. R"(#'''
  61. '''\#n
  62. '''#)",
  63. // Only a matching number of '#'s terminates the string.
  64. R"(#""#)",
  65. R"(#"xyz"foo"#)",
  66. R"(##"xyz"#foo"##)",
  67. R"(#"\""#)",
  68. // Escape sequences likewise require a matching number of '#'s.
  69. R"(#"\#"#"#)",
  70. R"(#"\"#)",
  71. R"(#'''
  72. \#'''#
  73. '''#)",
  74. // #"""# does not start a multiline string literal.
  75. R"(#"""#)",
  76. R"(##"""##)",
  77. };
  78. for (llvm::StringLiteral test : valid) {
  79. SCOPED_TRACE(test);
  80. std::optional<StringLiteral> result = StringLiteral::Lex(test);
  81. EXPECT_TRUE(result.has_value());
  82. if (result) {
  83. EXPECT_EQ(result->text(), test);
  84. }
  85. }
  86. llvm::StringLiteral invalid[] = {
  87. // clang-format off
  88. R"(")",
  89. R"("\)",
  90. R"("\")",
  91. R"("\\)",
  92. R"("\\\")",
  93. "'''\n",
  94. "'''\n'",
  95. "'''\n''",
  96. "#'''\n'''",
  97. R"(" \
  98. ")",
  99. // clang-format on
  100. };
  101. for (llvm::StringLiteral test : invalid) {
  102. SCOPED_TRACE(test);
  103. std::optional<StringLiteral> result = StringLiteral::Lex(test);
  104. EXPECT_TRUE(result.has_value());
  105. if (result) {
  106. EXPECT_FALSE(result->is_terminated());
  107. }
  108. }
  109. }
  110. TEST_F(StringLiteralTest, StringLiteralContents) {
  111. std::pair<llvm::StringLiteral, llvm::StringLiteral> testcases[] = {
  112. // Empty strings.
  113. {R"("")", ""},
  114. {R"(
  115. '''
  116. '''
  117. )",
  118. ""},
  119. // Nearly-empty strings.
  120. {R"(
  121. '''
  122. '''
  123. )",
  124. "\n"},
  125. // Lines containing only whitespace are treated as empty even if they
  126. // contain tabs.
  127. {"'''\n\t \t\n'''", "\n"},
  128. // Indent removal.
  129. {R"(
  130. '''file type indicator
  131. indented contents \
  132. '''
  133. )",
  134. " indented contents "},
  135. // Removal of tabs in indent and suffix.
  136. {"'''\n \t hello \t \n \t '''", " hello\n"},
  137. {R"(
  138. '''
  139. hello
  140. world
  141. end of test
  142. '''
  143. )",
  144. " hello\nworld\n\n end of test\n"},
  145. // Escape sequences.
  146. {R"(
  147. "\x14,\u{1234},\u{00000010},\n,\r,\t,\0,\",\',\\"
  148. )",
  149. llvm::StringLiteral::withInnerNUL(
  150. "\x14,\xE1\x88\xB4,\x10,\x0A,\x0D,\x09,\x00,\x22,\x27,\x5C")},
  151. {R"(
  152. "\0A\x1234"
  153. )",
  154. llvm::StringLiteral::withInnerNUL("\0A\x12"
  155. "34")},
  156. {R"(
  157. "\u{D7FF},\u{E000},\u{10FFFF}"
  158. )",
  159. "\xED\x9F\xBF,\xEE\x80\x80,\xF4\x8F\xBF\xBF"},
  160. // Escape sequences in 'raw' strings.
  161. {R"(
  162. #"\#x00,\#xFF,\#u{56789},\#u{ABCD},\#u{00000000000000000EF}"#
  163. )",
  164. llvm::StringLiteral::withInnerNUL(
  165. "\x00,\xFF,\xF1\x96\x9E\x89,\xEA\xAF\x8D,\xC3\xAF")},
  166. {R"(
  167. ##"\n,\#n,\##n,\##\##n,\##\###n"##
  168. )",
  169. "\\n,\\#n,\n,\\##n,\\###n"},
  170. // Trailing whitespace handling.
  171. {"'''\n Hello \\\n World \t \n Bye! \\\n '''",
  172. "Hello World\nBye! "},
  173. {"'''\n\\t\n'''", "\t\n"},
  174. {"'''\n\\t \n'''", "\t\n"},
  175. };
  176. for (auto [test, expected] : testcases) {
  177. error_tracker.Reset();
  178. auto value = Parse(test.trim());
  179. EXPECT_FALSE(error_tracker.seen_error()) << "`" << test << "`";
  180. EXPECT_EQ(value, expected);
  181. }
  182. }
  183. TEST_F(StringLiteralTest, DoubleQuotedMultiLineLiteral) {
  184. // For error recovery, """-delimited literals are lexed, but rejected.
  185. std::pair<llvm::StringLiteral, llvm::StringLiteral> testcases[] = {
  186. {R"(
  187. """
  188. '''
  189. """
  190. )",
  191. "'''\n"},
  192. {R"(
  193. #"""
  194. \#tx
  195. """#
  196. )",
  197. "\tx\n"},
  198. {R"(
  199. """abcxyz
  200. hello\
  201. """
  202. )",
  203. "hello"},
  204. };
  205. for (auto [test, contents] : testcases) {
  206. error_tracker.Reset();
  207. auto value = Parse(test.trim());
  208. EXPECT_TRUE(error_tracker.seen_error()) << "`" << test << "`";
  209. EXPECT_EQ(value, contents);
  210. }
  211. }
  212. TEST_F(StringLiteralTest, StringLiteralBadIndent) {
  213. std::pair<llvm::StringLiteral, llvm::StringLiteral> testcases[] = {
  214. // Indent doesn't match the last line.
  215. {"'''\n \tx\n '''", "x\n"},
  216. {"'''\n x\n '''", "x\n"},
  217. {"'''\n x\n\t'''", "x\n"},
  218. {"'''\n ok\n bad\n '''", "ok\nbad\n"},
  219. {"'''\n bad\n ok\n '''", "bad\nok\n"},
  220. {"'''\n escaped,\\\n bad\n '''", "escaped,bad\n"},
  221. // Indent on last line is followed by text.
  222. {"'''\n x\n x'''", "x\nx"},
  223. {"'''\n x\n x'''", " x\nx"},
  224. {"'''\n x\n x'''", "x\nx"},
  225. };
  226. for (auto [test, contents] : testcases) {
  227. error_tracker.Reset();
  228. auto value = Parse(test);
  229. EXPECT_TRUE(error_tracker.seen_error()) << "`" << test << "`";
  230. EXPECT_EQ(value, contents);
  231. }
  232. }
  233. TEST_F(StringLiteralTest, StringLiteralBadEscapeSequence) {
  234. llvm::StringLiteral testcases[] = {
  235. R"("\a")",
  236. R"("\b")",
  237. R"("\e")",
  238. R"("\f")",
  239. R"("\v")",
  240. R"("\?")",
  241. R"("\1")",
  242. R"("\9")",
  243. // \0 can't be followed by a decimal digit.
  244. R"("\01")",
  245. R"("\09")",
  246. // \x requires two (uppercase) hexadecimal digits.
  247. R"("\x")",
  248. R"("\x0")",
  249. R"("\x0G")",
  250. R"("\xab")",
  251. R"("\x\n")",
  252. R"("\x\"")",
  253. // \u requires a braced list of one or more hexadecimal digits.
  254. R"("\u")",
  255. R"("\u?")",
  256. R"("\u\"")",
  257. R"("\u{")",
  258. R"("\u{}")",
  259. R"("\u{A")",
  260. R"("\u{G}")",
  261. R"("\u{0000012323127z}")",
  262. R"("\u{-3}")",
  263. // \u must specify a non-surrogate code point.
  264. R"("\u{110000}")",
  265. R"("\u{000000000000000000000000000000000110000}")",
  266. R"("\u{D800}")",
  267. R"("\u{DFFF}")",
  268. };
  269. for (llvm::StringLiteral test : testcases) {
  270. error_tracker.Reset();
  271. Parse(test);
  272. EXPECT_TRUE(error_tracker.seen_error()) << "`" << test << "`";
  273. // TODO: Test value produced by error recovery.
  274. }
  275. }
  276. TEST_F(StringLiteralTest, TabInString) {
  277. auto value = Parse("\"x\ty\"");
  278. EXPECT_TRUE(error_tracker.seen_error());
  279. EXPECT_EQ(value, "x\ty");
  280. }
  281. TEST_F(StringLiteralTest, TabAtEndOfString) {
  282. auto value = Parse("\"\t\t\t\"");
  283. EXPECT_TRUE(error_tracker.seen_error());
  284. EXPECT_EQ(value, "\t\t\t");
  285. }
  286. TEST_F(StringLiteralTest, TabInBlockString) {
  287. auto value = Parse("'''\nx\ty\n'''");
  288. EXPECT_TRUE(error_tracker.seen_error());
  289. EXPECT_EQ(value, "x\ty\n");
  290. }
  291. TEST_F(StringLiteralTest, UnicodeTooManyDigits) {
  292. std::string text = "u{";
  293. text.append(10000, '9');
  294. text.append("}");
  295. auto value = Parse("\"\\" + text + "\"");
  296. EXPECT_TRUE(error_tracker.seen_error());
  297. EXPECT_EQ(value, text);
  298. }
  299. } // namespace
  300. } // namespace Carbon::Lex