Auto merge of #140592 - cuviper:beta-next, r=cuviper
[beta] backports - Don't allow flattened format_args in const. #139624 - set subsections_via_symbols for ld64 helper sections #139752 - Fix detection of `main` function if there are expressions around it #140220 - rustdoc: Fix doctest heuristic for main fn wrapping #140420 - extend the list of registered dylibs on `test::prepare_cargo_test` #140563 r? cuviper
This commit is contained in:
commit
973ec11f61
16 changed files with 274 additions and 62 deletions
|
|
@ -2011,6 +2011,12 @@ fn add_linked_symbol_object(
|
|||
file.set_mangling(object::write::Mangling::None);
|
||||
}
|
||||
|
||||
if file.format() == object::BinaryFormat::MachO {
|
||||
// Divide up the sections into sub-sections via symbols for dead code stripping.
|
||||
// Without this flag, unused `#[no_mangle]` or `#[used]` cannot be discard on MachO targets.
|
||||
file.set_subsections_via_symbols();
|
||||
}
|
||||
|
||||
// ld64 requires a relocation to load undefined symbols, see below.
|
||||
// Not strictly needed if linking with lld, but might as well do it there too.
|
||||
let ld64_section_helper = if file.format() == object::BinaryFormat::MachO {
|
||||
|
|
|
|||
|
|
@ -205,8 +205,15 @@ impl Argument<'_> {
|
|||
/// let f = format_args!("{}", "a");
|
||||
/// println!("{f}");
|
||||
/// ```
|
||||
///
|
||||
/// This function should _not_ be const, to make sure we don't accept
|
||||
/// format_args!() and panic!() with arguments in const, even when not evaluated:
|
||||
///
|
||||
/// ```compile_fail,E0015
|
||||
/// const _: () = if false { panic!("a {}", "a") };
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn none() -> [Self; 0] {
|
||||
pub fn none() -> [Self; 0] {
|
||||
[]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2570,9 +2570,9 @@ fn prepare_cargo_test(
|
|||
// We skip everything on Miri as then this overwrites the libdir set up
|
||||
// by `Cargo::new` and that actually makes things go wrong.
|
||||
if builder.kind != Kind::Miri {
|
||||
let mut dylib_path = dylib_path();
|
||||
dylib_path.insert(0, PathBuf::from(&*builder.sysroot_target_libdir(compiler, target)));
|
||||
cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
|
||||
let mut dylib_paths = builder.rustc_lib_paths(compiler);
|
||||
dylib_paths.push(PathBuf::from(&builder.sysroot_target_libdir(compiler, target)));
|
||||
helpers::add_dylib_path(dylib_paths, &mut cargo);
|
||||
}
|
||||
|
||||
if builder.remote_tested(target) {
|
||||
|
|
|
|||
|
|
@ -301,8 +301,6 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
|
|||
|
||||
let filename = FileName::anon_source_code(&wrapped_source);
|
||||
|
||||
// Any errors in parsing should also appear when the doctest is compiled for real, so just
|
||||
// send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
|
||||
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let fallback_bundle = rustc_errors::fallback_fluent_bundle(
|
||||
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
|
||||
|
|
@ -311,7 +309,8 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
|
|||
info.supports_color =
|
||||
HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone())
|
||||
.supports_color();
|
||||
|
||||
// Any errors in parsing should also appear when the doctest is compiled for real, so just
|
||||
// send all the errors that the parser emits directly into a `Sink` instead of stderr.
|
||||
let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
|
||||
|
||||
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
|
||||
|
|
@ -339,9 +338,6 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
|
|||
*prev_span_hi = hi;
|
||||
}
|
||||
|
||||
// Recurse through functions body. It is necessary because the doctest source code is
|
||||
// wrapped in a function to limit the number of AST errors. If we don't recurse into
|
||||
// functions, we would thing all top-level items (so basically nothing).
|
||||
fn check_item(item: &ast::Item, info: &mut ParseSourceInfo, crate_name: &Option<&str>) -> bool {
|
||||
let mut is_extern_crate = false;
|
||||
if !info.has_global_allocator
|
||||
|
|
@ -351,8 +347,6 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
|
|||
}
|
||||
match item.kind {
|
||||
ast::ItemKind::Fn(_) if !info.has_main_fn => {
|
||||
// We only push if it's the top item because otherwise, we would duplicate
|
||||
// its content since the top-level item was already added.
|
||||
if item.ident.name == sym::main {
|
||||
info.has_main_fn = true;
|
||||
}
|
||||
|
|
@ -411,37 +405,46 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
|
|||
push_to_s(&mut info.crate_attrs, source, attr.span, &mut prev_span_hi);
|
||||
}
|
||||
}
|
||||
let mut has_non_items = false;
|
||||
for stmt in &body.stmts {
|
||||
let mut is_extern_crate = false;
|
||||
match stmt.kind {
|
||||
StmtKind::Item(ref item) => {
|
||||
is_extern_crate = check_item(&item, &mut info, crate_name);
|
||||
is_extern_crate = check_item(item, &mut info, crate_name);
|
||||
}
|
||||
StmtKind::Expr(ref expr) if matches!(expr.kind, ast::ExprKind::Err(_)) => {
|
||||
reset_error_count(&psess);
|
||||
return Err(());
|
||||
}
|
||||
StmtKind::MacCall(ref mac_call) if !info.has_main_fn => {
|
||||
let mut iter = mac_call.mac.args.tokens.iter();
|
||||
|
||||
while let Some(token) = iter.next() {
|
||||
if let TokenTree::Token(token, _) = token
|
||||
&& let TokenKind::Ident(name, _) = token.kind
|
||||
&& name == kw::Fn
|
||||
&& let Some(TokenTree::Token(fn_token, _)) = iter.peek()
|
||||
&& let TokenKind::Ident(fn_name, _) = fn_token.kind
|
||||
&& fn_name == sym::main
|
||||
&& let Some(TokenTree::Delimited(_, _, Delimiter::Parenthesis, _)) = {
|
||||
iter.next();
|
||||
iter.peek()
|
||||
// We assume that the macro calls will expand to item(s) even though they could
|
||||
// expand to statements and expressions.
|
||||
StmtKind::MacCall(ref mac_call) => {
|
||||
if !info.has_main_fn {
|
||||
// For backward compatibility, we look for the token sequence `fn main(…)`
|
||||
// in the macro input (!) to crudely detect main functions "masked by a
|
||||
// wrapper macro". For the record, this is a horrible heuristic!
|
||||
// See <https://github.com/rust-lang/rust/issues/56898>.
|
||||
let mut iter = mac_call.mac.args.tokens.iter();
|
||||
while let Some(token) = iter.next() {
|
||||
if let TokenTree::Token(token, _) = token
|
||||
&& let TokenKind::Ident(kw::Fn, _) = token.kind
|
||||
&& let Some(TokenTree::Token(ident, _)) = iter.peek()
|
||||
&& let TokenKind::Ident(sym::main, _) = ident.kind
|
||||
&& let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, _)) = {
|
||||
iter.next();
|
||||
iter.peek()
|
||||
}
|
||||
{
|
||||
info.has_main_fn = true;
|
||||
break;
|
||||
}
|
||||
{
|
||||
info.has_main_fn = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
StmtKind::Expr(ref expr) => {
|
||||
if matches!(expr.kind, ast::ExprKind::Err(_)) {
|
||||
reset_error_count(&psess);
|
||||
return Err(());
|
||||
}
|
||||
has_non_items = true;
|
||||
}
|
||||
StmtKind::Let(_) | StmtKind::Semi(_) | StmtKind::Empty => has_non_items = true,
|
||||
}
|
||||
|
||||
// Weirdly enough, the `Stmt` span doesn't include its attributes, so we need to
|
||||
|
|
@ -466,6 +469,11 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
|
|||
push_to_s(&mut info.crates, source, span, &mut prev_span_hi);
|
||||
}
|
||||
}
|
||||
if has_non_items {
|
||||
// FIXME: if `info.has_main_fn` is `true`, emit a warning here to mention that
|
||||
// this code will not be called.
|
||||
info.has_main_fn = false;
|
||||
}
|
||||
Ok(info)
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
|
|||
1
tests/rustdoc-ui/doctest/auxiliary/items.rs
Normal file
1
tests/rustdoc-ui/doctest/auxiliary/items.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
fn item() {}
|
||||
|
|
@ -4,12 +4,12 @@
|
|||
//@ compile-flags:--test
|
||||
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
|
||||
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
|
||||
//@ failure-status: 101
|
||||
//@ check-pass
|
||||
|
||||
/// <https://github.com/rust-lang/rust/issues/91014>
|
||||
///
|
||||
/// ```rust
|
||||
/// struct S {}; // unexpected semicolon after struct def
|
||||
/// struct S {};
|
||||
///
|
||||
/// fn main() {
|
||||
/// assert_eq!(0, 1);
|
||||
|
|
|
|||
|
|
@ -1,29 +1,6 @@
|
|||
|
||||
running 1 test
|
||||
test $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) ... FAILED
|
||||
test $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) ... ok
|
||||
|
||||
failures:
|
||||
|
||||
---- $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) stdout ----
|
||||
error: expected item, found `;`
|
||||
--> $DIR/failed-doctest-extra-semicolon-on-item.rs:12:12
|
||||
|
|
||||
LL | struct S {}; // unexpected semicolon after struct def
|
||||
| ^
|
||||
|
|
||||
= help: braced struct declarations are not followed by a semicolon
|
||||
help: remove this semicolon
|
||||
|
|
||||
LL - struct S {}; // unexpected semicolon after struct def
|
||||
LL + struct S {} // unexpected semicolon after struct def
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
Couldn't compile the test.
|
||||
|
||||
failures:
|
||||
$DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11)
|
||||
|
||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
|
||||
running 4 tests
|
||||
test $DIR/main-alongside-macro-calls.rs - (line 19) ... ok
|
||||
test $DIR/main-alongside-macro-calls.rs - (line 24) ... ok
|
||||
test $DIR/main-alongside-macro-calls.rs - (line 28) ... FAILED
|
||||
test $DIR/main-alongside-macro-calls.rs - (line 33) ... FAILED
|
||||
|
||||
failures:
|
||||
|
||||
---- $DIR/main-alongside-macro-calls.rs - (line 28) stdout ----
|
||||
error: macros that expand to items must be delimited with braces or followed by a semicolon
|
||||
--> $DIR/main-alongside-macro-calls.rs:30:1
|
||||
|
|
||||
LL | println!();
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: macro expansion ignores `{` and any tokens following
|
||||
--> $SRC_DIR/std/src/macros.rs:LL:COL
|
||||
|
|
||||
::: $DIR/main-alongside-macro-calls.rs:30:1
|
||||
|
|
||||
LL | println!();
|
||||
| ---------- caused by the macro expansion here
|
||||
|
|
||||
= note: the usage of `print!` is likely invalid in item context
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Couldn't compile the test.
|
||||
---- $DIR/main-alongside-macro-calls.rs - (line 33) stdout ----
|
||||
error: macros that expand to items must be delimited with braces or followed by a semicolon
|
||||
--> $DIR/main-alongside-macro-calls.rs:34:1
|
||||
|
|
||||
LL | println!();
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: macro expansion ignores `{` and any tokens following
|
||||
--> $SRC_DIR/std/src/macros.rs:LL:COL
|
||||
|
|
||||
::: $DIR/main-alongside-macro-calls.rs:34:1
|
||||
|
|
||||
LL | println!();
|
||||
| ---------- caused by the macro expansion here
|
||||
|
|
||||
= note: the usage of `print!` is likely invalid in item context
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Couldn't compile the test.
|
||||
|
||||
failures:
|
||||
$DIR/main-alongside-macro-calls.rs - (line 28)
|
||||
$DIR/main-alongside-macro-calls.rs - (line 33)
|
||||
|
||||
test result: FAILED. 2 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
running 4 tests
|
||||
test $DIR/main-alongside-macro-calls.rs - (line 19) ... ok
|
||||
test $DIR/main-alongside-macro-calls.rs - (line 24) ... ok
|
||||
test $DIR/main-alongside-macro-calls.rs - (line 28) - compile fail ... ok
|
||||
test $DIR/main-alongside-macro-calls.rs - (line 33) - compile fail ... ok
|
||||
|
||||
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
|
||||
44
tests/rustdoc-ui/doctest/main-alongside-macro-calls.rs
Normal file
44
tests/rustdoc-ui/doctest/main-alongside-macro-calls.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// This test ensures that if there is are any macro calls alongside a `main` function,
|
||||
// it will indeed consider the `main` function as the program entry point and *won't*
|
||||
// generate its own `main` function to wrap everything even though macro calls are
|
||||
// valid in statement contexts, too, and could just as well expand to statements or
|
||||
// expressions (we don't perform any macro expansion to find `main`, see also
|
||||
// <https://github.com/rust-lang/rust/issues/57415>).
|
||||
//
|
||||
// See <./main-alongside-stmts.rs> for comparison.
|
||||
//
|
||||
//@ compile-flags:--test --test-args --test-threads=1
|
||||
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
|
||||
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
|
||||
//@ revisions: pass fail
|
||||
//@[pass] check-pass
|
||||
//@[fail] failure-status: 101
|
||||
|
||||
// Regression test for <https://github.com/rust-lang/rust/pull/140220#issuecomment-2831872920>:
|
||||
|
||||
//! ```
|
||||
//! fn main() {}
|
||||
//! include!("./auxiliary/items.rs");
|
||||
//! ```
|
||||
//!
|
||||
//! ```
|
||||
//! include!("./auxiliary/items.rs");
|
||||
//! fn main() {}
|
||||
//! ```
|
||||
|
||||
// Regression test for <https://github.com/rust-lang/rust/issues/140412>:
|
||||
// We test the "same" thing twice: Once via `compile_fail` to more closely mirror the reported
|
||||
// regression and once without it to make sure that it leads to the expected rustc errors,
|
||||
// namely `println!(…)` not being valid in item contexts.
|
||||
|
||||
#![cfg_attr(pass, doc = " ```compile_fail")]
|
||||
#![cfg_attr(fail, doc = " ```")]
|
||||
//! fn main() {}
|
||||
//! println!();
|
||||
//! ```
|
||||
//!
|
||||
#![cfg_attr(pass, doc = " ```compile_fail")]
|
||||
#![cfg_attr(fail, doc = " ```")]
|
||||
//! println!();
|
||||
//! fn main() {}
|
||||
//! ```
|
||||
33
tests/rustdoc-ui/doctest/main-alongside-stmts.rs
Normal file
33
tests/rustdoc-ui/doctest/main-alongside-stmts.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// This test ensures that if there is are any statements alongside a `main` function,
|
||||
// it will not consider the `main` function as the program entry point but instead
|
||||
// will generate its own `main` function to wrap everything as it needs to reside in a
|
||||
// module where only *items* are permitted syntactically.
|
||||
//
|
||||
// See <./main-alongside-macro-calls.rs> for comparison.
|
||||
//
|
||||
// This is a regression test for:
|
||||
// * <https://github.com/rust-lang/rust/issues/140162>
|
||||
// * <https://github.com/rust-lang/rust/issues/139651>
|
||||
//
|
||||
//@ compile-flags:--test --test-args --test-threads=1
|
||||
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
|
||||
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
|
||||
//@ check-pass
|
||||
|
||||
//! ```
|
||||
//! # if cfg!(miri) { return; }
|
||||
//! use std::ops::Deref;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! assert!(false);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ```
|
||||
//! let x = 2;
|
||||
//! assert_eq!(x, 2);
|
||||
//!
|
||||
//! fn main() {
|
||||
//! assert!(false);
|
||||
//! }
|
||||
//! ```
|
||||
7
tests/rustdoc-ui/doctest/main-alongside-stmts.stdout
Normal file
7
tests/rustdoc-ui/doctest/main-alongside-stmts.stdout
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
running 2 tests
|
||||
test $DIR/main-alongside-stmts.rs - (line 17) ... ok
|
||||
test $DIR/main-alongside-stmts.rs - (line 26) ... ok
|
||||
|
||||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
|
||||
|
||||
|
|
@ -9,4 +9,9 @@ const fn print() {
|
|||
//~| ERROR cannot call non-const function `_print` in constant functions
|
||||
}
|
||||
|
||||
const fn format_args() {
|
||||
format_args!("{}", 0);
|
||||
//~^ ERROR cannot call non-const formatting macro in constant functions
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,14 @@ LL | println!("{:?}", 0);
|
|||
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error[E0015]: cannot call non-const formatting macro in constant functions
|
||||
--> $DIR/format.rs:13:5
|
||||
|
|
||||
LL | format_args!("{}", 0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0015`.
|
||||
|
|
|
|||
20
tests/ui/linking/cdylib-no-mangle.rs
Normal file
20
tests/ui/linking/cdylib-no-mangle.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//@ only-apple
|
||||
//@ build-fail
|
||||
//@ dont-check-compiler-stderr
|
||||
//@ dont-check-compiler-stdout
|
||||
|
||||
// Regression test for <https://github.com/rust-lang/rust/issues/139744>.
|
||||
// Functions in the dynamic library marked with no_mangle should not be GC-ed.
|
||||
|
||||
#![crate_type = "cdylib"]
|
||||
|
||||
unsafe extern "C" {
|
||||
unsafe static THIS_SYMBOL_SHOULD_BE_UNDEFINED: usize;
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe fn function_marked_with_no_mangle() {
|
||||
println!("FUNCTION_MARKED_WITH_NO_MANGLE = {}", unsafe { THIS_SYMBOL_SHOULD_BE_UNDEFINED });
|
||||
}
|
||||
|
||||
//~? ERROR linking
|
||||
27
tests/ui/linking/executable-no-mangle-strip.rs
Normal file
27
tests/ui/linking/executable-no-mangle-strip.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//@ run-pass
|
||||
//@ ignore-windows-gnu: only statics marked with used can be GC-ed on windows-gnu
|
||||
|
||||
// Regression test for <https://github.com/rust-lang/rust/issues/139744>.
|
||||
// Functions in the binary marked with no_mangle should be GC-ed if they
|
||||
// are not indirectly referenced by main.
|
||||
|
||||
#![feature(used_with_arg)]
|
||||
|
||||
#[cfg_attr(windows, link(name = "this_lib_does_not_exist", kind = "raw-dylib"))]
|
||||
unsafe extern "C" {
|
||||
unsafe static THIS_SYMBOL_SHOULD_BE_UNDEFINED: usize;
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe fn function_marked_with_no_mangle() {
|
||||
println!("FUNCTION_MARKED_WITH_NO_MANGLE = {}", unsafe { THIS_SYMBOL_SHOULD_BE_UNDEFINED });
|
||||
}
|
||||
|
||||
#[used(compiler)]
|
||||
pub static FUNCTION_MARKED_WITH_USED: unsafe fn() = || {
|
||||
println!("FUNCTION_MARKED_WITH_USED = {}", unsafe { THIS_SYMBOL_SHOULD_BE_UNDEFINED });
|
||||
};
|
||||
|
||||
fn main() {
|
||||
println!("MAIN");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue