autoupdate.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  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 "testing/file_test/autoupdate.h"
  5. #include <fstream>
  6. #include "absl/strings/string_view.h"
  7. #include "common/check.h"
  8. #include "common/ostream.h"
  9. #include "common/raw_string_ostream.h"
  10. #include "llvm/ADT/DenseMap.h"
  11. #include "llvm/ADT/StringExtras.h"
  12. #include "llvm/Support/FormatVariadic.h"
  13. namespace Carbon::Testing {
  14. // Converts a matched line number to an int, trimming whitespace. Returns 0 if
  15. // there is no line number, to assist early placement.
  16. static auto ParseLineNumber(absl::string_view matched_line_number) -> int {
  17. llvm::StringRef trimmed = matched_line_number;
  18. trimmed = trimmed.trim();
  19. if (trimmed.empty()) {
  20. return 0;
  21. }
  22. // NOLINTNEXTLINE(google-runtime-int): API requirement.
  23. long long val;
  24. CARBON_CHECK(!llvm::getAsSignedInteger(trimmed, 10, val), "{0}",
  25. matched_line_number);
  26. return val;
  27. }
  28. FileTestAutoupdater::FileAndLineNumber::FileAndLineNumber(
  29. const LineNumberReplacement* replacement, int file_number,
  30. absl::string_view line_number)
  31. : replacement(replacement),
  32. file_number(file_number),
  33. line_number(ParseLineNumber(line_number)) {}
  34. auto FileTestAutoupdater::CheckLine::RemapLineNumbers(
  35. const llvm::DenseMap<std::pair<int, int>, int>& output_line_remap,
  36. const llvm::SmallVector<int>& new_last_line_numbers) -> void {
  37. // Only need to do remappings when there's a line number replacement.
  38. if (!replacement_) {
  39. return;
  40. }
  41. // Use a cursor for the line so that we can't keep matching the same
  42. // content, which may occur when we keep a literal line number.
  43. int line_offset = 0;
  44. while (true) {
  45. // Rebuild the cursor each time because we're editing the line, which
  46. // could cause a reallocation.
  47. absl::string_view line_cursor = line_;
  48. line_cursor.remove_prefix(line_offset);
  49. // Look for a line number to replace. There may be multiple, so we
  50. // repeatedly check.
  51. absl::string_view matched_line_number;
  52. if (replacement_->has_file) {
  53. RE2::PartialMatch(line_cursor, *replacement_->re, nullptr,
  54. &matched_line_number);
  55. } else {
  56. RE2::PartialMatch(line_cursor, *replacement_->re, &matched_line_number);
  57. }
  58. if (matched_line_number.empty()) {
  59. return;
  60. }
  61. // Update the cursor offset from the match.
  62. line_offset = matched_line_number.begin() - line_.c_str();
  63. // Calculate the new line number (possibly with new CHECK lines added, or
  64. // some removed).
  65. int old_line_number = ParseLineNumber(matched_line_number);
  66. int new_line_number = -1;
  67. if (auto remapped =
  68. output_line_remap.find({file_number(), old_line_number});
  69. remapped != output_line_remap.end()) {
  70. // Map old non-check lines to their new line numbers.
  71. new_line_number = remapped->second;
  72. } else {
  73. // We assume unmapped references point to the end-of-file.
  74. new_line_number = new_last_line_numbers[file_number()];
  75. }
  76. std::string replacement;
  77. if (output_file_number_ == file_number()) {
  78. int offset = new_line_number - output_line_number_;
  79. // Update the line offset in the CHECK line.
  80. const char* offset_prefix = offset < 0 ? "" : "+";
  81. replacement = llvm::formatv(
  82. replacement_->line_formatv.c_str(),
  83. llvm::formatv("[[@LINE{0}{1}]]", offset_prefix, offset));
  84. } else {
  85. // If the CHECK was written to a different file from the file that it
  86. // refers to, leave behind an absolute line reference rather than a
  87. // cross-file offset.
  88. replacement =
  89. llvm::formatv(replacement_->line_formatv.c_str(), new_line_number);
  90. }
  91. line_.replace(matched_line_number.data() - line_.data(),
  92. matched_line_number.size(), replacement);
  93. }
  94. }
  95. auto FileTestAutoupdater::GetFileAndLineNumber(
  96. const llvm::DenseMap<llvm::StringRef, int>& file_to_number_map,
  97. int default_file_number, const std::string& check_line)
  98. -> FileAndLineNumber {
  99. for (const auto& replacement : line_number_replacements_) {
  100. if (replacement.has_file) {
  101. absl::string_view filename;
  102. absl::string_view line_number;
  103. if (RE2::PartialMatch(check_line, *replacement.re, &filename,
  104. &line_number)) {
  105. if (auto it = file_to_number_map.find(filename);
  106. it != file_to_number_map.end()) {
  107. return FileAndLineNumber(&replacement, it->second, line_number);
  108. } else {
  109. return FileAndLineNumber(default_file_number);
  110. }
  111. }
  112. } else {
  113. // There's no file association, so we only look at the line, and assume
  114. // it refers to the default file.
  115. absl::string_view line_number;
  116. if (RE2::PartialMatch(check_line, *replacement.re, &line_number)) {
  117. return FileAndLineNumber(&replacement, default_file_number,
  118. line_number);
  119. }
  120. }
  121. }
  122. return FileAndLineNumber(default_file_number);
  123. }
  124. auto FileTestAutoupdater::BuildCheckLines(llvm::StringRef output,
  125. const char* label) -> CheckLines {
  126. if (output.empty()) {
  127. return CheckLines({});
  128. }
  129. // Prepare to look for filenames in lines.
  130. llvm::DenseMap<llvm::StringRef, int> file_to_number_map;
  131. for (auto [number, name] : llvm::enumerate(filenames_)) {
  132. file_to_number_map.insert({name, number});
  133. }
  134. // %t substitution means we may see TEST_TMPDIR in output.
  135. char* tmpdir_env = getenv("TEST_TMPDIR");
  136. CARBON_CHECK(tmpdir_env != nullptr);
  137. llvm::StringRef tmpdir = tmpdir_env;
  138. llvm::SmallVector<llvm::StringRef> lines(llvm::split(output, '\n'));
  139. // It's typical that output ends with a newline, but we don't want to add a
  140. // blank CHECK for it.
  141. if (lines.back().empty()) {
  142. lines.pop_back();
  143. }
  144. // `{{` and `[[` are escaped as a regex matcher.
  145. static RE2 double_brace_re(R"(\{\{)");
  146. static RE2 double_square_bracket_re(R"(\[\[)");
  147. // End-of-line whitespace is replaced with a regex matcher to make it visible.
  148. static RE2 end_of_line_whitespace_re(R"((\s+)$)");
  149. // The default file number for when no specific file is found.
  150. int default_file_number = 0;
  151. llvm::SmallVector<CheckLine> check_lines;
  152. for (const auto& line : lines) {
  153. // This code is relatively hot in our testing, and because when testing it
  154. // isn't run with an optimizer we benefit from making it use simple
  155. // constructs. For this reason, we avoid `llvm::formatv` and similar tools.
  156. std::string check_line;
  157. check_line.reserve(line.size() + strlen(label) + strlen("// CHECK:: "));
  158. check_line.append("// CHECK:");
  159. check_line.append(label);
  160. check_line.append(":");
  161. if (!line.empty()) {
  162. check_line.append(" ");
  163. check_line.append(line);
  164. }
  165. RE2::Replace(&check_line, double_brace_re, R"({{\\{\\{}})");
  166. RE2::Replace(&check_line, double_square_bracket_re, R"({{\\[\\[}})");
  167. RE2::Replace(&check_line, end_of_line_whitespace_re, R"({{\1}})");
  168. // Ignore TEST_TMPDIR in output.
  169. if (auto pos = check_line.find(tmpdir); pos != std::string::npos) {
  170. check_line.replace(pos, tmpdir.size(), "{{.+}}");
  171. }
  172. do_extra_check_replacements_(check_line);
  173. if (default_file_re_) {
  174. absl::string_view filename;
  175. if (RE2::PartialMatch(line, *default_file_re_, &filename)) {
  176. auto it = file_to_number_map.find(filename);
  177. CARBON_CHECK(it != file_to_number_map.end(),
  178. "default_file_re had unexpected match in '{0}' (`{1}`)",
  179. line, default_file_re_->pattern());
  180. default_file_number = it->second;
  181. }
  182. }
  183. auto file_and_line = GetFileAndLineNumber(file_to_number_map,
  184. default_file_number, check_line);
  185. check_lines.push_back(CheckLine(file_and_line, check_line));
  186. }
  187. return CheckLines(check_lines);
  188. }
  189. auto FileTestAutoupdater::AddRemappedNonCheckLine() -> void {
  190. new_lines_.push_back(non_check_line_);
  191. CARBON_CHECK(output_line_remap_
  192. .insert({{non_check_line_->file_number(),
  193. non_check_line_->line_number()},
  194. ++output_line_number_})
  195. .second);
  196. }
  197. auto FileTestAutoupdater::AddTips() -> void {
  198. CARBON_CHECK(tips_.empty(), "Should only add tips once");
  199. tips_.reserve(4);
  200. // This puts commands on a single line so that they can be easily copied.
  201. tips_.emplace_back("// TIP: To test this file alone, run:");
  202. tips_.emplace_back("// TIP: " + test_command_);
  203. tips_.emplace_back("// TIP: To dump output, run:");
  204. tips_.emplace_back("// TIP: " + dump_command_);
  205. for (const auto& tip : tips_) {
  206. new_lines_.push_back(&tip);
  207. ++output_line_number_;
  208. }
  209. }
  210. auto FileTestAutoupdater::ShouldAddCheckLine(const CheckLines& check_lines,
  211. bool to_file_end) const -> bool {
  212. return !autoupdate_split_ && check_lines.cursor != check_lines.lines.end() &&
  213. (check_lines.cursor->file_number() < output_file_number_ ||
  214. (check_lines.cursor->file_number() == output_file_number_ &&
  215. (to_file_end || check_lines.cursor->line_number() <=
  216. non_check_line_->line_number())));
  217. }
  218. auto FileTestAutoupdater::AddCheckLines(CheckLines& check_lines,
  219. bool to_file_end) -> void {
  220. for (; ShouldAddCheckLine(check_lines, to_file_end); ++check_lines.cursor) {
  221. new_lines_.push_back(check_lines.cursor);
  222. check_lines.cursor->SetOutputLine(
  223. to_file_end ? "" : non_check_line_->indent(), output_file_number_,
  224. ++output_line_number_);
  225. }
  226. }
  227. auto FileTestAutoupdater::FinishFile(bool is_last_file) -> void {
  228. bool include_stdout = any_attached_stdout_lines_ || is_last_file;
  229. // At the end of each file, print any remaining lines which are associated
  230. // with the file.
  231. if (ShouldAddCheckLine(stderr_, /*to_file_end=*/true) ||
  232. (include_stdout && ShouldAddCheckLine(stdout_, /*to_file_end=*/true))) {
  233. // Ensure there's a blank line before any trailing CHECKs.
  234. if (!new_lines_.empty() && !new_lines_.back()->is_blank()) {
  235. new_lines_.push_back(&blank_line_);
  236. ++output_line_number_;
  237. }
  238. AddCheckLines(stderr_, /*to_file_end=*/true);
  239. if (include_stdout) {
  240. AddCheckLines(stdout_, /*to_file_end=*/true);
  241. }
  242. }
  243. new_last_line_numbers_.push_back(output_line_number_);
  244. }
  245. auto FileTestAutoupdater::StartSplitFile() -> void {
  246. // Advance the file.
  247. ++output_file_number_;
  248. output_line_number_ = 0;
  249. CARBON_CHECK(output_file_number_ == non_check_line_->file_number(),
  250. "Non-sequential file: {0}", non_check_line_->file_number());
  251. // Each following file has precisely one split line.
  252. CARBON_CHECK(non_check_line_->line_number() < 1,
  253. "Expected a split line, got {0}", *non_check_line_);
  254. // The split line is ignored when calculating line counts.
  255. new_lines_.push_back(non_check_line_);
  256. // Add any file-specific but line-unattached STDOUT messages here. STDERR is
  257. // handled through the main loop because it's before the next line.
  258. if (any_attached_stdout_lines_) {
  259. AddCheckLines(stdout_, /*to_file_end=*/false);
  260. }
  261. ++non_check_line_;
  262. }
  263. auto FileTestAutoupdater::Run(bool dry_run) -> bool {
  264. // Print everything until the autoupdate line.
  265. while (non_check_line_->line_number() != autoupdate_line_number_) {
  266. CARBON_CHECK(non_check_line_ != non_check_lines_.end() &&
  267. non_check_line_->file_number() == 0,
  268. "Missed autoupdate?");
  269. AddRemappedNonCheckLine();
  270. ++non_check_line_;
  271. }
  272. // Add the AUTOUPDATE line along with any early STDERR lines, so that the
  273. // initial batch of CHECK lines have STDERR before STDOUT. This also ensures
  274. // we don't insert a blank line before the STDERR checks if there are no more
  275. // lines after AUTOUPDATE.
  276. AddRemappedNonCheckLine();
  277. AddTips();
  278. if (!autoupdate_split_) {
  279. AddCheckLines(stderr_, /*to_file_end=*/false);
  280. if (any_attached_stdout_lines_) {
  281. AddCheckLines(stdout_, /*to_file_end=*/false);
  282. }
  283. }
  284. ++non_check_line_;
  285. // Loop through remaining content.
  286. while (non_check_line_ != non_check_lines_.end()) {
  287. if (output_file_number_ < non_check_line_->file_number()) {
  288. FinishFile(/*is_last_file=*/false);
  289. StartSplitFile();
  290. if (autoupdate_split_ &&
  291. output_file_number_ == static_cast<int>(filenames_.size())) {
  292. break;
  293. }
  294. continue;
  295. }
  296. // STDERR check lines are placed before the line they refer to, or as
  297. // early as possible if they don't refer to a line. Include all STDERR
  298. // lines until we find one that wants to go later in the file.
  299. AddCheckLines(stderr_, /*to_file_end=*/false);
  300. AddRemappedNonCheckLine();
  301. // STDOUT check lines are placed after the line they refer to, or at the
  302. // end of the file if none of them refers to a line.
  303. if (any_attached_stdout_lines_) {
  304. AddCheckLines(stdout_, /*to_file_end=*/false);
  305. }
  306. ++non_check_line_;
  307. }
  308. // When autoupdate_split_ was true, this will result in all check lines (and
  309. // only check lines) being added to the split by FinishFile. We don't use
  310. // autoupdate_split_ past this point.
  311. autoupdate_split_ = false;
  312. FinishFile(/*is_last_file=*/true);
  313. for (auto& check_line : stdout_.lines) {
  314. check_line.RemapLineNumbers(output_line_remap_, new_last_line_numbers_);
  315. }
  316. for (auto& check_line : stderr_.lines) {
  317. check_line.RemapLineNumbers(output_line_remap_, new_last_line_numbers_);
  318. }
  319. // Generate the autoupdated file.
  320. RawStringOstream new_content_stream;
  321. for (const auto& line : new_lines_) {
  322. new_content_stream << *line << '\n';
  323. }
  324. std::string new_content = new_content_stream.TakeStr();
  325. // Update the file on disk if needed.
  326. if (new_content == input_content_) {
  327. return false;
  328. }
  329. if (!dry_run) {
  330. std::ofstream out(file_test_path_);
  331. out << new_content;
  332. }
  333. return true;
  334. }
  335. } // namespace Carbon::Testing