autoupdate.h 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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. #ifndef CARBON_TESTING_FILE_TEST_AUTOUPDATE_H_
  5. #define CARBON_TESTING_FILE_TEST_AUTOUPDATE_H_
  6. #include <filesystem>
  7. #include <utility>
  8. #include "common/check.h"
  9. #include "llvm/ADT/DenseMap.h"
  10. #include "llvm/ADT/SmallVector.h"
  11. #include "llvm/ADT/StringRef.h"
  12. #include "re2/re2.h"
  13. #include "testing/file_test/line.h"
  14. namespace Carbon::Testing {
  15. class FileTestAutoupdater {
  16. public:
  17. struct LineNumberReplacement {
  18. bool has_file;
  19. // The line replacement. The pattern should match lines. If has_file,
  20. // pattern should have a file and line group; otherwise, only a line group,
  21. // but default_file_re should be provided.
  22. //
  23. // Uses shared_ptr for storage in SmallVector.
  24. std::shared_ptr<RE2> re;
  25. // line_formatv should provide {0} to substitute with [[@LINE...]] deltas.
  26. std::string line_formatv;
  27. };
  28. // The file and line number that a CHECK line refers to, and the
  29. // replacement from which they were determined, if any.
  30. struct FileAndLineNumber {
  31. explicit FileAndLineNumber(int file_number) : file_number(file_number) {}
  32. explicit FileAndLineNumber(const LineNumberReplacement* replacement,
  33. int file_number, absl::string_view line_number);
  34. const LineNumberReplacement* replacement = nullptr;
  35. int file_number;
  36. int line_number = -1;
  37. };
  38. // A CHECK line which is integrated into autoupdate output.
  39. //
  40. // `final` because we use pointer arithmetic on this type.
  41. class CheckLine final : public FileTestLineBase {
  42. public:
  43. explicit CheckLine(FileAndLineNumber file_and_line_number, std::string line)
  44. : FileTestLineBase(file_and_line_number.file_number,
  45. file_and_line_number.line_number),
  46. replacement_(file_and_line_number.replacement),
  47. line_(std::move(line)) {}
  48. auto Print(llvm::raw_ostream& out) const -> void override {
  49. out << indent_ << line_;
  50. }
  51. // When the location of the CHECK in output is known, we can set the indent
  52. // and its line.
  53. auto SetOutputLine(llvm::StringRef indent, int output_file_number,
  54. int output_line_number) -> void {
  55. indent_ = indent;
  56. output_file_number_ = output_file_number;
  57. output_line_number_ = output_line_number;
  58. }
  59. // When the location of all lines in a file are known, we can set the line
  60. // offset based on the target line.
  61. auto RemapLineNumbers(
  62. const llvm::DenseMap<llvm::StringRef, int>& file_to_number_map,
  63. const llvm::DenseMap<std::pair<int, int>, int>& output_line_remap,
  64. const llvm::SmallVector<int>& new_last_line_numbers) -> void;
  65. auto line() const -> llvm::StringRef { return line_; }
  66. auto is_blank() const -> bool override { return false; }
  67. private:
  68. const LineNumberReplacement* replacement_;
  69. std::string line_;
  70. llvm::StringRef indent_;
  71. int output_file_number_ = -1;
  72. int output_line_number_ = -1;
  73. };
  74. using CheckLineArray = llvm::SmallVector<FileTestAutoupdater::CheckLine>;
  75. explicit FileTestAutoupdater(
  76. const std::filesystem::path& file_test_path, std::string test_command,
  77. std::string dump_command, llvm::StringRef input_content,
  78. const llvm::SmallVector<llvm::StringRef>& filenames,
  79. int autoupdate_line_number, bool autoupdate_split,
  80. const llvm::SmallVector<FileTestLine>& non_check_lines,
  81. llvm::StringRef actual_stdout, llvm::StringRef actual_stderr,
  82. const std::optional<RE2>& default_file_re,
  83. const llvm::SmallVector<LineNumberReplacement>& line_number_replacements,
  84. std::function<auto(std::string&)->void> do_extra_check_replacements,
  85. std::function<auto(CheckLineArray&, bool)->void> finalize_check_lines)
  86. : file_test_path_(file_test_path),
  87. test_command_(std::move(test_command)),
  88. dump_command_(std::move(dump_command)),
  89. input_content_(input_content),
  90. autoupdate_line_number_(autoupdate_line_number),
  91. non_check_lines_(non_check_lines),
  92. default_file_re_(default_file_re),
  93. line_number_replacements_(line_number_replacements),
  94. do_extra_check_replacements_(std::move(do_extra_check_replacements)),
  95. finalize_check_lines_(std::move(finalize_check_lines)),
  96. autoupdate_split_file_(
  97. autoupdate_split ? std::optional(filenames.size()) : std::nullopt),
  98. file_to_number_map_(BuildFileToNumberMap(filenames)),
  99. // BuildCheckLines should only be called after other member
  100. // initialization.
  101. stdout_(BuildCheckLines(actual_stdout, /*is_stderr=*/false)),
  102. stderr_(BuildCheckLines(actual_stderr, /*is_stderr=*/true)),
  103. any_attached_stdout_lines_(llvm::any_of(
  104. stdout_.lines,
  105. [&](const CheckLine& line) { return line.line_number() != -1; })),
  106. non_check_line_(non_check_lines_.begin()) {
  107. for (const auto& replacement : line_number_replacements_) {
  108. CARBON_CHECK(replacement.has_file || default_file_re_,
  109. "For replacement with pattern `{0}` to have has_file=false, "
  110. "override GetDefaultFileRE.",
  111. replacement.re->pattern());
  112. CARBON_CHECK(replacement.re->ok(), "Invalid line replacement RE2: {0}",
  113. replacement.re->error());
  114. }
  115. }
  116. // Automatically updates CHECKs in the provided file when dry_run=false.
  117. // Returns true if generated file content differs from actual file content.
  118. auto Run(bool dry_run) -> bool;
  119. private:
  120. // A TIP line added by autoupdate. Not associated with any line in output.
  121. class TipLine : public FileTestLineBase {
  122. public:
  123. explicit TipLine(std::string line)
  124. : FileTestLineBase(-1, -1), line_(std::move(line)) {}
  125. auto Print(llvm::raw_ostream& out) const -> void override { out << line_; }
  126. auto is_blank() const -> bool override { return line_.empty(); }
  127. private:
  128. std::string line_;
  129. };
  130. // Clusters information for stdout and stderr.
  131. struct CheckLines {
  132. explicit CheckLines(CheckLineArray lines)
  133. : lines(std::move(lines)), cursor(this->lines.begin()) {}
  134. // The full list of check lines.
  135. CheckLineArray lines;
  136. // An iterator into check_lines.
  137. CheckLine* cursor;
  138. };
  139. // Looks for the patterns in the line. Returns the first match, or defaulted
  140. // information if not found.
  141. auto GetFileAndLineNumber(
  142. const llvm::DenseMap<llvm::StringRef, int>& file_to_number_map,
  143. int default_file_number, const std::string& check_line)
  144. -> FileAndLineNumber;
  145. // Builds a mapping from file name to file number.
  146. auto BuildFileToNumberMap(const llvm::SmallVector<llvm::StringRef>& filenames)
  147. -> llvm::DenseMap<llvm::StringRef, int> {
  148. llvm::DenseMap<llvm::StringRef, int> file_to_number_map;
  149. for (auto [number, name] : llvm::enumerate(filenames)) {
  150. file_to_number_map.insert({name, number});
  151. }
  152. return file_to_number_map;
  153. }
  154. // Builds CheckLine lists for autoupdate.
  155. auto BuildCheckLines(llvm::StringRef output, bool is_stderr) -> CheckLines;
  156. // Adds a non-check line to the new_lines and output_line_remap. The caller
  157. // still needs to advance the cursor when ready.
  158. auto AddRemappedNonCheckLine() -> void;
  159. // Adds TIP lines for file_test usage.
  160. auto AddTips() -> void;
  161. // Returns true if there's a CheckLine that should be added at
  162. // `to_line_number`.
  163. auto ShouldAddCheckLine(const CheckLines& check_lines, bool to_file_end) const
  164. -> bool;
  165. // Adds check_lines until output reaches:
  166. // - If not to_file_end, non_check_line.
  167. // - If to_file_end, the end of the file.
  168. auto AddCheckLines(CheckLines& check_lines, bool to_file_end) -> void;
  169. // Adds remaining check lines for the current file. stderr is always included,
  170. // but stdout is only included when either any_attached_stdout_lines_ or
  171. // is_last_file is true.
  172. auto FinishFile(bool is_last_file) -> void;
  173. // Starts a new split file, updating file and line numbers. Advances past the
  174. // split line.
  175. auto StartSplitFile() -> void;
  176. // Passed-in state.
  177. const std::filesystem::path& file_test_path_;
  178. std::string test_command_;
  179. std::string dump_command_;
  180. llvm::StringRef input_content_;
  181. int autoupdate_line_number_;
  182. const llvm::SmallVector<FileTestLine>& non_check_lines_;
  183. const std::optional<RE2>& default_file_re_;
  184. const llvm::SmallVector<LineNumberReplacement>& line_number_replacements_;
  185. std::function<auto(std::string&)->void> do_extra_check_replacements_;
  186. std::function<auto(CheckLineArray&, bool)->void> finalize_check_lines_;
  187. // If we have an autoupdate split that still needs to be processed, the file
  188. // number of the autoupdate split. Otherwise, this is nullopt.
  189. std::optional<int> autoupdate_split_file_;
  190. // Generated TIP lines, from AddTips.
  191. llvm::SmallVector<TipLine> tips_;
  192. // Mapping from file names to file numbers.
  193. llvm::DenseMap<llvm::StringRef, int> file_to_number_map_;
  194. // The constructed CheckLine list and cursor.
  195. CheckLines stdout_;
  196. CheckLines stderr_;
  197. // Whether any stdout lines have an associated line number.
  198. bool any_attached_stdout_lines_;
  199. // Iterators for the main Run loop.
  200. const FileTestLine* non_check_line_;
  201. // Tracks the new last line numbers for each file.
  202. llvm::SmallVector<int> new_last_line_numbers_;
  203. // A reusable blank line. new_lines_ can contain a reference back to it.
  204. const FileTestLine blank_line_ = FileTestLine(-1, -1, "");
  205. // Stitched-together content.
  206. llvm::SmallVector<const FileTestLineBase*> new_lines_;
  207. // Maps {file_number, original line number} to a new line number.
  208. llvm::DenseMap<std::pair<int, int>, int> output_line_remap_;
  209. // The current output file number; mainly used for tracking progression.
  210. int output_file_number_ = 0;
  211. // The current output line number in stitched content.
  212. int output_line_number_ = 0;
  213. };
  214. } // namespace Carbon::Testing
  215. #endif // CARBON_TESTING_FILE_TEST_AUTOUPDATE_H_