Skip to content

Commit a52145e

Browse files
committed
transpile: tests: add .expect_unresolved_import instead of hardcoding libc
Also, we check that the error messages are as expected, too (meaning we actually run `rustc`). This also makes the `libc` tests more explicit.
1 parent f1262e6 commit a52145e

2 files changed

Lines changed: 91 additions & 16 deletions

File tree

c2rust-rust-tools/src/lib.rs

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use itertools::Itertools;
22
use log::warn;
3+
use std::collections::HashSet;
34
use std::fmt;
45
use std::fmt::Display;
56
use std::fmt::Formatter;
67
use std::io;
78
use std::io::Write;
89
use std::path::Path;
910
use std::process::Command;
11+
use std::str;
1012
use std::str::FromStr;
1113

1214
use crate::RustEdition::Edition2021;
@@ -148,6 +150,7 @@ pub struct Rustc<'a> {
148150
edition: RustEdition,
149151
crate_name: Option<&'a str>,
150152
expect_error: bool,
153+
imported_crates: &'a [&'a str],
151154
}
152155

153156
pub fn rustc(rs_path: &Path) -> Rustc {
@@ -156,6 +159,7 @@ pub fn rustc(rs_path: &Path) -> Rustc {
156159
edition: Default::default(),
157160
crate_name: None,
158161
expect_error: false,
162+
imported_crates: Default::default(),
159163
}
160164
}
161165

@@ -178,20 +182,39 @@ impl<'a> Rustc<'a> {
178182
}
179183
}
180184

185+
pub fn expect_unresolved_imports(self, imported_crates: &'a [&'a str]) -> Self {
186+
Self {
187+
imported_crates,
188+
..self
189+
}
190+
}
191+
181192
pub fn run(self) {
182193
let Self {
183194
rs_path,
184195
edition,
185196
crate_name,
186197
expect_error,
198+
imported_crates,
187199
} = self;
188200
let crate_name =
189201
crate_name.unwrap_or_else(|| rs_path.file_stem().unwrap().to_str().unwrap());
190-
run_rustc(rs_path, edition, crate_name, expect_error);
202+
run_rustc(rs_path, edition, crate_name, expect_error, &imported_crates);
191203
}
192204
}
193205

