Skip to content

Commit f616817

Browse files
authored
Improve autoupdate diagnostic CHECK line positioning. (#5942)
When an error diagnostic has an unattached location, for example because the diagnostic points into a file that's in the prelude, use the next attached location to position the error diagnostic's CHECK line. In particular, if the error is followed by a note, use the position of the note to determine where to place the error. This exposes a general mechanism to do final fixups of the CHECK lines to individual file_test binaries, which the toolchain's binary uses to special-case error / warning CHECK lines.
1 parent efbc9f7 commit f616817

File tree

9 files changed

+141
-91
lines changed

9 files changed

+141
-91
lines changed

testing/file_test/autoupdate.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ auto FileTestAutoupdater::GetFileAndLineNumber(
150150
}
151151

152152
auto FileTestAutoupdater::BuildCheckLines(llvm::StringRef output,
153-
const char* label) -> CheckLines {
153+
bool is_stderr) -> CheckLines {
154154
if (output.empty()) {
155155
return CheckLines({});
156156
}
@@ -166,16 +166,19 @@ auto FileTestAutoupdater::BuildCheckLines(llvm::StringRef output,
166166
lines.pop_back();
167167
}
168168

169+
auto label =
170+
is_stderr ? llvm::StringLiteral("STDERR") : llvm::StringLiteral("STDOUT");
171+
169172
// The default file number for when no specific file is found.
170173
int default_file_number = 0;
171174

172-
llvm::SmallVector<CheckLine> check_lines;
175+
CheckLineArray check_lines;
173176
for (const auto& line : lines) {
174177
// This code is relatively hot in our testing, and because when testing it
175178
// isn't run with an optimizer we benefit from making it use simple
176179
// constructs. For this reason, we avoid `llvm::formatv` and similar tools.
177180
std::string check_line;
178-
check_line.reserve(line.size() + strlen(label) + strlen("// CHECK:: "));
181+
check_line.reserve(line.size() + label.size() + strlen("// CHECK:: "));
179182
check_line.append("// CHECK:");
180183
check_line.append(label);
181184
check_line.append(":");
@@ -219,6 +222,7 @@ auto FileTestAutoupdater::BuildCheckLines(llvm::StringRef output,
219222
default_file_number, check_line);
220223
check_lines.push_back(CheckLine(file_and_line, check_line));
221224
}
225+
finalize_check_lines_(check_lines, is_stderr);
222226
return CheckLines(check_lines);
223227
}
224228

testing/file_test/autoupdate.h

Lines changed: 67 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,64 @@ class FileTestAutoupdater {
3333
std::string line_formatv;
3434
};
3535

36+
// The file and line number that a CHECK line refers to, and the
37+
// replacement from which they were determined, if any.
38+
struct FileAndLineNumber {
39+
explicit FileAndLineNumber(int file_number) : file_number(file_number) {}
40+
41+
explicit FileAndLineNumber(const LineNumberReplacement* replacement,
42+
int file_number, absl::string_view line_number);
43+
44+
const LineNumberReplacement* replacement = nullptr;
45+
int file_number;
46+
int line_number = -1;
47+
};
48+
49+
// A CHECK line which is integrated into autoupdate output.
50+
//
51+
// `final` because we use pointer arithmetic on this type.
52+
class CheckLine final : public FileTestLineBase {
53+
public:
54+
explicit CheckLine(FileAndLineNumber file_and_line_number, std::string line)
55+
: FileTestLineBase(file_and_line_number.file_number,
56+
file_and_line_number.line_number),
57+
replacement_(file_and_line_number.replacement),
58+
line_(std::move(line)) {}
59+
60+
auto Print(llvm::raw_ostream& out) const -> void override {
61+
out << indent_ << line_;
62+
}
63+
64+
// When the location of the CHECK in output is known, we can set the indent
65+
// and its line.
66+
auto SetOutputLine(llvm::StringRef indent, int output_file_number,
67+
int output_line_number) -> void {
68+
indent_ = indent;
69+
output_file_number_ = output_file_number;
70+
output_line_number_ = output_line_number;
71+
}
72+
73+
// When the location of all lines in a file are known, we can set the line
74+
// offset based on the target line.
75+
auto RemapLineNumbers(
76+
const llvm::DenseMap<llvm::StringRef, int>& file_to_number_map,
77+
const llvm::DenseMap<std::pair<int, int>, int>& output_line_remap,
78+
const llvm::SmallVector<int>& new_last_line_numbers) -> void;
79+
80+
auto line() const -> llvm::StringRef { return line_; }
81+
82+
auto is_blank() const -> bool override { return false; }
83+
84+
private:
85+
const LineNumberReplacement* replacement_;
86+
std::string line_;
87+
llvm::StringRef indent_;
88+
int output_file_number_ = -1;
89+
int output_line_number_ = -1;
90+
};
91+
92+
using CheckLineArray = llvm::SmallVector<FileTestAutoupdater::CheckLine>;
93+
3694
explicit FileTestAutoupdater(
3795
const std::filesystem::path& file_test_path, std::string test_command,
3896
std::string dump_command, llvm::StringRef input_content,
@@ -42,7 +100,8 @@ class FileTestAutoupdater {
42100
llvm::StringRef actual_stdout, llvm::StringRef actual_stderr,
43101
const std::optional<RE2>& default_file_re,
44102
const llvm::SmallVector<LineNumberReplacement>& line_number_replacements,
45-
std::function<auto(std::string&)->void> do_extra_check_replacements)
103+
std::function<auto(std::string&)->void> do_extra_check_replacements,
104+
std::function<auto(CheckLineArray&, bool)->void> finalize_check_lines)
46105
: file_test_path_(file_test_path),
47106
test_command_(std::move(test_command)),
48107
dump_command_(std::move(dump_command)),
@@ -52,13 +111,14 @@ class FileTestAutoupdater {
52111
default_file_re_(default_file_re),
53112
line_number_replacements_(line_number_replacements),
54113
do_extra_check_replacements_(std::move(do_extra_check_replacements)),
114+
finalize_check_lines_(std::move(finalize_check_lines)),
55115
autoupdate_split_file_(
56116
autoupdate_split ? std::optional(filenames.size()) : std::nullopt),
57117
file_to_number_map_(BuildFileToNumberMap(filenames)),
58118
// BuildCheckLines should only be called after other member
59119
// initialization.
60-
stdout_(BuildCheckLines(actual_stdout, "STDOUT")),
61-
stderr_(BuildCheckLines(actual_stderr, "STDERR")),
120+
stdout_(BuildCheckLines(actual_stdout, /*is_stderr=*/false)),
121+
stderr_(BuildCheckLines(actual_stderr, /*is_stderr=*/true)),
62122
any_attached_stdout_lines_(llvm::any_of(
63123
stdout_.lines,
64124
[&](const CheckLine& line) { return line.line_number() != -1; })),
@@ -78,19 +138,6 @@ class FileTestAutoupdater {
78138
auto Run(bool dry_run) -> bool;
79139

80140
private:
81-
// The file and line number that a CHECK line refers to, and the
82-
// replacement from which they were determined, if any.
83-
struct FileAndLineNumber {
84-
explicit FileAndLineNumber(int file_number) : file_number(file_number) {}
85-
86-
explicit FileAndLineNumber(const LineNumberReplacement* replacement,
87-
int file_number, absl::string_view line_number);
88-
89-
const LineNumberReplacement* replacement = nullptr;
90-
int file_number;
91-
int line_number = -1;
92-
};
93-
94141
// A TIP line added by autoupdate. Not associated with any line in output.
95142
class TipLine : public FileTestLineBase {
96143
public:
@@ -105,55 +152,13 @@ class FileTestAutoupdater {
105152
std::string line_;
106153
};
107154

108-
// A CHECK line which is integrated into autoupdate output.
109-
//
110-
// `final` because we use pointer arithmetic on this type.
111-
class CheckLine final : public FileTestLineBase {
112-
public:
113-
// RE2 is passed by a pointer because it doesn't support std::optional.
114-
explicit CheckLine(FileAndLineNumber file_and_line_number, std::string line)
115-
: FileTestLineBase(file_and_line_number.file_number,
116-
file_and_line_number.line_number),
117-
replacement_(file_and_line_number.replacement),
118-
line_(std::move(line)) {}
119-
120-
auto Print(llvm::raw_ostream& out) const -> void override {
121-
out << indent_ << line_;
122-
}
123-
124-
// When the location of the CHECK in output is known, we can set the indent
125-
// and its line.
126-
auto SetOutputLine(llvm::StringRef indent, int output_file_number,
127-
int output_line_number) -> void {
128-
indent_ = indent;
129-
output_file_number_ = output_file_number;
130-
output_line_number_ = output_line_number;
131-
}
132-
133-
// When the location of all lines in a file are known, we can set the line
134-
// offset based on the target line.
135-
auto RemapLineNumbers(
136-
const llvm::DenseMap<llvm::StringRef, int>& file_to_number_map,
137-
const llvm::DenseMap<std::pair<int, int>, int>& output_line_remap,
138-
const llvm::SmallVector<int>& new_last_line_numbers) -> void;
139-
140-
auto is_blank() const -> bool override { return false; }
141-
142-
private:
143-
const LineNumberReplacement* replacement_;
144-
std::string line_;
145-
llvm::StringRef indent_;
146-
int output_file_number_ = -1;
147-
int output_line_number_ = -1;
148-
};
149-
150155
// Clusters information for stdout and stderr.
151156
struct CheckLines {
152-
explicit CheckLines(llvm::SmallVector<CheckLine> lines)
157+
explicit CheckLines(CheckLineArray lines)
153158
: lines(std::move(lines)), cursor(this->lines.begin()) {}
154159

155160
// The full list of check lines.
156-
llvm::SmallVector<CheckLine> lines;
161+
CheckLineArray lines;
157162
// An iterator into check_lines.
158163
CheckLine* cursor;
159164
};
@@ -176,7 +181,7 @@ class FileTestAutoupdater {
176181
}
177182

178183
// Builds CheckLine lists for autoupdate.
179-
auto BuildCheckLines(llvm::StringRef output, const char* label) -> CheckLines;
184+
auto BuildCheckLines(llvm::StringRef output, bool is_stderr) -> CheckLines;
180185

181186
// Adds a non-check line to the new_lines and output_line_remap. The caller
182187
// still needs to advance the cursor when ready.
@@ -214,6 +219,7 @@ class FileTestAutoupdater {
214219
const std::optional<RE2>& default_file_re_;
215220
const llvm::SmallVector<LineNumberReplacement>& line_number_replacements_;
216221
std::function<auto(std::string&)->void> do_extra_check_replacements_;
222+
std::function<auto(CheckLineArray&, bool)->void> finalize_check_lines_;
217223

218224
// If we have an autoupdate split that still needs to be processed, the file
219225
// number of the autoupdate split. Otherwise, this is nullopt.

testing/file_test/file_test_base.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ static auto RunAutoupdater(FileTestBase* test_base, const TestFile& test_file,
194194
test_base->GetLineNumberReplacements(expected_filenames),
195195
[&](std::string& line) {
196196
test_base->DoExtraCheckReplacements(line);
197+
},
198+
[&](FileTestBase::CheckLineArray& lines, bool is_stderr) {
199+
test_base->FinalizeCheckLines(lines, is_stderr);
197200
})
198201
.Run(dry_run);
199202
}

testing/file_test/file_test_base.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class FileTestBase {
2828
public:
2929
// Provided for child class convenience.
3030
using LineNumberReplacement = FileTestAutoupdater::LineNumberReplacement;
31+
using CheckLineArray = FileTestAutoupdater::CheckLineArray;
3132

3233
// The result of Run(), used to detect errors. Failing test files should be
3334
// named with a `fail_` prefix to indicate an expectation of failure.
@@ -101,6 +102,11 @@ class FileTestBase {
101102
virtual auto DoExtraCheckReplacements(std::string& /*check_line*/) const
102103
-> void {}
103104

105+
// Optionally allows children to perform tweaks on check lines before
106+
// they are merged into the output file.
107+
virtual auto FinalizeCheckLines(CheckLineArray& /*check_lines*/,
108+
bool /*is_stderr*/) const -> void {}
109+
104110
// Whether to allow running the test in parallel, particularly for autoupdate.
105111
// This can be overridden to force some tests to be run serially. At any given
106112
// time, all parallel tests and a single non-parallel test will be allowed to

testing/file_test/line.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ class FileTestLineBase : public Printable<FileTestLineBase> {
2525
auto file_number() const -> int { return file_number_; }
2626
auto line_number() const -> int { return line_number_; }
2727

28+
void set_location(int file_number, int line_number) {
29+
file_number_ = file_number;
30+
line_number_ = line_number;
31+
}
32+
2833
private:
2934
int file_number_;
3035
int line_number_;

toolchain/check/testdata/basics/type_literals.carbon

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,14 @@ var test_i0: i0;
7878
// CHECK:STDERR: ^~
7979
// CHECK:STDERR:
8080
var test_i1: i1;
81-
// CHECK:STDERR: fail_iN_bad_width.carbon:[[@LINE+7]]:15: error: bit width of integer type literal must be a multiple of 8; use `Core.Int(15)` instead [IntWidthNotMultipleOf8]
81+
// CHECK:STDERR: fail_iN_bad_width.carbon:[[@LINE+4]]:15: error: bit width of integer type literal must be a multiple of 8; use `Core.Int(15)` instead [IntWidthNotMultipleOf8]
8282
// CHECK:STDERR: var test_i15: i15;
8383
// CHECK:STDERR: ^~~
8484
// CHECK:STDERR:
85+
var test_i15: i15;
8586
// CHECK:STDERR: min_prelude/parts/int.carbon:10:9: error: integer type width of 1000000000 is greater than the maximum supported width of 8388608 [IntWidthTooLarge]
8687
// CHECK:STDERR: adapt MakeInt(N);
8788
// CHECK:STDERR: ^~~~~~~~~~
88-
var test_i15: i15;
8989
// CHECK:STDERR: fail_iN_bad_width.carbon:[[@LINE+4]]:23: note: in `i1000000000` used here [ResolvingSpecificHere]
9090
// CHECK:STDERR: var test_i1000000000: i1000000000;
9191
// CHECK:STDERR: ^~~~~~~~~~~
@@ -115,14 +115,14 @@ var test_u0: u0;
115115
// CHECK:STDERR: ^~
116116
// CHECK:STDERR:
117117
var test_u1: u1;
118-
// CHECK:STDERR: fail_uN_bad_width.carbon:[[@LINE+7]]:15: error: bit width of integer type literal must be a multiple of 8; use `Core.UInt(15)` instead [IntWidthNotMultipleOf8]
118+
// CHECK:STDERR: fail_uN_bad_width.carbon:[[@LINE+4]]:15: error: bit width of integer type literal must be a multiple of 8; use `Core.UInt(15)` instead [IntWidthNotMultipleOf8]
119119
// CHECK:STDERR: var test_u15: u15;
120120
// CHECK:STDERR: ^~~
121121
// CHECK:STDERR:
122+
var test_u15: u15;
122123
// CHECK:STDERR: min_prelude/parts/uint.carbon:10:9: error: integer type width of 1000000000 is greater than the maximum supported width of 8388608 [IntWidthTooLarge]
123124
// CHECK:STDERR: adapt MakeUInt(N);
124125
// CHECK:STDERR: ^~~~~~~~~~~
125-
var test_u15: u15;
126126
// CHECK:STDERR: fail_uN_bad_width.carbon:[[@LINE+4]]:23: note: in `u1000000000` used here [ResolvingSpecificHere]
127127
// CHECK:STDERR: var test_u1000000000: u1000000000;
128128
// CHECK:STDERR: ^~~~~~~~~~~

0 commit comments

Comments
 (0)