Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/doc/rustc-dev-guide/src/tests/compiletest.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,14 +311,20 @@ For example, `./x test tests/debuginfo -- --debugger gdb` will only test GDB com

### Codegen tests

The tests in [`tests/codegen-llvm`] test LLVM code generation. They compile the test
with the `--emit=llvm-ir` flag to emit LLVM IR. They then run the LLVM
The tests in [`tests/codegen-llvm`] test LLVM code generation. They compile the
test with the `--emit=llvm-ir` flag to emit LLVM IR. They then run the LLVM
[FileCheck] tool. The test is annotated with various `// CHECK` comments to
check the generated code. See the [FileCheck] documentation for a tutorial and
more information.

See also the [assembly tests](#assembly-tests) for a similar set of tests.

By default, codegen tests will have `//@ needs-target-std` *implied* (that the
target needs to support std), *unless* the `#![no_std]`/`#![no_core]` attribute
was specified in the test source. You can override this behavior and explicitly
write `//@ needs-target-std` to only run the test when target supports std, even
if the test is `#![no_std]`/`#![no_core]`.

If you need to work with `#![no_std]` cross-compiling tests, consult the
[`minicore` test auxiliary](./minicore.md) chapter.

Expand Down
2 changes: 2 additions & 0 deletions src/doc/rustc-dev-guide/src/tests/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ The following directives will check rustc build settings and target settings:
on `wasm32-unknown-unknown` target because the target does not support the
`proc-macro` crate type.
- `needs-target-std` — ignores if target platform does not have std support.
- See also [`#![no_std]`/`#![no_core]` and implied `needs-target-std` for
codegen tests](./compiletest.md#codegen-tests).
- `ignore-backends` — ignores the listed backends, separated by whitespace characters.
Please note
that this directive can be overriden with the `--bypass-ignore-backends=[BACKEND]` command line
Expand Down
15 changes: 14 additions & 1 deletion src/tools/compiletest/src/directives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ pub(crate) struct TestProps {
pub add_minicore: bool,
/// Add these flags to the build of `minicore`.
pub minicore_compile_flags: Vec<String>,
/// Whether line annotatins are required for the given error kind.
/// Whether line annotations are required for the given error kind.
pub dont_require_annotations: HashSet<ErrorKind>,
/// Whether pretty printers should be disabled in gdb.
pub disable_gdb_pretty_printers: bool,
Expand Down Expand Up @@ -600,6 +600,19 @@ fn iter_directives(
}
}

// Note: affects all codegen test suites under test mode `codegen`, e.g. `codegen-llvm`.
//
// Codegen tests automatically receive implied `//@ needs-target-std`, unless
// `#![no_std]`/`#![no_core]` attribute was explicitly seen. The rationale is basically to avoid
// having to manually maintain a bunch of `//@ needs-target-std` directives esp. for targets
// tested/built out-of-tree.
if mode == TestMode::Codegen && !file_directives.has_explicit_no_std_core_attribute {
let implied_needs_target_std_line =
line_directive(testfile, LineNumber::ZERO, "//@ needs-target-std")
.expect("valid `needs-target-std` directive line");
it(&implied_needs_target_std_line);
}

for directive_line in &file_directives.lines {
it(directive_line);
}
Expand Down
19 changes: 18 additions & 1 deletion src/tools/compiletest/src/directives/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,37 @@ use crate::directives::line::{DirectiveLine, line_directive};
pub(crate) struct FileDirectives<'a> {
pub(crate) path: &'a Utf8Path,
pub(crate) lines: Vec<DirectiveLine<'a>>,

/// Whether the test source file contains an explicit `#![no_std]`/`#![no_core]` attribute.
pub(crate) has_explicit_no_std_core_attribute: bool,
}

impl<'a> FileDirectives<'a> {
pub(crate) fn from_file_contents(path: &'a Utf8Path, file_contents: &'a str) -> Self {
let mut lines = vec![];
let mut has_explicit_no_std_core_attribute = false;

for (line_number, ln) in LineNumber::enumerate().zip(file_contents.lines()) {
let ln = ln.trim();

// Perform a naive check for lines starting with `#![no_std]`/`#![no_core]`, which
// suppresses the implied `//@ needs-target-std` in codegen tests. This ignores
// occurrences in ordinary comments.
//
// This check is imperfect in some edge cases, but we can generally trust our own test
// suite to not hit those edge cases (e.g. `#![no_std]`/`#![no_core]` in multi-line
// comments or string literals). Tests can write `//@ needs-target-std` manually if
// needed.
if ln.starts_with("#![no_std]") || ln.starts_with("#![no_core]") {
has_explicit_no_std_core_attribute = true;
continue;
}

if let Some(directive_line) = line_directive(path, line_number, ln) {
lines.push(directive_line);
Copy link
Contributor

@xdoardo xdoardo Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked too much into it, but I noticed that the testing infrastructure looks at the directives in reverse to find reasons to ignore the test for the current target.

This means, for example, that a test like this will be ignored with the "target needs std" message if it is run for a target that does not have std and is not a 64-bit platform.

It could make sense to add the synthetic directive at the beginning of the vector instead, so that the (potential) reasons to ignore the test for the current target that are already in the file are checked first.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems okay, but also I think that's an orthogonal change which can benefit from a more aggressive change: I wonder if we should report the set of criteria that contributes to a test being ignored, rather than just the "first" or "last" directive.

}
}

Self { path, lines }
Self { path, lines, has_explicit_no_std_core_attribute }
}
}
96 changes: 95 additions & 1 deletion src/tools/compiletest/src/directives/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ fn test_parse_normalize_rule() {
#[derive(Default)]
struct ConfigBuilder {
mode: Option<String>,
suite: Option<String>,
channel: Option<String>,
edition: Option<Edition>,
host: Option<String>,
Expand All @@ -126,6 +127,11 @@ impl ConfigBuilder {
self
}

fn suite(&mut self, s: &str) -> &mut Self {
self.suite = Some(s.to_owned());
self
}

fn channel(&mut self, s: &str) -> &mut Self {
self.channel = Some(s.to_owned());
self
Expand Down Expand Up @@ -196,7 +202,8 @@ impl ConfigBuilder {
"compiletest",
"--mode",
self.mode.as_deref().unwrap_or("ui"),
"--suite=ui",
"--suite",
self.suite.as_deref().unwrap_or("ui"),
"--compile-lib-path=",
"--run-lib-path=",
"--python=",
Expand Down Expand Up @@ -1019,6 +1026,93 @@ fn test_needs_target_std() {
assert!(!check_ignore(&config, "//@ needs-target-std"));
}

#[test]
fn implied_needs_target_std() {
let config = cfg().mode("codegen").suite("codegen-llvm").target("x86_64-unknown-none").build();
// Implied `needs-target-std` due to no `#![no_std]`/`#![no_core]`.
assert!(check_ignore(&config, ""));
assert!(check_ignore(&config, "//@ needs-target-std"));
assert!(!check_ignore(&config, "#![no_std]"));
assert!(!check_ignore(&config, "#![no_core]"));
// Make sure that `//@ needs-target-std` takes precedence.
assert!(check_ignore(
&config,
r#"
//@ needs-target-std
#![no_std]
"#
));
assert!(check_ignore(
&config,
r#"
//@ needs-target-std
#![no_core]
"#
));

let config =
cfg().mode("codegen").suite("codegen-llvm").target("x86_64-unknown-linux-gnu").build();
assert!(!check_ignore(&config, ""));
assert!(!check_ignore(&config, "//@ needs-target-std"));
assert!(!check_ignore(&config, "#![no_std]"));
assert!(!check_ignore(&config, "#![no_core]"));
assert!(!check_ignore(
&config,
r#"
//@ needs-target-std
#![no_std]
"#
));
assert!(!check_ignore(
&config,
r#"
//@ needs-target-std
#![no_core]
"#
));

let config = cfg().mode("ui").suite("ui").target("x86_64-unknown-none").build();
// The implied `//@ needs-target-std` is only applicable for mode=codegen tests.
assert!(!check_ignore(&config, ""));
assert!(check_ignore(&config, "//@ needs-target-std"));
assert!(!check_ignore(&config, "#![no_std]"));
assert!(!check_ignore(&config, "#![no_core]"));
assert!(check_ignore(
&config,
r#"
//@ needs-target-std
#![no_std]
"#
));
assert!(check_ignore(
&config,
r#"
//@ needs-target-std
#![no_core]
"#
));

let config = cfg().mode("ui").suite("ui").target("x86_64-unknown-linux-gnu").build();
assert!(!check_ignore(&config, ""));
assert!(!check_ignore(&config, "//@ needs-target-std"));
assert!(!check_ignore(&config, "#![no_std]"));
assert!(!check_ignore(&config, "#![no_core]"));
assert!(!check_ignore(
&config,
r#"
//@ needs-target-std
#![no_std]
"#
));
assert!(!check_ignore(
&config,
r#"
//@ needs-target-std
#![no_core]
"#
));
}

fn parse_edition_range(line: &str) -> Option<EditionRange> {
let config = cfg().build();

Expand Down
Loading