194-
fn run_rustc(rs_path: &Path, edition: RustEdition, crate_name: &str, expect_error: bool) {
206+
fn run_rustc(
207+
rs_path: &Path,
208+
edition: RustEdition,
209+
crate_name: &str,
210+
expect_error: bool,
211+
imported_crates: &[&str],
212+
) {
213+
let rs = fs_err::read_to_string(rs_path).unwrap();
214+
for imported_crate in imported_crates {
215+
assert!(rs.contains(&format!("::{imported_crate}")));
216+
}
217+
195218
// There's no good way to not create an output with `rustc`,
196219
// so just create an `.rlib` and then delete it immediately.
197220
let rlib_path = rs_path.with_file_name(format!("lib{crate_name}.rlib"));
@@ -215,6 +238,48 @@ fn run_rustc(rs_path: &Path, edition: RustEdition, crate_name: &str, expect_erro
215238
}
216239
io::stdout().write_all(&output.stdout).unwrap();
217240
io::stderr().write_all(&output.stderr).unwrap();
241+
242+
// Using rustc itself to build snapshots that reference crates (like libc) is difficult because we don't know
243+
// the appropriate --extern libc=/path/to/liblibc-XXXXXXXXXXXXXXXX.rlib to pass.
244+
// Skip for now, as we've already compared the literal text,
245+
// and we're checking the error messages here.
246+
let stderr = str::from_utf8(&output.stderr).unwrap();
247+
let error_lines = stderr
248+
.split('\n')
249+
// .split(|&b| b == b'\n')
250+
.filter(|line| line.starts_with("error[E"))
251+
.collect::<HashSet<_>>();
252+
dbg!(&error_lines);
253+
for imported_crate in imported_crates {
254+
// For `::{imported_crate}::*`.
255+
let absolute_path = match edition {
256+
Edition2021 => format!("error[E0433]: failed to resolve: could not find `{imported_crate}` in the list of imported crates"),
257+
Edition2024 => format!("error[E0433]: cannot find `{imported_crate}` in the crate root"),
258+
};
259+
// For `use ::{imported_crate}*`.
260+
let absolute_use_path = format!("error[E0432]: unresolved import `{imported_crate}`");
261+
// For `{imported_crate}::*`.
262+
let relative_path = format!(
263+
"error[E0433]: failed to resolve: use of undeclared crate or module `{imported_crate}`"
264+
);
265+
266+
let absolute_path = absolute_path.as_str();
267+
let absolute_use_path = absolute_use_path.as_str();
268+
let relative_path = relative_path.as_str();
269+
dbg!(absolute_path);
270+
dbg!(absolute_use_path);
271+
dbg!(relative_path);
272+
273+
assert!(
274+
error_lines.contains(absolute_path)
275+
|| error_lines.contains(absolute_use_path)
276+
|| error_lines.contains(relative_path)
277+
);
278+
}
279+
if !imported_crates.is_empty() {
280+
return;
281+
}
282+
218283
if expect_error {
219284
assert!(
220285
!status.success(),

c2rust-transpile/tests/snapshots.rs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ fn transpile_snapshot(
101101
c_path: &Path,
102102
edition: RustEdition,
103103
expect_compile_error: bool,
104+
imported_crates: &[&str],
104105
) {
105106
let cfg = config(edition);
106107
compile_and_transpile_file(c_path, cfg);
@@ -133,21 +134,11 @@ fn transpile_snapshot(
133134

134135
insta::assert_snapshot!(snapshot_name, &rs, &debug_expr);
135136

136-
// Using rustc itself to build snapshots that reference libc is difficult because we don't know
137-
// the appropriate --extern libc=/path/to/liblibc-XXXXXXXXXXXXXXXX.rlib to pass. Skip for now,
138-
// as we've already compared the literal text.
139-
if rs.contains("libc::") {
140-
eprintln!(
141-
"warning: skipping compiling {} with rustc since it depends on libc",
142-
rs_path.display()
143-
);
144-
return;
145-
}
146-
147137
rustc(&rs_path)
148138
.edition(edition)
149139
.crate_name(crate_name)
150140
.expect_error(expect_compile_error)
141+
.expect_unresolved_imports(imported_crates)
151142
.run();
152143
}
153144

@@ -158,6 +149,7 @@ struct TranspileTest<'a> {
158149
os_specific: bool,
159150
expect_compile_error_edition_2021: bool,
160151
expect_compile_error_edition_2024: bool,
152+
imported_crates: Vec<&'a str>,
161153
}
162154

163155
fn transpile(c_file_name: &str) -> TranspileTest {
@@ -167,6 +159,7 @@ fn transpile(c_file_name: &str) -> TranspileTest {
167159
os_specific: false,
168160
expect_compile_error_edition_2021: false,
169161
expect_compile_error_edition_2024: false,
162+
imported_crates: Default::default(),
170163
}
171164
}
172165

@@ -211,13 +204,19 @@ impl<'a> TranspileTest<'a> {
211204
.expect_compile_error_edition_2024(expect_error)
212205
}
213206

207+
pub fn expect_unresolved_import(mut self, imported_crate: &'a str) -> Self {
208+
self.imported_crates.push(imported_crate);
209+
self
210+
}
211+
214212
pub fn run(self) {
215213
let Self {
216214
c_file_name,
217215
arch_specific,
218216
os_specific,
219217
expect_compile_error_edition_2021,
220218
expect_compile_error_edition_2024,
219+
imported_crates,
221220
} = self;
222221

223222
let specific_dir_prefix = [arch_specific.then_some("arch"), os_specific.then_some("os")]
@@ -268,12 +267,14 @@ impl<'a> TranspileTest<'a> {
268267
&c_path,
269268
Edition2021,
270269
expect_compile_error_edition_2021,
270+
&imported_crates,
271271
);
272272
transpile_snapshot(
273273
&platform,
274274
&c_path,
275275
Edition2024,
276276
expect_compile_error_edition_2024,
277+
&imported_crates,
277278
);
278279
}
279280
}
@@ -451,7 +452,10 @@ fn test_rotate_os_specific() {
451452

452453
#[test]
453454
fn test_sigign() {
454-
transpile("sigign.c").os_specific(true).run();
455+
transpile("sigign.c")
456+
.os_specific(true)
457+
.expect_unresolved_import("libc")
458+
.run();
455459
}
456460

457461
#[test]
@@ -461,12 +465,18 @@ fn test_typedefidx() {
461465

462466
#[test]
463467
fn test_types() {
464-
transpile("types.c").os_specific(true).run();
468+
transpile("types.c")
469+
.os_specific(true)
470+
.expect_unresolved_import("libc")
471+
.run();
465472
}
466473

467474
#[test]
468475
fn test_wide_strings() {
469-
transpile("wide_strings.c").os_specific(true).run();
476+
transpile("wide_strings.c")
477+
.os_specific(true)
478+
.expect_unresolved_import("libc")
479+
.run();
470480
}
471481

472482
// arch-os-specific

0 commit comments

Comments
 (0)