diff options
366 files changed, 8821 insertions, 4789 deletions
diff --git a/.gitmodules b/.gitmodules index 8618f61f04a..d4a80efc277 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,7 +37,7 @@ [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/12.0-2021-02-03 + branch = rustc/12.0-2021-04-15 [submodule "src/doc/embedded-book"] path = src/doc/embedded-book url = https://github.com/rust-embedded/book.git diff --git a/Cargo.lock b/Cargo.lock index 2b7fbf1b647..8fec4bf128f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1909,9 +1909,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.88" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" dependencies = [ "rustc-std-workspace-core", ] @@ -2492,6 +2492,7 @@ dependencies = [ name = "panic_abort" version = "0.0.0" dependencies = [ + "alloc", "cfg-if 0.1.10", "compiler_builtins", "core", @@ -2875,9 +2876,9 @@ dependencies = [ [[package]] name = "racer" -version = "2.1.44" +version = "2.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7972a124e2b24dce35eb19f81eced829faec0e8227a7d744bbb1089934d05399" +checksum = "15408926f6207643150e0fc2c54a75a689b192df03ac6c59d42ea99c6782c7f7" dependencies = [ "bitflags", "clap", @@ -3117,6 +3118,7 @@ version = "1.41.0" dependencies = [ "anyhow", "cargo", + "cargo-util", "cargo_metadata 0.8.2", "clippy_lints", "crossbeam-channel", @@ -3262,18 +3264,19 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_arena" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93575affa286089b92c8208aea4e60fe9fdd251a619a09b566d6e4e2cc123212" +checksum = "259cca0e975ecb05fd289ace45280c30ff792efc04e856a7f18b7fc86a3cb610" dependencies = [ + "rustc-ap-rustc_data_structures", "smallvec 1.6.1", ] [[package]] name = "rustc-ap-rustc_ast" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c700f2d3b25aa8d6446dd2936048737b08b2d547bd86e2a70afa9fee4e9c522" +checksum = "bb9be435d50c88e94bbad6ea468c8680b52c5043bb298ab8058d05251717f8f8" dependencies = [ "bitflags", "rustc-ap-rustc_data_structures", @@ -3288,9 +3291,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_ast_passes" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e01f63e5259ee397bbe2e395d34a2e6b6b24f10c184d30fbbee1dcd7117f4f3" +checksum = "75246dd1a95a57f7767e53bde3971baa2d948078e180564709f5ea46cf863ddd" dependencies = [ "itertools 0.9.0", "rustc-ap-rustc_ast", @@ -3307,9 +3310,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_ast_pretty" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d644c69c55deb24257cb0cb5261265fe5134f6f545e9062e1c18b07e422c68" +checksum = "79bede0b44bed453fd0034b7ba492840391f6486bf3e17a1af12922f0b98d4cc" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_span", @@ -3318,9 +3321,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_attr" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "797fc68816d5396870f04e03d35164f5275d2502403239d4caec7ce063683f41" +checksum = "84a92a4a34b996694ca2dab70361c60d2d48c07adce57e8155b7ec75e069e3ea" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", @@ -3336,9 +3339,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_data_structures" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d840c4e6198b57982a54543ae604d634c7ceb7107f0c75970b88ebaff077ac5" +checksum = "9cbfa7f82517a1b2efe7106c864c3f930b1da8aff07a27fd317af2f36522fd2e" dependencies = [ "arrayvec", "bitflags", @@ -3367,9 +3370,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_errors" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f2f99bdc828ad417636d9016611dc9047b641fadcb7f533b8b0e9616d81f90b" +checksum = "58a272a5101843bcb40900cc9ccf80ecfec62830bb1f4a242986da4a34c0da89" dependencies = [ "annotate-snippets 0.8.0", "atty", @@ -3387,9 +3390,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_expand" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27008b4c7ded287bf5cb20b84d6d5a6566329140f2e2bc8f6e68b37a34898595" +checksum = "3bc7988f3facf2402fe057405ef0f7fbacc7e7a483da25e35a35ac09491fbbfb" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_passes", @@ -3411,9 +3414,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_feature" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb47b53670f1263ed1389dda932d5b5a6daf98579c1f076c2ee7d7f22709b7c" +checksum = "5e931cd1580ae60c5737d3fa57633034935e885414e794d83b3e52a81021985c" dependencies = [ "rustc-ap-rustc_data_structures", "rustc-ap-rustc_span", @@ -3421,21 +3424,21 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_fs_util" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdaddc4bae5ffab17037553e172f5014686db600050429aaa60aec14fe780e84" +checksum = "8fe9422e10d5b441d2a78202667bc85d7cf713a087b9ae6cdea0dfc825d79f07" [[package]] name = "rustc-ap-rustc_graphviz" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d73c72543311e88786f7380a3bfd946395579c1a0c0441a879a97fcdea79130" +checksum = "ffffffdef9fd51db69c1d4c045ced8aaab999be5627f2d3a0ce020d74c1f1e50" [[package]] name = "rustc-ap-rustc_index" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bba8d74ed4bad44a5b4264cf2a51ad0bd458ed56caa5bb090e989b8002ec6327" +checksum = "7f6f53afc4f7111c82295cb7ea3878f520bbac6a2c5a12e125b4ca9156498cff" dependencies = [ "arrayvec", "rustc-ap-rustc_macros", @@ -3444,18 +3447,18 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_lexer" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a030d00510966cd31e13dca5e6c1bd40d303a932c54eca40e854188bca8c49e" +checksum = "8056b05346dff7e39164d0434c6ec443a14ab5fbf6221bd1a56e5abbeae5f60c" dependencies = [ "unicode-xid", ] [[package]] name = "rustc-ap-rustc_lint_defs" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdff95da1b5d979183ef5c285817ba6cc67a1ac11296ef1e87b1b5bbaf57213c" +checksum = "364c3fb7b3cbdfe3fbb21d4078ff2cb3c58df63cda27995f8b064d21ee6dede5" dependencies = [ "rustc-ap-rustc_ast", "rustc-ap-rustc_data_structures", @@ -3468,9 +3471,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_macros" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3ed7401bf6f5a256d58cd0e1c1e2e77eec25e60a0d7ad75313962edcb4e396" +checksum = "4607d6879cae3bae4d0369ca4b3a7510fd6295ac32eec088ac975208ba96ca45" dependencies = [ "proc-macro2", "quote", @@ -3480,9 +3483,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_parse" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "609a624baffa3f99847d57d30c96ee6732ce0912f8df4be239b6fd91533910d6" +checksum = "78d22889bff7ca2346037c9df7ea55c66ffb714f5b50fb62b41975f8ac7a2d70" dependencies = [ "bitflags", "rustc-ap-rustc_ast", @@ -3500,9 +3503,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_serialize" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc232e2a351d8131c8f1386ce372ee22ef7b1b0b897bbf817a8ce4792029a564" +checksum = "d33c710120953c0214f47a6caf42064d7e241003b4af36c98a6d6156e70335f1" dependencies = [ "indexmap", "smallvec 1.6.1", @@ -3510,9 +3513,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_session" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18acf94c820cd0c64ee1cbd811fd1f4d5ba18987c457c88771359b90cb1a12f5" +checksum = "6d35919041429a90713c8f704fa5209ba159cb554ce74d95722cbc18ac4b4c6f" dependencies = [ "bitflags", "getopts", @@ -3532,9 +3535,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_span" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3479f453a38b6a5572938d035fc2b3cb6ec379c57f598b8682b512eb90c7858" +checksum = "73b12170c69603c0bf4b50e5c25fd348aae13b8c6465aa0ef4389c9eaa568e51" dependencies = [ "cfg-if 0.1.10", "md-5", @@ -3552,9 +3555,9 @@ dependencies = [ [[package]] name = "rustc-ap-rustc_target" -version = "705.0.0" +version = "712.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cacaf829778cf07bb97a9f4604896789de12392175f3743e74a30ed370f1c1" +checksum = "0a8329d92e7dc24b974f759e6c6e97e2bbc47b18d0573343028f8135ca367200" dependencies = [ "bitflags", "rustc-ap-rustc_data_structures", @@ -4662,7 +4665,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.4.36" +version = "1.4.37" dependencies = [ "annotate-snippets 0.6.1", "anyhow", @@ -4680,7 +4683,6 @@ dependencies = [ "regex", "rustc-ap-rustc_ast", "rustc-ap-rustc_ast_pretty", - "rustc-ap-rustc_attr", "rustc-ap-rustc_data_structures", "rustc-ap-rustc_errors", "rustc-ap-rustc_expand", @@ -5032,11 +5034,23 @@ dependencies = [ "profiler_builtins", "rand 0.7.3", "rustc-demangle", + "std_detect", "unwind", "wasi", ] [[package]] +name = "std_detect" +version = "0.1.5" +dependencies = [ + "cfg-if 0.1.10", + "compiler_builtins", + "libc", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] name = "string_cache" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index f961d3e9b97..02011357eac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,8 @@ exclude = [ # not all `Cargo.toml` files are available, so we exclude the `x` binary, # so it can be invoked before the current checkout is set up. "src/tools/x", + # stdarch has its own Cargo workspace + "library/stdarch", ] [profile.release.package.compiler_builtins] @@ -88,6 +90,7 @@ object.debug = 0 # vendored copy. [patch."https://github.com/rust-lang/cargo"] cargo = { path = "src/tools/cargo" } +cargo-util = { path = "src/tools/cargo/crates/cargo-util" } [patch."https://github.com/rust-lang/rustfmt"] # Similar to Cargo above we want the RLS to use a vendored version of `rustfmt` diff --git a/RELEASES.md b/RELEASES.md index c6c0007d115..024610bc7a4 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -50,6 +50,8 @@ Libraries - [`io::Empty` now implements `io::Seek`.][78044] - [`rc::Weak<T>` and `sync::Weak<T>`'s methods such as `as_ptr` are now implemented for `T: ?Sized` types.][80764] +- [`Div` and `Rem` by their `NonZero` variant is now implemented for all unsigned integers.][79134] + Stabilized APIs --------------- @@ -72,6 +74,8 @@ Stabilized APIs - [`str::split_inclusive`] - [`sync::OnceState`] - [`task::Wake`] +- [`VecDeque::range`] +- [`VecDeque::range_mut`] Cargo ----- @@ -115,6 +119,7 @@ Compatibility Notes - `thumbv7neon-unknown-linux-gnueabihf` - `armv7-unknown-linux-gnueabi` - `x86_64-unknown-linux-gnux32` +- [`atomic::spin_loop_hint` has been deprecated.][80966] It's recommended to use `hint::spin_loop` instead. Internal Only ------------- @@ -145,6 +150,8 @@ Internal Only [80764]: https://github.com/rust-lang/rust/pull/80764 [80749]: https://github.com/rust-lang/rust/pull/80749 [80662]: https://github.com/rust-lang/rust/pull/80662 +[79134]: https://github.com/rust-lang/rust/pull/79134 +[80966]: https://github.com/rust-lang/rust/pull/80966 [cargo/8997]: https://github.com/rust-lang/cargo/pull/8997 [cargo/9112]: https://github.com/rust-lang/cargo/pull/9112 [feature-resolver@2.0]: https://doc.rust-lang.org/nightly/cargo/reference/features.html#feature-resolver-version-2 @@ -166,6 +173,8 @@ Internal Only [`Seek::stream_position`]: https://doc.rust-lang.org/nightly/std/io/trait.Seek.html#method.stream_position [`Peekable::next_if`]: https://doc.rust-lang.org/nightly/std/iter/struct.Peekable.html#method.next_if [`Peekable::next_if_eq`]: https://doc.rust-lang.org/nightly/std/iter/struct.Peekable.html#method.next_if_eq +[`VecDeque::range`]: https://doc.rust-lang.org/nightly/std/collections/struct.VecDeque.html#method.range +[`VecDeque::range_mut`]: https://doc.rust-lang.org/nightly/std/collections/struct.VecDeque.html#method.range_mut Version 1.50.0 (2021-02-11) ============================ diff --git a/compiler/rustc_ast/src/ast_like.rs b/compiler/rustc_ast/src/ast_like.rs index 63bc7c49a99..945a44ab663 100644 --- a/compiler/rustc_ast/src/ast_like.rs +++ b/compiler/rustc_ast/src/ast_like.rs @@ -1,20 +1,32 @@ use super::ptr::P; +use super::token::Nonterminal; use super::tokenstream::LazyTokenStream; use super::{Arm, ExprField, FieldDef, GenericParam, Param, PatField, Variant}; -use super::{AssocItem, Expr, ForeignItem, Item, Local}; +use super::{AssocItem, Expr, ForeignItem, Item, Local, MacCallStmt}; use super::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility}; use super::{AttrVec, Attribute, Stmt, StmtKind}; +use std::fmt::Debug; + /// An `AstLike` represents an AST node (or some wrapper around /// and AST node) which stores some combination of attributes /// and tokens. -pub trait AstLike: Sized { +pub trait AstLike: Sized + Debug { + /// This is `true` if this `AstLike` might support 'custom' (proc-macro) inner + /// attributes. Attributes like `#![cfg]` and `#![cfg_attr]` are not + /// considered 'custom' attributes + /// + /// If this is `false`, then this `AstLike` definitely does + /// not support 'custom' inner attributes, which enables some optimizations + /// during token collection. + const SUPPORTS_CUSTOM_INNER_ATTRS: bool; fn attrs(&self) -> &[Attribute]; fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)); fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>>; } impl<T: AstLike + 'static> AstLike for P<T> { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; fn attrs(&self) -> &[Attribute] { (**self).attrs() } @@ -26,6 +38,55 @@ impl<T: AstLike + 'static> AstLike for P<T> { } } +impl AstLike for crate::token::Nonterminal { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; + fn attrs(&self) -> &[Attribute] { + match self { + Nonterminal::NtItem(item) => item.attrs(), + Nonterminal::NtStmt(stmt) => stmt.attrs(), + Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.attrs(), + Nonterminal::NtPat(_) + | Nonterminal::NtTy(_) + | Nonterminal::NtMeta(_) + | Nonterminal::NtPath(_) + | Nonterminal::NtVis(_) + | Nonterminal::NtTT(_) + | Nonterminal::NtBlock(_) + | Nonterminal::NtIdent(..) + | Nonterminal::NtLifetime(_) => &[], + } + } + fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { + match self { + Nonterminal::NtItem(item) => item.visit_attrs(f), + Nonterminal::NtStmt(stmt) => stmt.visit_attrs(f), + Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.visit_attrs(f), + Nonterminal::NtPat(_) + | Nonterminal::NtTy(_) + | Nonterminal::NtMeta(_) + | Nonterminal::NtPath(_) + | Nonterminal::NtVis(_) + | Nonterminal::NtTT(_) + | Nonterminal::NtBlock(_) + | Nonterminal::NtIdent(..) + | Nonterminal::NtLifetime(_) => {} + } + } + fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { + match self { + Nonterminal::NtItem(item) => item.tokens_mut(), + Nonterminal::NtStmt(stmt) => stmt.tokens_mut(), + Nonterminal::NtExpr(expr) | Nonterminal::NtLiteral(expr) => expr.tokens_mut(), + Nonterminal::NtPat(pat) => pat.tokens_mut(), + Nonterminal::NtTy(ty) => ty.tokens_mut(), + Nonterminal::NtMeta(attr_item) => attr_item.tokens_mut(), + Nonterminal::NtPath(path) => path.tokens_mut(), + Nonterminal::NtVis(vis) => vis.tokens_mut(), + _ => panic!("Called tokens_mut on {:?}", self), + } + } +} + fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec<Attribute>)) { crate::mut_visit::visit_clobber(attrs, |attrs| { let mut vec = attrs.into(); @@ -35,6 +96,10 @@ fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec<Attribute>)) { } impl AstLike for StmtKind { + // This might be an `StmtKind::Item`, which contains + // an item that supports inner attrs + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; + fn attrs(&self) -> &[Attribute] { match self { StmtKind::Local(local) => local.attrs(), @@ -66,6 +131,8 @@ impl AstLike for StmtKind { } impl AstLike for Stmt { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = StmtKind::SUPPORTS_CUSTOM_INNER_ATTRS; + fn attrs(&self) -> &[Attribute] { self.kind.attrs() } @@ -79,6 +146,8 @@ impl AstLike for Stmt { } impl AstLike for Attribute { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + fn attrs(&self) -> &[Attribute] { &[] } @@ -94,6 +163,8 @@ impl AstLike for Attribute { } impl<T: AstLike> AstLike for Option<T> { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; + fn attrs(&self) -> &[Attribute] { self.as_ref().map(|inner| inner.attrs()).unwrap_or(&[]) } @@ -127,8 +198,13 @@ impl VecOrAttrVec for AttrVec { } macro_rules! derive_has_tokens_and_attrs { - ($($ty:path),*) => { $( + ( + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner_attrs:literal; + $($ty:path),* + ) => { $( impl AstLike for $ty { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner_attrs; + fn attrs(&self) -> &[Attribute] { &self.attrs } @@ -140,6 +216,7 @@ macro_rules! derive_has_tokens_and_attrs { fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { Some(&mut self.tokens) } + } )* } } @@ -147,6 +224,8 @@ macro_rules! derive_has_tokens_and_attrs { macro_rules! derive_has_attrs_no_tokens { ($($ty:path),*) => { $( impl AstLike for $ty { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + fn attrs(&self) -> &[Attribute] { &self.attrs } @@ -165,12 +244,13 @@ macro_rules! derive_has_attrs_no_tokens { macro_rules! derive_has_tokens_no_attrs { ($($ty:path),*) => { $( impl AstLike for $ty { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + fn attrs(&self) -> &[Attribute] { &[] } fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec<Attribute>)) {} - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { Some(&mut self.tokens) } @@ -178,10 +258,18 @@ macro_rules! derive_has_tokens_no_attrs { )* } } -// These AST nodes support both inert and active -// attributes, so they also have tokens. +// These ast nodes support both active and inert attributes, +// so they have tokens collected to pass to proc macros +derive_has_tokens_and_attrs! { + // Both `Item` and `AssocItem` can have bodies, which + // can contain inner attributes + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true; + Item, AssocItem, ForeignItem +} + derive_has_tokens_and_attrs! { - Item, Expr, Local, AssocItem, ForeignItem + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + Local, MacCallStmt, Expr } // These ast nodes only support inert attributes, so they don't diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 0fbe4d0120c..41121d095f3 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -6,7 +6,9 @@ use crate::ast::{Lit, LitKind}; use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem}; use crate::ast::{Path, PathSegment}; use crate::token::{self, CommentKind, Token}; -use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree, TreeAndSpacing}; +use crate::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; +use crate::tokenstream::{DelimSpan, Spacing, TokenTree, TreeAndSpacing}; +use crate::tokenstream::{LazyTokenStream, TokenStream}; use rustc_index::bit_set::GrowableBitSet; use rustc_span::source_map::BytePos; @@ -268,14 +270,18 @@ impl Attribute { } } - pub fn tokens(&self) -> TokenStream { + pub fn tokens(&self) -> AttrAnnotatedTokenStream { match self.kind { AttrKind::Normal(_, ref tokens) => tokens .as_ref() .unwrap_or_else(|| panic!("attribute is missing tokens: {:?}", self)) .create_token_stream(), - AttrKind::DocComment(comment_kind, data) => TokenStream::from(TokenTree::Token( - Token::new(token::DocComment(comment_kind, self.style, data), self.span), + AttrKind::DocComment(comment_kind, data) => AttrAnnotatedTokenStream::from(( + AttrAnnotatedTokenTree::Token(Token::new( + token::DocComment(comment_kind, self.style, data), + self.span, + )), + Spacing::Alone, )), } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index b1840f475aa..05f57f978c7 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -631,6 +631,33 @@ pub fn noop_flat_map_param<T: MutVisitor>(mut param: Param, vis: &mut T) -> Smal } // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. +pub fn visit_attr_annotated_tt<T: MutVisitor>(tt: &mut AttrAnnotatedTokenTree, vis: &mut T) { + match tt { + AttrAnnotatedTokenTree::Token(token) => { + visit_token(token, vis); + } + AttrAnnotatedTokenTree::Delimited(DelimSpan { open, close }, _delim, tts) => { + vis.visit_span(open); + vis.visit_span(close); + visit_attr_annotated_tts(tts, vis); + } + AttrAnnotatedTokenTree::Attributes(data) => { + for attr in &mut *data.attrs { + match &mut attr.kind { + AttrKind::Normal(_, attr_tokens) => { + visit_lazy_tts(attr_tokens, vis); + } + AttrKind::DocComment(..) => { + vis.visit_span(&mut attr.span); + } + } + } + visit_lazy_tts_opt_mut(Some(&mut data.tokens), vis); + } + } +} + +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. pub fn visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) { match tt { TokenTree::Token(token) => { @@ -652,16 +679,30 @@ pub fn visit_tts<T: MutVisitor>(TokenStream(tts): &mut TokenStream, vis: &mut T) } } -pub fn visit_lazy_tts<T: MutVisitor>(lazy_tts: &mut Option<LazyTokenStream>, vis: &mut T) { +pub fn visit_attr_annotated_tts<T: MutVisitor>( + AttrAnnotatedTokenStream(tts): &mut AttrAnnotatedTokenStream, + vis: &mut T, +) { + if vis.token_visiting_enabled() && !tts.is_empty() { + let tts = Lrc::make_mut(tts); + visit_vec(tts, |(tree, _is_joint)| visit_attr_annotated_tt(tree, vis)); + } +} + +pub fn visit_lazy_tts_opt_mut<T: MutVisitor>(lazy_tts: Option<&mut LazyTokenStream>, vis: &mut T) { if vis.token_visiting_enabled() { - visit_opt(lazy_tts, |lazy_tts| { + if let Some(lazy_tts) = lazy_tts { let mut tts = lazy_tts.create_token_stream(); - visit_tts(&mut tts, vis); + visit_attr_annotated_tts(&mut tts, vis); *lazy_tts = LazyTokenStream::new(tts); - }) + } } } +pub fn visit_lazy_tts<T: MutVisitor>(lazy_tts: &mut Option<LazyTokenStream>, vis: &mut T) { + visit_lazy_tts_opt_mut(lazy_tts.as_mut(), vis); +} + // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. // Applies ident visitor if it's an ident; applies other visits to interpolated nodes. // In practice the ident part is not actually used by specific visitors right now, diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 1c26668779f..8318b242cae 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -14,6 +14,7 @@ //! ownership of the original. use crate::token::{self, DelimToken, Token, TokenKind}; +use crate::AttrVec; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{self, Lrc}; @@ -123,11 +124,11 @@ where } pub trait CreateTokenStream: sync::Send + sync::Sync { - fn create_token_stream(&self) -> TokenStream; + fn create_token_stream(&self) -> AttrAnnotatedTokenStream; } -impl CreateTokenStream for TokenStream { - fn create_token_stream(&self) -> TokenStream { +impl CreateTokenStream for AttrAnnotatedTokenStream { + fn create_token_stream(&self) -> AttrAnnotatedTokenStream { self.clone() } } @@ -143,14 +144,14 @@ impl LazyTokenStream { LazyTokenStream(Lrc::new(Box::new(inner))) } - pub fn create_token_stream(&self) -> TokenStream { + pub fn create_token_stream(&self) -> AttrAnnotatedTokenStream { self.0.create_token_stream() } } impl fmt::Debug for LazyTokenStream { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt("LazyTokenStream", f) + write!(f, "LazyTokenStream({:?})", self.create_token_stream()) } } @@ -173,6 +174,145 @@ impl<CTX> HashStable<CTX> for LazyTokenStream { } } +/// A `AttrAnnotatedTokenStream` is similar to a `TokenStream`, but with extra +/// information about the tokens for attribute targets. This is used +/// during expansion to perform early cfg-expansion, and to process attributes +/// during proc-macro invocations. +#[derive(Clone, Debug, Default, Encodable, Decodable)] +pub struct AttrAnnotatedTokenStream(pub Lrc<Vec<(AttrAnnotatedTokenTree, Spacing)>>); + +/// Like `TokenTree`, but for `AttrAnnotatedTokenStream` +#[derive(Clone, Debug, Encodable, Decodable)] +pub enum AttrAnnotatedTokenTree { + Token(Token), + Delimited(DelimSpan, DelimToken, AttrAnnotatedTokenStream), + /// Stores the attributes for an attribute target, + /// along with the tokens for that attribute target. + /// See `AttributesData` for more information + Attributes(AttributesData), +} + +impl AttrAnnotatedTokenStream { + pub fn new(tokens: Vec<(AttrAnnotatedTokenTree, Spacing)>) -> AttrAnnotatedTokenStream { + AttrAnnotatedTokenStream(Lrc::new(tokens)) + } + + /// Converts this `AttrAnnotatedTokenStream` to a plain `TokenStream + /// During conversion, `AttrAnnotatedTokenTree::Attributes` get 'flattened' + /// back to a `TokenStream` of the form `outer_attr attr_target`. + /// If there are inner attributes, they are inserted into the proper + /// place in the attribute target tokens. + pub fn to_tokenstream(&self) -> TokenStream { + let trees: Vec<_> = self + .0 + .iter() + .flat_map(|tree| match &tree.0 { + AttrAnnotatedTokenTree::Token(inner) => { + smallvec![(TokenTree::Token(inner.clone()), tree.1)].into_iter() + } + AttrAnnotatedTokenTree::Delimited(span, delim, stream) => smallvec![( + TokenTree::Delimited(*span, *delim, stream.to_tokenstream()), + tree.1, + )] + .into_iter(), + AttrAnnotatedTokenTree::Attributes(data) => { + let mut outer_attrs = Vec::new(); + let mut inner_attrs = Vec::new(); + let attrs: Vec<_> = data.attrs.clone().into(); + for attr in attrs { + match attr.style { + crate::AttrStyle::Outer => { + assert!( + inner_attrs.len() == 0, + "Found outer attribute {:?} after inner attrs {:?}", + attr, + inner_attrs + ); + outer_attrs.push(attr); + } + crate::AttrStyle::Inner => { + inner_attrs.push(attr); + } + } + } + + let mut target_tokens: Vec<_> = data + .tokens + .create_token_stream() + .to_tokenstream() + .0 + .iter() + .cloned() + .collect(); + if !inner_attrs.is_empty() { + let mut found = false; + // Check the last two trees (to account for a trailing semi) + for (tree, _) in target_tokens.iter_mut().rev().take(2) { + if let TokenTree::Delimited(span, delim, delim_tokens) = tree { + // Inner attributes are only supported on extern blocks, functions, impls, + // and modules. All of these have their inner attributes placed at + // the beginning of the rightmost outermost braced group: + // e.g. fn foo() { #![my_attr} } + // + // Therefore, we can insert them back into the right location + // without needing to do any extra position tracking. + // + // Note: Outline modules are an exception - they can + // have attributes like `#![my_attr]` at the start of a file. + // Support for custom attributes in this position is not + // properly implemented - we always synthesize fake tokens, + // so we never reach this code. + + let mut builder = TokenStreamBuilder::new(); + for inner_attr in &inner_attrs { + builder.push(inner_attr.tokens().to_tokenstream()); + } + builder.push(delim_tokens.clone()); + *tree = TokenTree::Delimited(*span, *delim, builder.build()); + found = true; + break; + } + } + + assert!( + found, + "Failed to find trailing delimited group in: {:?}", + target_tokens + ); + } + let mut flat: SmallVec<[_; 1]> = SmallVec::new(); + for attr in outer_attrs { + // FIXME: Make this more efficient + flat.extend(attr.tokens().to_tokenstream().0.clone().iter().cloned()); + } + flat.extend(target_tokens); + flat.into_iter() + } + }) + .collect(); + TokenStream::new(trees) + } +} + +/// Stores the tokens for an attribute target, along +/// with its attributes. +/// +/// This is constructed during parsing when we need to capture +/// tokens. +/// +/// For example, `#[cfg(FALSE)] struct Foo {}` would +/// have an `attrs` field contaiing the `#[cfg(FALSE)]` attr, +/// and a `tokens` field storing the (unparesd) tokens `struct Foo {}` +#[derive(Clone, Debug, Encodable, Decodable)] +pub struct AttributesData { + /// Attributes, both outer and inner. + /// These are stored in the original order that they were parsed in. + pub attrs: AttrVec, + /// The underlying tokens for the attribute target that `attrs` + /// are applied to + pub tokens: LazyTokenStream, +} + /// A `TokenStream` is an abstract sequence of tokens, organized into [`TokenTree`]s. /// /// The goal is for procedural macros to work with `TokenStream`s and `TokenTree`s @@ -235,6 +375,12 @@ impl TokenStream { } } +impl From<(AttrAnnotatedTokenTree, Spacing)> for AttrAnnotatedTokenStream { + fn from((tree, spacing): (AttrAnnotatedTokenTree, Spacing)) -> AttrAnnotatedTokenStream { + AttrAnnotatedTokenStream::new(vec![(tree, spacing)]) + } +} + impl From<TokenTree> for TokenStream { fn from(tree: TokenTree) -> TokenStream { TokenStream::new(vec![(tree, Spacing::Alone)]) @@ -457,6 +603,10 @@ impl Cursor { } } + pub fn index(&self) -> usize { + self.index + } + pub fn append(&mut self, new_stream: TokenStream) { if new_stream.is_empty() { return; diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index cd5d116964f..44056df4ab9 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -37,8 +37,8 @@ #![recursion_limit = "256"] use rustc_ast::node_id::NodeMap; -use rustc_ast::token::{self, DelimToken, Nonterminal, Token}; -use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, DelimSpan, TokenStream, TokenTree}; +use rustc_ast::token::{self, Token}; +use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree}; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast::walk_list; use rustc_ast::{self as ast, *}; @@ -56,7 +56,7 @@ use rustc_hir::{ConstArg, GenericArg, ParamName}; use rustc_index::vec::{Idx, IndexVec}; use rustc_session::lint::builtin::{BARE_TRAIT_OBJECTS, MISSING_ABI}; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; -use rustc_session::parse::ParseSess; +use rustc_session::utils::{FlattenNonterminals, NtToTokenstream}; use rustc_session::Session; use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{respan, DesugaringKind}; @@ -213,8 +213,6 @@ pub trait ResolverAstLowering { ) -> LocalDefId; } -type NtToTokenstream = fn(&Nonterminal, &ParseSess, CanSynthesizeMissingTokens) -> TokenStream; - /// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree, /// and if so, what meaning it has. #[derive(Debug)] @@ -403,42 +401,6 @@ enum AnonymousLifetimeMode { PassThrough, } -struct TokenStreamLowering<'a> { - parse_sess: &'a ParseSess, - synthesize_tokens: CanSynthesizeMissingTokens, - nt_to_tokenstream: NtToTokenstream, -} - -impl<'a> TokenStreamLowering<'a> { - fn lower_token_stream(&mut self, tokens: TokenStream) -> TokenStream { - tokens.into_trees().flat_map(|tree| self.lower_token_tree(tree).into_trees()).collect() - } - - fn lower_token_tree(&mut self, tree: TokenTree) -> TokenStream { - match tree { - TokenTree::Token(token) => self.lower_token(token), - TokenTree::Delimited(span, delim, tts) => { - TokenTree::Delimited(span, delim, self.lower_token_stream(tts)).into() - } - } - } - - fn lower_token(&mut self, token: Token) -> TokenStream { - match token.kind { - token::Interpolated(nt) => { - let tts = (self.nt_to_tokenstream)(&nt, self.parse_sess, self.synthesize_tokens); - TokenTree::Delimited( - DelimSpan::from_single(token.span), - DelimToken::NoDelim, - self.lower_token_stream(tts), - ) - .into() - } - _ => TokenTree::Token(token).into(), - } - } -} - impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_crate(mut self, c: &Crate) -> hir::Crate<'hir> { /// Full-crate AST visitor that inserts into a fresh @@ -1037,12 +999,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - let tokens = TokenStreamLowering { + let tokens = FlattenNonterminals { parse_sess: &self.sess.parse_sess, synthesize_tokens: CanSynthesizeMissingTokens::Yes, nt_to_tokenstream: self.nt_to_tokenstream, } - .lower_token(token.clone()); + .process_token(token.clone()); MacArgs::Eq(eq_span, unwrap_single_token(self.sess, tokens, token.span)) } } @@ -1053,12 +1015,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { tokens: TokenStream, synthesize_tokens: CanSynthesizeMissingTokens, ) -> TokenStream { - TokenStreamLowering { + FlattenNonterminals { parse_sess: &self.sess.parse_sess, synthesize_tokens, nt_to_tokenstream: self.nt_to_tokenstream, } - .lower_token_stream(tokens) + .process_token_stream(tokens) } /// Given an associated type constraint like one of these: diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 87c4bfae1af..f6b18bdea51 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -366,16 +366,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { over time" ); } - if self.sess.contains_name(&i.attrs[..], sym::main) { - gate_feature_post!( - &self, - main, - i.span, - "declaration of a non-standard `#[main]` \ - function may change over time, for now \ - a top-level `fn main()` is required" - ); - } } ast::ItemKind::Struct(..) => { diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 025872df017..79dc857074d 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -1,11 +1,18 @@ use crate::util::check_builtin_macro_attribute; -use rustc_ast::mut_visit::{self, MutVisitor}; -use rustc_ast::ptr::P; -use rustc_ast::{self as ast, AstLike}; +use rustc_ast as ast; +use rustc_ast::mut_visit::MutVisitor; +use rustc_ast::tokenstream::CanSynthesizeMissingTokens; +use rustc_ast::visit::Visitor; +use rustc_ast::{mut_visit, visit}; +use rustc_ast::{AstLike, Attribute}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_expand::config::StripUnconfigured; use rustc_expand::configure; +use rustc_parse::parser::ForceCollect; +use rustc_session::utils::FlattenNonterminals; + +use rustc_ast::ptr::P; use rustc_span::symbol::sym; use rustc_span::Span; use smallvec::SmallVec; @@ -22,74 +29,179 @@ crate fn expand( crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Vec<Annotatable> { let mut visitor = CfgEval { - cfg: StripUnconfigured { sess: ecx.sess, features: ecx.ecfg.features, modified: false }, + cfg: &mut StripUnconfigured { + sess: ecx.sess, + features: ecx.ecfg.features, + config_tokens: true, + }, }; - let mut annotatable = visitor.configure_annotatable(annotatable); - if visitor.cfg.modified { - // Erase the tokens if cfg-stripping modified the item - // This will cause us to synthesize fake tokens - // when `nt_to_tokenstream` is called on this item. - if let Some(tokens) = annotatable.tokens_mut() { - *tokens = None; + let annotatable = visitor.configure_annotatable(annotatable); + vec![annotatable] +} + +struct CfgEval<'a, 'b> { + cfg: &'a mut StripUnconfigured<'b>, +} + +fn flat_map_annotatable(vis: &mut impl MutVisitor, annotatable: Annotatable) -> Annotatable { + // Since the item itself has already been configured by the InvocationCollector, + // we know that fold result vector will contain exactly one element + match annotatable { + Annotatable::Item(item) => Annotatable::Item(vis.flat_map_item(item).pop().unwrap()), + Annotatable::TraitItem(item) => { + Annotatable::TraitItem(vis.flat_map_trait_item(item).pop().unwrap()) + } + Annotatable::ImplItem(item) => { + Annotatable::ImplItem(vis.flat_map_impl_item(item).pop().unwrap()) + } + Annotatable::ForeignItem(item) => { + Annotatable::ForeignItem(vis.flat_map_foreign_item(item).pop().unwrap()) } + Annotatable::Stmt(stmt) => { + Annotatable::Stmt(stmt.map(|stmt| vis.flat_map_stmt(stmt).pop().unwrap())) + } + Annotatable::Expr(mut expr) => Annotatable::Expr({ + vis.visit_expr(&mut expr); + expr + }), + Annotatable::Arm(arm) => Annotatable::Arm(vis.flat_map_arm(arm).pop().unwrap()), + Annotatable::ExprField(field) => { + Annotatable::ExprField(vis.flat_map_expr_field(field).pop().unwrap()) + } + Annotatable::PatField(fp) => { + Annotatable::PatField(vis.flat_map_pat_field(fp).pop().unwrap()) + } + Annotatable::GenericParam(param) => { + Annotatable::GenericParam(vis.flat_map_generic_param(param).pop().unwrap()) + } + Annotatable::Param(param) => Annotatable::Param(vis.flat_map_param(param).pop().unwrap()), + Annotatable::FieldDef(sf) => { + Annotatable::FieldDef(vis.flat_map_field_def(sf).pop().unwrap()) + } + Annotatable::Variant(v) => Annotatable::Variant(vis.flat_map_variant(v).pop().unwrap()), + } +} + +struct CfgFinder { + has_cfg_or_cfg_attr: bool, +} + +impl CfgFinder { + fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool { + let mut finder = CfgFinder { has_cfg_or_cfg_attr: false }; + match annotatable { + Annotatable::Item(item) => finder.visit_item(&item), + Annotatable::TraitItem(item) => finder.visit_assoc_item(&item, visit::AssocCtxt::Trait), + Annotatable::ImplItem(item) => finder.visit_assoc_item(&item, visit::AssocCtxt::Impl), + Annotatable::ForeignItem(item) => finder.visit_foreign_item(&item), + Annotatable::Stmt(stmt) => finder.visit_stmt(&stmt), + Annotatable::Expr(expr) => finder.visit_expr(&expr), + Annotatable::Arm(arm) => finder.visit_arm(&arm), + Annotatable::ExprField(field) => finder.visit_expr_field(&field), + Annotatable::PatField(field) => finder.visit_pat_field(&field), + Annotatable::GenericParam(param) => finder.visit_generic_param(¶m), + Annotatable::Param(param) => finder.visit_param(¶m), + Annotatable::FieldDef(field) => finder.visit_field_def(&field), + Annotatable::Variant(variant) => finder.visit_variant(&variant), + }; + finder.has_cfg_or_cfg_attr } - vec![annotatable] } -struct CfgEval<'a> { - cfg: StripUnconfigured<'a>, +impl<'ast> visit::Visitor<'ast> for CfgFinder { + fn visit_attribute(&mut self, attr: &'ast Attribute) { + // We want short-circuiting behavior, so don't use the '|=' operator. + self.has_cfg_or_cfg_attr = self.has_cfg_or_cfg_attr + || attr + .ident() + .map_or(false, |ident| ident.name == sym::cfg || ident.name == sym::cfg_attr); + } } -impl CfgEval<'_> { +impl CfgEval<'_, '_> { fn configure<T: AstLike>(&mut self, node: T) -> Option<T> { self.cfg.configure(node) } - fn configure_annotatable(&mut self, annotatable: Annotatable) -> Annotatable { - // Since the item itself has already been configured by the InvocationCollector, - // we know that fold result vector will contain exactly one element - match annotatable { - Annotatable::Item(item) => Annotatable::Item(self.flat_map_item(item).pop().unwrap()), - Annotatable::TraitItem(item) => { - Annotatable::TraitItem(self.flat_map_trait_item(item).pop().unwrap()) - } - Annotatable::ImplItem(item) => { - Annotatable::ImplItem(self.flat_map_impl_item(item).pop().unwrap()) - } - Annotatable::ForeignItem(item) => { - Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap()) - } - Annotatable::Stmt(stmt) => { - Annotatable::Stmt(stmt.map(|stmt| self.flat_map_stmt(stmt).pop().unwrap())) - } - Annotatable::Expr(mut expr) => Annotatable::Expr({ - self.visit_expr(&mut expr); - expr - }), - Annotatable::Arm(arm) => Annotatable::Arm(self.flat_map_arm(arm).pop().unwrap()), - Annotatable::ExprField(field) => { - Annotatable::ExprField(self.flat_map_expr_field(field).pop().unwrap()) - } - Annotatable::PatField(fp) => { - Annotatable::PatField(self.flat_map_pat_field(fp).pop().unwrap()) - } - Annotatable::GenericParam(param) => { - Annotatable::GenericParam(self.flat_map_generic_param(param).pop().unwrap()) - } - Annotatable::Param(param) => { - Annotatable::Param(self.flat_map_param(param).pop().unwrap()) - } - Annotatable::FieldDef(sf) => { - Annotatable::FieldDef(self.flat_map_field_def(sf).pop().unwrap()) + pub fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Annotatable { + // Tokenizing and re-parsing the `Annotatable` can have a significant + // performance impact, so try to avoid it if possible + if !CfgFinder::has_cfg_or_cfg_attr(&annotatable) { + return annotatable; + } + + // The majority of parsed attribute targets will never need to have early cfg-expansion + // run (e.g. they are not part of a `#[derive]` or `#[cfg_eval]` macro inoput). + // Therefore, we normally do not capture the necessary information about `#[cfg]` + // and `#[cfg_attr]` attributes during parsing. + // + // Therefore, when we actually *do* run early cfg-expansion, we need to tokenize + // and re-parse the attribute target, this time capturing information about + // the location of `#[cfg]` and `#[cfg_attr]` in the token stream. The tokenization + // process is lossless, so this process is invisible to proc-macros. + + // FIXME - get rid of this clone + let nt = annotatable.clone().into_nonterminal(); + + let mut orig_tokens = rustc_parse::nt_to_tokenstream( + &nt, + &self.cfg.sess.parse_sess, + CanSynthesizeMissingTokens::No, + ); + + // 'Flatten' all nonterminals (i.e. `TokenKind::Interpolated`) + // to `None`-delimited groups containing the corresponding tokens. This + // is normally delayed until the proc-macro server actually needs to + // provide a `TokenKind::Interpolated` to a proc-macro. We do this earlier, + // so that we can handle cases like: + // + // ```rust + // #[cfg_eval] #[cfg] $item + //``` + // + // where `$item` is `#[cfg_attr] struct Foo {}`. We want to make + // sure to evaluate *all* `#[cfg]` and `#[cfg_attr]` attributes - the simplest + // way to do this is to do a single parse of a stream without any nonterminals. + let mut flatten = FlattenNonterminals { + nt_to_tokenstream: rustc_parse::nt_to_tokenstream, + parse_sess: &self.cfg.sess.parse_sess, + synthesize_tokens: CanSynthesizeMissingTokens::No, + }; + orig_tokens = flatten.process_token_stream(orig_tokens); + + // Re-parse the tokens, setting the `capture_cfg` flag to save extra information + // to the captured `AttrAnnotatedTokenStream` (specifically, we capture + // `AttrAnnotatedTokenTree::AttributesData` for all occurences of `#[cfg]` and `#[cfg_attr]`) + let mut parser = + rustc_parse::stream_to_parser(&self.cfg.sess.parse_sess, orig_tokens, None); + parser.capture_cfg = true; + annotatable = match annotatable { + Annotatable::Item(_) => { + Annotatable::Item(parser.parse_item(ForceCollect::Yes).unwrap().unwrap()) } - Annotatable::Variant(v) => { - Annotatable::Variant(self.flat_map_variant(v).pop().unwrap()) + Annotatable::TraitItem(_) => Annotatable::TraitItem( + parser.parse_trait_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), + ), + Annotatable::ImplItem(_) => Annotatable::ImplItem( + parser.parse_impl_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), + ), + Annotatable::ForeignItem(_) => Annotatable::ForeignItem( + parser.parse_foreign_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), + ), + Annotatable::Stmt(_) => { + Annotatable::Stmt(P(parser.parse_stmt(ForceCollect::Yes).unwrap().unwrap())) } - } + Annotatable::Expr(_) => Annotatable::Expr(parser.parse_expr_force_collect().unwrap()), + _ => unreachable!(), + }; + + // Now that we have our re-parsed `AttrAnnotatedTokenStream`, recursively configuring + // our attribute target will correctly the tokens as well. + flat_map_annotatable(self, annotatable) } } -impl MutVisitor for CfgEval<'_> { +impl MutVisitor for CfgEval<'_, '_> { fn visit_expr(&mut self, expr: &mut P<ast::Expr>) { self.cfg.configure_expr(expr); mut_visit::noop_visit_expr(expr, self); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 04753926c3e..a3decff3ae7 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -541,7 +541,7 @@ impl<'a> TraitDef<'a> { self.generics.to_generics(cx, self.span, type_ident, generics); // Create the generic parameters - params.extend(generics.params.iter().map(|param| match param.kind { + params.extend(generics.params.iter().map(|param| match ¶m.kind { GenericParamKind::Lifetime { .. } => param.clone(), GenericParamKind::Type { .. } => { // I don't think this can be moved out of the loop, since @@ -561,7 +561,18 @@ impl<'a> TraitDef<'a> { cx.typaram(self.span, param.ident, vec![], bounds, None) } - GenericParamKind::Const { .. } => param.clone(), + GenericParamKind::Const { ty, kw_span, .. } => { + let const_nodefault_kind = GenericParamKind::Const { + ty: ty.clone(), + kw_span: kw_span.clone(), + + // We can't have default values inside impl block + default: None, + }; + let mut param_clone = param.clone(); + param_clone.kind = const_nodefault_kind; + param_clone + } })); // and similarly for where clauses diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 28e82597843..c8a7ff67b4d 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -142,7 +142,7 @@ fn entry_point_type(sess: &Session, item: &ast::Item, depth: usize) -> EntryPoin ast::ItemKind::Fn(..) => { if sess.contains_name(&item.attrs, sym::start) { EntryPointType::Start - } else if sess.contains_name(&item.attrs, sym::main) { + } else if sess.contains_name(&item.attrs, sym::rustc_main) { EntryPointType::MainAttr } else if item.ident.name == sym::main { if depth == 1 { @@ -187,7 +187,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { let attrs = attrs .into_iter() .filter(|attr| { - !self.sess.check_name(attr, sym::main) + !self.sess.check_name(attr, sym::rustc_main) && !self.sess.check_name(attr, sym::start) }) .chain(iter::once(allow_dead_code)) @@ -220,7 +220,7 @@ fn generate_test_harness( let expn_id = ext_cx.resolver.expansion_for_ast_pass( DUMMY_SP, AstPass::TestHarness, - &[sym::main, sym::test, sym::rustc_attrs], + &[sym::test, sym::rustc_attrs], None, ); let def_site = DUMMY_SP.with_def_site_ctxt(expn_id); @@ -247,7 +247,7 @@ fn generate_test_harness( /// By default this expands to /// /// ``` -/// #[main] +/// #[rustc_main] /// pub fn main() { /// extern crate test; /// test::test_main_static(&[ @@ -297,8 +297,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> { let test_extern_stmt = ecx.stmt_item(sp, ecx.item(sp, test_id, vec![], ast::ItemKind::ExternCrate(None))); - // #[main] - let main_meta = ecx.meta_word(sp, sym::main); + // #[rustc_main] + let main_meta = ecx.meta_word(sp, sym::rustc_main); let main_attr = ecx.attribute(main_meta); // pub fn main() { ... } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index c7ce32b385e..27fc2abedc7 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -277,5 +277,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( // simd_select // simd_rem // simd_neg + // simd_trunc + // simd_floor } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index af366f93b91..52fab2ea17d 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1057,8 +1057,10 @@ fn generic_simd_intrinsic( sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)), sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)), sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)), sym::simd_ceil => ("ceil", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_round => ("round", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)), sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)), sym::simd_fexp2 => ("exp2", bx.type_func(&[vec_ty], vec_ty)), sym::simd_flog10 => ("log10", bx.type_func(&[vec_ty], vec_ty)), @@ -1083,8 +1085,10 @@ fn generic_simd_intrinsic( | sym::simd_fsin | sym::simd_fcos | sym::simd_fabs - | sym::simd_floor | sym::simd_ceil + | sym::simd_floor + | sym::simd_round + | sym::simd_trunc | sym::simd_fexp | sym::simd_fexp2 | sym::simd_flog10 diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 70f78c07c65..54a8249b175 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2128,7 +2128,13 @@ extern "C" { pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool; pub fn LLVMRustPrintTargetCPUs(T: &TargetMachine); - pub fn LLVMRustPrintTargetFeatures(T: &TargetMachine); + pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t; + pub fn LLVMRustGetTargetFeature( + T: &TargetMachine, + Index: size_t, + Feature: &mut *const c_char, + Desc: &mut *const c_char, + ); pub fn LLVMRustGetHostCPUName(len: *mut usize) -> *const c_char; pub fn LLVMRustCreateTargetMachine( diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index e80de2bc902..b44553e4f6d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -10,6 +10,7 @@ use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; use std::ffi::{CStr, CString}; +use std::ptr; use std::slice; use std::str; use std::sync::atomic::{AtomicBool, Ordering}; @@ -192,15 +193,77 @@ pub fn print_passes() { } } +fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> { + let len = unsafe { llvm::LLVMRustGetTargetFeaturesCount(tm) }; + let mut ret = Vec::with_capacity(len); + for i in 0..len { + unsafe { + let mut feature = ptr::null(); + let mut desc = ptr::null(); + llvm::LLVMRustGetTargetFeature(tm, i, &mut feature, &mut desc); + if feature.is_null() || desc.is_null() { + bug!("LLVM returned a `null` target feature string"); + } + let feature = CStr::from_ptr(feature).to_str().unwrap_or_else(|e| { + bug!("LLVM returned a non-utf8 feature string: {}", e); + }); + let desc = CStr::from_ptr(desc).to_str().unwrap_or_else(|e| { + bug!("LLVM returned a non-utf8 feature string: {}", e); + }); + ret.push((feature, desc)); + } + } + ret +} + +fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) { + let mut target_features = llvm_target_features(tm); + let mut rustc_target_features = supported_target_features(sess) + .iter() + .filter_map(|(feature, _gate)| { + let llvm_feature = to_llvm_feature(sess, *feature); + // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings. + target_features.binary_search_by_key(&llvm_feature, |(f, _d)| *f).ok().map(|index| { + let (_f, desc) = target_features.remove(index); + (*feature, desc) + }) + }) + .collect::<Vec<_>>(); + rustc_target_features.extend_from_slice(&[( + "crt-static", + "Enables C Run-time Libraries to be statically linked", + )]); + let max_feature_len = target_features + .iter() + .chain(rustc_target_features.iter()) + .map(|(feature, _desc)| feature.len()) + .max() + .unwrap_or(0); + + println!("Features supported by rustc for this target:"); + for (feature, desc) in &rustc_target_features { + println!(" {1:0$} - {2}.", max_feature_len, feature, desc); + } + println!("\nCode-generation features supported by LLVM for this target:"); + for (feature, desc) in &target_features { + println!(" {1:0$} - {2}.", max_feature_len, feature, desc); + } + if target_features.len() == 0 { + println!(" Target features listing is not supported by this LLVM version."); + } + println!("\nUse +feature to enable a feature, or -feature to disable it."); + println!("For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n"); + println!("Code-generation features cannot be used in cfg or #[target_feature],"); + println!("and may be renamed or removed in a future version of LLVM or rustc.\n"); +} + pub(crate) fn print(req: PrintRequest, sess: &Session) { require_inited(); let tm = create_informational_target_machine(sess); - unsafe { - match req { - PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm), - PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm), - _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), - } + match req { + PrintRequest::TargetCPUs => unsafe { llvm::LLVMRustPrintTargetCPUs(tm) }, + PrintRequest::TargetFeatures => print_target_features(sess, tm), + _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 04d06864ee1..c8688faa80b 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1094,7 +1094,7 @@ fn start_executing_work<B: ExtraBackendMethods>( // only place where we have access to the compiler `Session`. // - LLVM work can be done on any thread. // - Codegen can only happen on the main thread. - // - Each thread doing substantial work most be in possession of a `Token` + // - Each thread doing substantial work must be in possession of a `Token` // from the `Jobserver`. // - The compiler process always holds one `Token`. Any additional `Tokens` // have to be requested from the `Jobserver`. @@ -1146,7 +1146,7 @@ fn start_executing_work<B: ExtraBackendMethods>( // if possible. These two goals are at odds with each other: If memory // consumption were not an issue, we could just let the main thread produce // LLVM WorkItems at full speed, assuring maximal utilization of - // Tokens/LLVM worker threads. However, since codegen usual is faster + // Tokens/LLVM worker threads. However, since codegen is usually faster // than LLVM processing, the queue of LLVM WorkItems would fill up and each // WorkItem potentially holds on to a substantial amount of memory. // diff --git a/compiler/rustc_error_codes/src/error_codes/E0137.md b/compiler/rustc_error_codes/src/error_codes/E0137.md index 0a02913d236..d4e19170f3f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0137.md +++ b/compiler/rustc_error_codes/src/error_codes/E0137.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + More than one function was declared with the `#[main]` attribute. Erroneous code example: -```compile_fail,E0137 +```compile_fail #![feature(main)] #[main] @@ -16,7 +18,7 @@ This error indicates that the compiler found multiple functions with the `#[main]` attribute. This is an error because there must be a unique entry point into a Rust program. Example: -``` +```compile_fail #![feature(main)] #[main] diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index a2035ee3c6e..59505842816 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -3,7 +3,7 @@ use crate::module::DirOwnership; use rustc_ast::ptr::P; use rustc_ast::token::{self, Nonterminal}; -use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, LazyTokenStream, TokenStream}; +use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream}; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, Stability}; @@ -46,26 +46,26 @@ pub enum Annotatable { Variant(ast::Variant), } -impl AstLike for Annotatable { - fn attrs(&self) -> &[Attribute] { +impl Annotatable { + pub fn span(&self) -> Span { match *self { - Annotatable::Item(ref item) => &item.attrs, - Annotatable::TraitItem(ref trait_item) => &trait_item.attrs, - Annotatable::ImplItem(ref impl_item) => &impl_item.attrs, - Annotatable::ForeignItem(ref foreign_item) => &foreign_item.attrs, - Annotatable::Stmt(ref stmt) => stmt.attrs(), - Annotatable::Expr(ref expr) => &expr.attrs, - Annotatable::Arm(ref arm) => &arm.attrs, - Annotatable::ExprField(ref field) => &field.attrs, - Annotatable::PatField(ref fp) => &fp.attrs, - Annotatable::GenericParam(ref gp) => &gp.attrs, - Annotatable::Param(ref p) => &p.attrs, - Annotatable::FieldDef(ref sf) => &sf.attrs, - Annotatable::Variant(ref v) => &v.attrs(), + Annotatable::Item(ref item) => item.span, + Annotatable::TraitItem(ref trait_item) => trait_item.span, + Annotatable::ImplItem(ref impl_item) => impl_item.span, + Annotatable::ForeignItem(ref foreign_item) => foreign_item.span, + Annotatable::Stmt(ref stmt) => stmt.span, + Annotatable::Expr(ref expr) => expr.span, + Annotatable::Arm(ref arm) => arm.span, + Annotatable::ExprField(ref field) => field.span, + Annotatable::PatField(ref fp) => fp.pat.span, + Annotatable::GenericParam(ref gp) => gp.ident.span, + Annotatable::Param(ref p) => p.span, + Annotatable::FieldDef(ref sf) => sf.span, + Annotatable::Variant(ref v) => v.span, } } - fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { + pub fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) { match self { Annotatable::Item(item) => item.visit_attrs(f), Annotatable::TraitItem(trait_item) => trait_item.visit_attrs(f), @@ -83,44 +83,6 @@ impl AstLike for Annotatable { } } - fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> { - match self { - Annotatable::Item(item) => item.tokens_mut(), - Annotatable::TraitItem(trait_item) => trait_item.tokens_mut(), - Annotatable::ImplItem(impl_item) => impl_item.tokens_mut(), - Annotatable::ForeignItem(foreign_item) => foreign_item.tokens_mut(), - Annotatable::Stmt(stmt) => stmt.tokens_mut(), - Annotatable::Expr(expr) => expr.tokens_mut(), - Annotatable::Arm(arm) => arm.tokens_mut(), - Annotatable::ExprField(field) => field.tokens_mut(), - Annotatable::PatField(fp) => fp.tokens_mut(), - Annotatable::GenericParam(gp) => gp.tokens_mut(), - Annotatable::Param(p) => p.tokens_mut(), - Annotatable::FieldDef(sf) => sf.tokens_mut(), - Annotatable::Variant(v) => v.tokens_mut(), - } - } -} - -impl Annotatable { - pub fn span(&self) -> Span { - match *self { - Annotatable::Item(ref item) => item.span, - Annotatable::TraitItem(ref trait_item) => trait_item.span, - Annotatable::ImplItem(ref impl_item) => impl_item.span, - Annotatable::ForeignItem(ref foreign_item) => foreign_item.span, - Annotatable::Stmt(ref stmt) => stmt.span, - Annotatable::Expr(ref expr) => expr.span, - Annotatable::Arm(ref arm) => arm.span, - Annotatable::ExprField(ref field) => field.span, - Annotatable::PatField(ref fp) => fp.pat.span, - Annotatable::GenericParam(ref gp) => gp.ident.span, - Annotatable::Param(ref p) => p.span, - Annotatable::FieldDef(ref sf) => sf.span, - Annotatable::Variant(ref v) => v.span, - } - } - pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) { match self { Annotatable::Item(item) => visitor.visit_item(item), @@ -139,7 +101,7 @@ impl Annotatable { } } - crate fn into_nonterminal(self) -> Nonterminal { + pub fn into_nonterminal(self) -> Nonterminal { match self { Annotatable::Item(item) => token::NtItem(item), Annotatable::TraitItem(item) | Annotatable::ImplItem(item) => { @@ -161,10 +123,7 @@ impl Annotatable { } crate fn into_tokens(self, sess: &ParseSess) -> TokenStream { - // Tokens of an attribute target may be invalidated by some outer `#[derive]` performing - // "full configuration" (attributes following derives on the same item should be the most - // common case), that's why synthesizing tokens is allowed. - nt_to_tokenstream(&self.into_nonterminal(), sess, CanSynthesizeMissingTokens::Yes) + nt_to_tokenstream(&self.into_nonterminal(), sess, CanSynthesizeMissingTokens::No) } pub fn expect_item(self) -> P<ast::Item> { diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index a23731cf309..03c83f9c07b 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -2,8 +2,10 @@ use rustc_ast::ptr::P; use rustc_ast::token::{DelimToken, Token, TokenKind}; -use rustc_ast::tokenstream::{DelimSpan, LazyTokenStream, Spacing, TokenStream, TokenTree}; -use rustc_ast::{self as ast, AstLike, AttrItem, Attribute, MetaItem}; +use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; +use rustc_ast::tokenstream::{DelimSpan, Spacing}; +use rustc_ast::tokenstream::{LazyTokenStream, TokenTree}; +use rustc_ast::{self as ast, AstLike, AttrItem, AttrStyle, Attribute, MetaItem}; use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::map_in_place::MapInPlace; @@ -23,7 +25,10 @@ use rustc_span::{Span, DUMMY_SP}; pub struct StripUnconfigured<'a> { pub sess: &'a Session, pub features: Option<&'a Features>, - pub modified: bool, + /// If `true`, perform cfg-stripping on attached tokens. + /// This is only used for the input to derive macros, + /// which needs eager expansion of `cfg` and `cfg_attr` + pub config_tokens: bool, } fn get_features( @@ -194,7 +199,7 @@ fn get_features( // `cfg_attr`-process the crate's attributes and compute the crate's features. pub fn features(sess: &Session, mut krate: ast::Crate) -> (ast::Crate, Features) { - let mut strip_unconfigured = StripUnconfigured { sess, features: None, modified: false }; + let mut strip_unconfigured = StripUnconfigured { sess, features: None, config_tokens: false }; let unconfigured_attrs = krate.attrs.clone(); let diag = &sess.parse_sess.span_diagnostic; @@ -241,24 +246,83 @@ impl<'a> StripUnconfigured<'a> { pub fn configure<T: AstLike>(&mut self, mut node: T) -> Option<T> { self.process_cfg_attrs(&mut node); if self.in_cfg(node.attrs()) { + self.try_configure_tokens(&mut node); Some(node) } else { - self.modified = true; None } } + fn try_configure_tokens<T: AstLike>(&mut self, node: &mut T) { + if self.config_tokens { + if let Some(Some(tokens)) = node.tokens_mut() { + let attr_annotated_tokens = tokens.create_token_stream(); + *tokens = LazyTokenStream::new(self.configure_tokens(&attr_annotated_tokens)); + } + } + } + fn configure_krate_attrs( &mut self, mut attrs: Vec<ast::Attribute>, ) -> Option<Vec<ast::Attribute>> { attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); - if self.in_cfg(&attrs) { - Some(attrs) - } else { - self.modified = true; - None + if self.in_cfg(&attrs) { Some(attrs) } else { None } + } + + /// Performs cfg-expansion on `stream`, producing a new `AttrAnnotatedTokenStream`. + /// This is only used during the invocation of `derive` proc-macros, + /// which require that we cfg-expand their entire input. + /// Normal cfg-expansion operates on parsed AST nodes via the `configure` method + fn configure_tokens(&mut self, stream: &AttrAnnotatedTokenStream) -> AttrAnnotatedTokenStream { + fn can_skip(stream: &AttrAnnotatedTokenStream) -> bool { + stream.0.iter().all(|(tree, _spacing)| match tree { + AttrAnnotatedTokenTree::Attributes(_) => false, + AttrAnnotatedTokenTree::Token(_) => true, + AttrAnnotatedTokenTree::Delimited(_, _, inner) => can_skip(inner), + }) + } + + if can_skip(stream) { + return stream.clone(); } + + let trees: Vec<_> = stream + .0 + .iter() + .flat_map(|(tree, spacing)| match tree.clone() { + AttrAnnotatedTokenTree::Attributes(mut data) => { + let mut attrs: Vec<_> = std::mem::take(&mut data.attrs).into(); + attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); + data.attrs = attrs.into(); + + if self.in_cfg(&data.attrs) { + data.tokens = LazyTokenStream::new( + self.configure_tokens(&data.tokens.create_token_stream()), + ); + Some((AttrAnnotatedTokenTree::Attributes(data), *spacing)).into_iter() + } else { + None.into_iter() + } + } + AttrAnnotatedTokenTree::Delimited(sp, delim, mut inner) => { + inner = self.configure_tokens(&inner); + Some((AttrAnnotatedTokenTree::Delimited(sp, delim, inner), *spacing)) + .into_iter() + } + AttrAnnotatedTokenTree::Token(token) => { + if let TokenKind::Interpolated(nt) = token.kind { + panic!( + "Nonterminal should have been flattened at {:?}: {:?}", + token.span, nt + ); + } else { + Some((AttrAnnotatedTokenTree::Token(token), *spacing)).into_iter() + } + } + }) + .collect(); + AttrAnnotatedTokenStream::new(trees) } /// Parse and expand all `cfg_attr` attributes into a list of attributes @@ -285,9 +349,6 @@ impl<'a> StripUnconfigured<'a> { return vec![attr]; } - // A `#[cfg_attr]` either gets removed, or replaced with a new attribute - self.modified = true; - let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) { None => return vec![], Some(r) => r, @@ -311,7 +372,7 @@ impl<'a> StripUnconfigured<'a> { expanded_attrs .into_iter() .flat_map(|(item, span)| { - let orig_tokens = attr.tokens(); + let orig_tokens = attr.tokens().to_tokenstream(); // We are taking an attribute of the form `#[cfg_attr(pred, attr)]` // and producing an attribute of the form `#[attr]`. We @@ -321,25 +382,34 @@ impl<'a> StripUnconfigured<'a> { // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token // for `attr` when we expand it to `#[attr]` - let pound_token = orig_tokens.trees().next().unwrap(); - if !matches!(pound_token, TokenTree::Token(Token { kind: TokenKind::Pound, .. })) { - panic!("Bad tokens for attribute {:?}", attr); + let mut orig_trees = orig_tokens.trees(); + let pound_token = match orig_trees.next().unwrap() { + TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token, + _ => panic!("Bad tokens for attribute {:?}", attr), + }; + let pound_span = pound_token.span; + + let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)]; + if attr.style == AttrStyle::Inner { + // For inner attributes, we do the same thing for the `!` in `#![some_attr]` + let bang_token = match orig_trees.next().unwrap() { + TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token, + _ => panic!("Bad tokens for attribute {:?}", attr), + }; + trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone)); } // We don't really have a good span to use for the syntheized `[]` // in `#[attr]`, so just use the span of the `#` token. - let bracket_group = TokenTree::Delimited( - DelimSpan::from_single(pound_token.span()), + let bracket_group = AttrAnnotatedTokenTree::Delimited( + DelimSpan::from_single(pound_span), DelimToken::Bracket, item.tokens .as_ref() .unwrap_or_else(|| panic!("Missing tokens for {:?}", item)) .create_token_stream(), ); - let tokens = Some(LazyTokenStream::new(TokenStream::new(vec![ - (pound_token, Spacing::Alone), - (bracket_group, Spacing::Alone), - ]))); - + trees.push((bracket_group, Spacing::Alone)); + let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees))); self.process_cfg_attr(attr::mk_attr_from_item(item, tokens, attr.style, span)) }) .collect() @@ -457,7 +527,8 @@ impl<'a> StripUnconfigured<'a> { self.sess.parse_sess.span_diagnostic.span_err(attr.span, msg); } - self.process_cfg_attrs(expr) + self.process_cfg_attrs(expr); + self.try_configure_tokens(&mut *expr); } } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 0f4441d020b..529ef7e4611 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -12,7 +12,7 @@ use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor}; -use rustc_ast::{AstLike, AttrItem, AttrStyle, Block, Inline, ItemKind, LitKind, MacArgs}; +use rustc_ast::{AstLike, AttrItem, Block, Inline, ItemKind, LitKind, MacArgs}; use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem}; use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe}; use rustc_ast_pretty::pprust; @@ -491,6 +491,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let fragment_kind = invoc.fragment_kind; let (expanded_fragment, new_invocations) = match self.expand_invoc(invoc, &ext.kind) { ExpandResult::Ready(fragment) => { + let mut derive_invocations = Vec::new(); let derive_placeholders = self .cx .resolver @@ -512,14 +513,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { _ => unreachable!(), }; - invocations.reserve(derives.len()); + derive_invocations.reserve(derives.len()); derives .into_iter() .map(|(path, _exts)| { // FIXME: Consider using the derive resolutions (`_exts`) // instead of enqueuing the derives to be resolved again later. let expn_id = ExpnId::fresh(None); - invocations.push(( + derive_invocations.push(( Invocation { kind: InvocationKind::Derive { path, @@ -546,7 +547,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }) .unwrap_or_default(); - self.collect_invocations(fragment, &derive_placeholders) + let (fragment, collected_invocations) = + self.collect_invocations(fragment, &derive_placeholders); + // We choose to expand any derive invocations associated with this macro invocation + // *before* any macro invocations collected from the output fragment + derive_invocations.extend(collected_invocations); + (fragment, derive_invocations) } ExpandResult::Retry(invoc) => { if force { @@ -605,10 +611,15 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let invocations = { let mut collector = InvocationCollector { + // Non-derive macro invocations cannot see the results of cfg expansion - they + // will either be removed along with the item, or invoked before the cfg/cfg_attr + // attribute is expanded. Therefore, we don't need to configure the tokens + // Derive macros *can* see the results of cfg-expansion - they are handled + // specially in `fully_expand_fragment` cfg: StripUnconfigured { sess: &self.cx.sess, features: self.cx.ecfg.features, - modified: false, + config_tokens: false, }, cx: self.cx, invocations: Vec::new(), @@ -703,13 +714,26 @@ impl<'a, 'b> MacroExpander<'a, 'b> { SyntaxExtensionKind::Attr(expander) => { self.gate_proc_macro_input(&item); self.gate_proc_macro_attr_item(span, &item); - let tokens = match attr.style { - AttrStyle::Outer => item.into_tokens(&self.cx.sess.parse_sess), - // FIXME: Properly collect tokens for inner attributes - AttrStyle::Inner => rustc_parse::fake_token_stream( + let mut fake_tokens = false; + if let Annotatable::Item(item_inner) = &item { + if let ItemKind::Mod(_, mod_kind) = &item_inner.kind { + // FIXME: Collect tokens and use them instead of generating + // fake ones. These are unstable, so it needs to be + // fixed prior to stabilization + // Fake tokens when we are invoking an inner attribute, and: + fake_tokens = matches!(attr.style, ast::AttrStyle::Inner) && + // We are invoking an attribute on the crate root, or an outline + // module + (item_inner.ident.name.is_empty() || !matches!(mod_kind, ast::ModKind::Loaded(_, Inline::Yes, _))); + } + } + let tokens = if fake_tokens { + rustc_parse::fake_token_stream( &self.cx.sess.parse_sess, &item.into_nonterminal(), - ), + ) + } else { + item.into_tokens(&self.cx.sess.parse_sess) }; let attr_item = attr.unwrap_normal_item(); if let MacArgs::Eq(..) = attr_item.args { @@ -891,21 +915,21 @@ pub fn parse_ast_fragment<'a>( } AstFragmentKind::TraitItems => { let mut items = SmallVec::new(); - while let Some(item) = this.parse_trait_item()? { + while let Some(item) = this.parse_trait_item(ForceCollect::No)? { items.extend(item); } AstFragment::TraitItems(items) } AstFragmentKind::ImplItems => { let mut items = SmallVec::new(); - while let Some(item) = this.parse_impl_item()? { + while let Some(item) = this.parse_impl_item(ForceCollect::No)? { items.extend(item); } AstFragment::ImplItems(items) } AstFragmentKind::ForeignItems => { let mut items = SmallVec::new(); - while let Some(item) = this.parse_foreign_item()? { + while let Some(item) = this.parse_foreign_item(ForceCollect::No)? { items.extend(item); } AstFragment::ForeignItems(items) diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 61b776ff2d2..3f84979ac05 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -94,7 +94,7 @@ impl MultiItemModifier for ProcMacroDerive { { TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into() } else { - nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::Yes) + nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::No) }; let server = proc_macro_server::Rustc::new(ecx); diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 1a91eb600a9..6fd1af60fe2 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -134,9 +134,6 @@ declare_features! ( /// Allows using the `box $expr` syntax. (active, box_syntax, "1.0.0", Some(49733), None), - /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls. - (active, main, "1.0.0", Some(29634), None), - /// Allows using `#[start]` on a function indicating that it is the program entrypoint. (active, start, "1.0.0", Some(29633), None), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 7df9b3f0a79..f286ea7cdec 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -536,6 +536,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_specialization_trait, Normal, template!(Word), "the `#[rustc_specialization_trait]` attribute is used to check specializations" ), + rustc_attr!( + rustc_main, Normal, template!(Word), + "the `#[rustc_main]` attribute is used internally to specify test entry point function", + ), // ========================================================================== // Internal attributes, Testing: diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index e1491576616..c2ad339ed41 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -132,6 +132,8 @@ declare_features! ( (removed, link_args, "1.53.0", Some(29596), None, Some("removed in favor of using `-C link-arg=ARG` on command line, \ which is available from cargo build scripts with `cargo:rustc-link-arg` now")), + /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls. + (removed, main, "1.53.0", Some(29634), None, None), // ------------------------------------------------------------------------- // feature-group-end: removed features diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 2270b2b33e2..9685d21762b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -405,7 +405,6 @@ fn test_codegen_options_tracking_hash() { untracked!(incremental, Some(String::from("abc"))); // `link_arg` is omitted because it just forwards to `link_args`. untracked!(link_args, vec![String::from("abc"), String::from("def")]); - untracked!(link_dead_code, Some(true)); untracked!(link_self_contained, Some(true)); untracked!(linker, Some(PathBuf::from("linker"))); untracked!(linker_flavor, Some(LinkerFlavor::Gcc)); @@ -433,6 +432,7 @@ fn test_codegen_options_tracking_hash() { tracked!(force_unwind_tables, Some(true)); tracked!(inline_threshold, Some(0xf007ba11)); tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); + tracked!(link_dead_code, Some(true)); tracked!(llvm_args, vec![String::from("1"), String::from("2")]); tracked!(lto, LtoCli::Fat); tracked!(metadata, vec![String::from("A"), String::from("B")]); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 0b49e6562f9..7f9e459635a 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1,3 +1,5 @@ +// ignore-tidy-filelength + //! Lints in the Rust compiler. //! //! This contains lints which can feasibly be implemented as their own @@ -857,11 +859,10 @@ declare_lint! { /// ``` /// /// This syntax is now a hard error in the 2018 edition. In the 2015 - /// edition, this lint is "allow" by default, because the old code is - /// still valid, and warning for all old code can be noisy. This lint + /// edition, this lint is "warn" by default. This lint /// enables the [`cargo fix`] tool with the `--edition` flag to /// automatically transition old code from the 2015 edition to 2018. The - /// tool will switch this lint to "warn" and will automatically apply the + /// tool will run this lint and automatically apply the /// suggested fix from the compiler (which is to add `_` to each /// parameter). This provides a completely automated way to update old /// code for a new edition. See [issue #41686] for more details. @@ -869,7 +870,7 @@ declare_lint! { /// [issue #41686]: https://github.com/rust-lang/rust/issues/41686 /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html pub ANONYMOUS_PARAMETERS, - Allow, + Warn, "detects anonymous parameters", @future_incompatible = FutureIncompatibleInfo { reference: "issue #41686 <https://github.com/rust-lang/rust/issues/41686>", @@ -884,6 +885,10 @@ declare_lint_pass!( impl EarlyLintPass for AnonymousParameters { fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) { + if cx.sess.edition() != Edition::Edition2015 { + // This is a hard error in future editions; avoid linting and erroring + return; + } if let ast::AssocItemKind::Fn(box FnKind(_, ref sig, _, _)) = it.kind { for arg in sig.decl.inputs.iter() { if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind { @@ -2961,3 +2966,88 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations { } } } + +declare_lint! { + /// The `deref_nullptr` lint detects when an null pointer is dereferenced, + /// which causes [undefined behavior]. + /// + /// ### Example + /// + /// ```rust,no_run + /// # #![allow(unused)] + /// use std::ptr; + /// unsafe { + /// let x = &*ptr::null::<i32>(); + /// let x = ptr::addr_of!(*ptr::null::<i32>()); + /// let x = *(0 as *const i32); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Dereferencing a null pointer causes [undefined behavior] even as a place expression, + /// like `&*(0 as *const i32)` or `addr_of!(*(0 as *const i32))`. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + pub DEREF_NULLPTR, + Warn, + "detects when an null pointer is dereferenced" +} + +declare_lint_pass!(DerefNullPtr => [DEREF_NULLPTR]); + +impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { + /// test if expression is a null ptr + fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + match &expr.kind { + rustc_hir::ExprKind::Cast(ref expr, ref ty) => { + if let rustc_hir::TyKind::Ptr(_) = ty.kind { + return is_zero(expr) || is_null_ptr(cx, expr); + } + } + // check for call to `core::ptr::null` or `core::ptr::null_mut` + rustc_hir::ExprKind::Call(ref path, _) => { + if let rustc_hir::ExprKind::Path(ref qpath) = path.kind { + if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() { + return cx.tcx.is_diagnostic_item(sym::ptr_null, def_id) + || cx.tcx.is_diagnostic_item(sym::ptr_null_mut, def_id); + } + } + } + _ => {} + } + false + } + + /// test if experssion is the literal `0` + fn is_zero(expr: &hir::Expr<'_>) -> bool { + match &expr.kind { + rustc_hir::ExprKind::Lit(ref lit) => { + if let LitKind::Int(a, _) = lit.node { + return a == 0; + } + } + _ => {} + } + false + } + + if let rustc_hir::ExprKind::Unary(ref un_op, ref expr_deref) = expr.kind { + if let rustc_hir::UnOp::Deref = un_op { + if is_null_ptr(cx, expr_deref) { + cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| { + let mut err = lint.build("dereferencing a null pointer"); + err.span_label( + expr.span, + "this code causes undefined behavior when executed", + ); + err.emit(); + }); + } + } + } + } +} diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 647ecad0469..54fcaef414f 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -109,6 +109,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_anon_const(&mut self, c: &'a ast::AnonConst) { run_early_pass!(self, check_anon_const, c); + self.check_id(c.id); ast_visit::walk_anon_const(self, c); } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index e2724b52453..2f46969b021 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -206,6 +206,7 @@ macro_rules! late_lint_mod_passes { UnreachablePub: UnreachablePub, ExplicitOutlivesRequirements: ExplicitOutlivesRequirements, InvalidValue: InvalidValue, + DerefNullPtr: DerefNullPtr, ] ); }; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 27724b4965c..ad42366b04d 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2522,9 +2522,10 @@ declare_lint! { /// /// The fix to this is to wrap the unsafe code in an `unsafe` block. /// - /// This lint is "allow" by default because it has not yet been - /// stabilized, and is not yet complete. See [RFC #2585] and [issue - /// #71668] for more details + /// This lint is "allow" by default since this will affect a large amount + /// of existing code, and the exact plan for increasing the severity is + /// still being considered. See [RFC #2585] and [issue #71668] for more + /// details. /// /// [`unsafe fn`]: https://doc.rust-lang.org/reference/unsafe-functions.html /// [`unsafe` block]: https://doc.rust-lang.org/reference/expressions/block-expr.html#unsafe-blocks @@ -2944,6 +2945,7 @@ declare_lint_pass! { NONTRIVIAL_STRUCTURAL_MATCH, SOFT_UNSTABLE, INLINE_NO_SANITIZE, + BAD_ASM_STYLE, ASM_SUB_REGISTER, UNSAFE_OP_IN_UNSAFE_FN, INCOMPLETE_INCLUDE, diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index b8d5b50dcb5..617b2ed970e 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -404,26 +404,21 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) { printf("\n"); } -extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef TM) { +extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) { const TargetMachine *Target = unwrap(TM); const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable(); - unsigned MaxFeatLen = getLongestEntryLength(FeatTable); - - printf("Available features for this target:\n"); - for (auto &Feature : FeatTable) - printf(" %-*s - %s.\n", MaxFeatLen, Feature.Key, Feature.Desc); - printf("\nRust-specific features:\n"); - printf(" %-*s - %s.\n", - MaxFeatLen, - "crt-static", - "Enables libraries with C Run-time Libraries(CRT) to be statically linked" - ); - printf("\n"); + return FeatTable.size(); +} - printf("Use +feature to enable a feature, or -feature to disable it.\n" - "For example, rustc -C -target-cpu=mycpu -C " - "target-feature=+feature1,-feature2\n\n"); +extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index, + const char** Feature, const char** Desc) { + const TargetMachine *Target = unwrap(TM); + const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); + const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable(); + const SubtargetFeatureKV Feat = FeatTable[Index]; + *Feature = Feat.Key; + *Desc = Feat.Desc; } #else @@ -432,9 +427,11 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef) { printf("Target CPU help is not supported by this LLVM version.\n\n"); } -extern "C" void LLVMRustPrintTargetFeatures(LLVMTargetMachineRef) { - printf("Target features help is not supported by this LLVM version.\n\n"); +extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef) { + return 0; } + +extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef, const char**, const char**) {} #endif extern "C" const char* LLVMRustGetHostCPUName(size_t *len) { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index bac69e282a5..3a445484711 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1055,6 +1055,8 @@ rustc_queries! { desc { "checking if the crate is_compiler_builtins" } } query has_global_allocator(_: CrateNum) -> bool { + // This query depends on untracked global state in CStore + eval_always fatal_cycle desc { "checking if the crate has_global_allocator" } } diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index d295b17d902..008e6d015e8 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -36,6 +36,7 @@ pub enum TypeError<'tcx> { UnsafetyMismatch(ExpectedFound<hir::Unsafety>), AbiMismatch(ExpectedFound<abi::Abi>), Mutability, + ArgumentMutability(usize), TupleSize(ExpectedFound<usize>), FixedArraySize(ExpectedFound<u64>), ArgCount, @@ -46,6 +47,7 @@ pub enum TypeError<'tcx> { RegionsPlaceholderMismatch, Sorts(ExpectedFound<Ty<'tcx>>), + ArgumentSorts(ExpectedFound<Ty<'tcx>>, usize), IntMismatch(ExpectedFound<ty::IntVarValue>), FloatMismatch(ExpectedFound<ty::FloatTy>), Traits(ExpectedFound<DefId>), @@ -110,7 +112,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { AbiMismatch(values) => { write!(f, "expected {} fn, found {} fn", values.expected, values.found) } - Mutability => write!(f, "types differ in mutability"), + ArgumentMutability(_) | Mutability => write!(f, "types differ in mutability"), TupleSize(values) => write!( f, "expected a tuple with {} element{}, \ @@ -142,7 +144,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> { br_string(br) ), RegionsPlaceholderMismatch => write!(f, "one type is more general than the other"), - Sorts(values) => ty::tls::with(|tcx| { + ArgumentSorts(values, _) | Sorts(values) => ty::tls::with(|tcx| { report_maybe_different( f, &values.expected.sort_string(tcx), @@ -199,10 +201,11 @@ impl<'tcx> TypeError<'tcx> { use self::TypeError::*; match self { CyclicTy(_) | CyclicConst(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) - | FixedArraySize(_) | Sorts(_) | IntMismatch(_) | FloatMismatch(_) - | VariadicMismatch(_) | TargetFeatureCast(_) => false, + | FixedArraySize(_) | ArgumentSorts(..) | Sorts(_) | IntMismatch(_) + | FloatMismatch(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false, Mutability + | ArgumentMutability(_) | TupleSize(_) | ArgCount | RegionsDoesNotOutlive(..) @@ -339,7 +342,7 @@ impl<'tcx> TyCtxt<'tcx> { use self::TypeError::*; debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); match err { - Sorts(values) => { + ArgumentSorts(values, _) | Sorts(values) => { match (values.expected.kind(), values.found.kind()) { (ty::Closure(..), ty::Closure(..)) => { db.note("no two closures, even if identical, have the same type"); diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index ca60339da0d..b6f93c9bd59 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -179,6 +179,12 @@ impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> { } else { relation.relate_with_variance(ty::Contravariant, a, b) } + }) + .enumerate() + .map(|(i, r)| match r { + Err(TypeError::Sorts(exp_found)) => Err(TypeError::ArgumentSorts(exp_found, i)), + Err(TypeError::Mutability) => Err(TypeError::ArgumentMutability(i)), + r => r, }); Ok(ty::FnSig { inputs_and_output: tcx.mk_type_list(inputs_and_output)?, diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index a969626b370..7290c41d615 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -587,6 +587,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { UnsafetyMismatch(x) => UnsafetyMismatch(x), AbiMismatch(x) => AbiMismatch(x), Mutability => Mutability, + ArgumentMutability(i) => ArgumentMutability(i), TupleSize(x) => TupleSize(x), FixedArraySize(x) => FixedArraySize(x), ArgCount => ArgCount, @@ -607,6 +608,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { CyclicTy(t) => return tcx.lift(t).map(|t| CyclicTy(t)), CyclicConst(ct) => return tcx.lift(ct).map(|ct| CyclicConst(ct)), ProjectionMismatched(x) => ProjectionMismatched(x), + ArgumentSorts(x, i) => return tcx.lift(x).map(|x| ArgumentSorts(x, i)), Sorts(x) => return tcx.lift(x).map(Sorts), ExistentialMismatch(x) => return tcx.lift(x).map(ExistentialMismatch), ConstMismatch(x) => return tcx.lift(x).map(ConstMismatch), diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index 5fdf8a8d1ee..df6f1963923 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -1681,7 +1681,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if decl.can_be_made_mutable() { err.span_suggestion( decl.source_info.span, - "make this binding mutable", + "consider making this binding mutable", format!("mut {}", name), Applicability::MachineApplicable, ); diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 3a189e6b33d..f944e5f8f04 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -584,7 +584,7 @@ fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, abi: Abi) -> bo Some(UnwindAttr::Aborts) => true, // If no attribute was found and the panic strategy is `unwind`, then we should examine // the function's ABI string to determine whether it should abort upon panic. - None => { + None if tcx.features().c_unwind => { use Abi::*; match abi { // In the case of ABI's that have an `-unwind` equivalent, check whether the ABI @@ -615,6 +615,10 @@ fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, abi: Abi) -> bo | Unadjusted => true, } } + // If the `c_unwind` feature gate is not active, follow the behavior that was in place + // prior to #76570. This is a special case: some functions have a C ABI but are meant to + // unwind anyway. Don't stop them. + None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)` } } diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 9fead30c4a1..905077a48e2 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -1,5 +1,6 @@ //! The main parser interface. +#![feature(array_windows)] #![feature(crate_visibility_modifier)] #![feature(bindings_after_at)] #![feature(iter_order_by)] @@ -9,9 +10,12 @@ #![recursion_limit = "256"] use rustc_ast as ast; -use rustc_ast::token::{self, Nonterminal}; -use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens, LazyTokenStream, TokenStream}; +use rustc_ast::token::{self, Nonterminal, Token, TokenKind}; +use rustc_ast::tokenstream::{self, AttributesData, CanSynthesizeMissingTokens, LazyTokenStream}; +use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; +use rustc_ast::tokenstream::{Spacing, TokenStream}; use rustc_ast::AstLike; +use rustc_ast::Attribute; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, FatalError, Level, PResult}; @@ -21,8 +25,6 @@ use rustc_span::{FileName, SourceFile, Span}; use std::path::Path; use std::str; -use tracing::debug; - pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); #[macro_use] @@ -255,19 +257,23 @@ pub fn nt_to_tokenstream( // before we fall back to the stringification. let convert_tokens = - |tokens: Option<&LazyTokenStream>| tokens.as_ref().map(|t| t.create_token_stream()); + |tokens: Option<&LazyTokenStream>| Some(tokens?.create_token_stream().to_tokenstream()); let tokens = match *nt { - Nonterminal::NtItem(ref item) => prepend_attrs(sess, &item.attrs, nt, item.tokens.as_ref()), + Nonterminal::NtItem(ref item) => prepend_attrs(&item.attrs, item.tokens.as_ref()), Nonterminal::NtBlock(ref block) => convert_tokens(block.tokens.as_ref()), Nonterminal::NtStmt(ref stmt) => { - let do_prepend = |tokens| prepend_attrs(sess, stmt.attrs(), nt, tokens); if let ast::StmtKind::Empty = stmt.kind { - let tokens: TokenStream = - tokenstream::TokenTree::token(token::Semi, stmt.span).into(); - do_prepend(Some(&LazyTokenStream::new(tokens))) + let tokens = AttrAnnotatedTokenStream::new(vec![( + tokenstream::AttrAnnotatedTokenTree::Token(Token::new( + TokenKind::Semi, + stmt.span, + )), + Spacing::Alone, + )]); + prepend_attrs(&stmt.attrs(), Some(&LazyTokenStream::new(tokens))) } else { - do_prepend(stmt.tokens()) + prepend_attrs(&stmt.attrs(), stmt.tokens()) } } Nonterminal::NtPat(ref pat) => convert_tokens(pat.tokens.as_ref()), @@ -283,10 +289,7 @@ pub fn nt_to_tokenstream( Nonterminal::NtVis(ref vis) => convert_tokens(vis.tokens.as_ref()), Nonterminal::NtTT(ref tt) => Some(tt.clone().into()), Nonterminal::NtExpr(ref expr) | Nonterminal::NtLiteral(ref expr) => { - if expr.tokens.is_none() { - debug!("missing tokens for expr {:?}", expr); - } - prepend_attrs(sess, &expr.attrs, nt, expr.tokens.as_ref()) + prepend_attrs(&expr.attrs, expr.tokens.as_ref()) } }; @@ -295,34 +298,30 @@ pub fn nt_to_tokenstream( } else if matches!(synthesize_tokens, CanSynthesizeMissingTokens::Yes) { return fake_token_stream(sess, nt); } else { - panic!("Missing tokens for nt at {:?}: {:?}", nt.span(), pprust::nonterminal_to_string(nt)); + panic!( + "Missing tokens for nt {:?} at {:?}: {:?}", + nt, + nt.span(), + pprust::nonterminal_to_string(nt) + ); } } +fn prepend_attrs(attrs: &[Attribute], tokens: Option<&LazyTokenStream>) -> Option<TokenStream> { + let tokens = tokens?; + if attrs.is_empty() { + return Some(tokens.create_token_stream().to_tokenstream()); + } + let attr_data = AttributesData { attrs: attrs.to_vec().into(), tokens: tokens.clone() }; + let wrapped = AttrAnnotatedTokenStream::new(vec![( + AttrAnnotatedTokenTree::Attributes(attr_data), + Spacing::Alone, + )]); + Some(wrapped.to_tokenstream()) +} + pub fn fake_token_stream(sess: &ParseSess, nt: &Nonterminal) -> TokenStream { let source = pprust::nonterminal_to_string(nt); let filename = FileName::macro_expansion_source_code(&source); parse_stream_from_source_str(filename, source, sess, Some(nt.span())) } - -fn prepend_attrs( - sess: &ParseSess, - attrs: &[ast::Attribute], - nt: &Nonterminal, - tokens: Option<&tokenstream::LazyTokenStream>, -) -> Option<tokenstream::TokenStream> { - if attrs.is_empty() { - return Some(tokens?.create_token_stream()); - } - let mut builder = tokenstream::TokenStreamBuilder::new(); - for attr in attrs { - // FIXME: Correctly handle tokens for inner attributes. - // For now, we fall back to reparsing the original AST node - if attr.style == ast::AttrStyle::Inner { - return Some(fake_token_stream(sess, nt)); - } - builder.push(attr.tokens()); - } - builder.push(tokens?.create_token_stream()); - Some(builder.build()) -} diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 95d4a48b845..ee6ff4dba39 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -1,10 +1,11 @@ -use super::{AttrWrapper, Parser, PathStyle}; +use super::{AttrWrapper, Capturing, Parser, PathStyle}; use rustc_ast as ast; use rustc_ast::attr; use rustc_ast::token::{self, Nonterminal}; use rustc_ast_pretty::pprust; use rustc_errors::{error_code, PResult}; use rustc_span::{sym, Span}; +use std::convert::TryInto; use tracing::debug; @@ -29,6 +30,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> { let mut attrs: Vec<ast::Attribute> = Vec::new(); let mut just_parsed_doc_comment = false; + let start_pos = self.token_cursor.num_next_calls; loop { debug!("parse_outer_attributes: self.token={:?}", self.token); let attr = if self.check(&token::Pound) { @@ -74,7 +76,7 @@ impl<'a> Parser<'a> { break; } } - Ok(AttrWrapper::new(attrs)) + Ok(AttrWrapper::new(attrs.into(), start_pos)) } /// Matches `attribute = # ! [ meta_item ]`. @@ -177,6 +179,7 @@ impl<'a> Parser<'a> { crate fn parse_inner_attributes(&mut self) -> PResult<'a, Vec<ast::Attribute>> { let mut attrs: Vec<ast::Attribute> = vec![]; loop { + let start_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap(); // Only try to parse if it is an inner attribute (has `!`). let attr = if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) { Some(self.parse_attribute(InnerAttrPolicy::Permitted)?) @@ -191,6 +194,18 @@ impl<'a> Parser<'a> { None }; if let Some(attr) = attr { + let end_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap(); + // If we are currently capturing tokens, mark the location of this inner attribute. + // If capturing ends up creating a `LazyTokenStream`, we will include + // this replace range with it, removing the inner attribute from the final + // `AttrAnnotatedTokenStream`. Inner attributes are stored in the parsed AST note. + // During macro expansion, they are selectively inserted back into the + // token stream (the first inner attribute is remoevd each time we invoke the + // corresponding macro). + let range = start_pos..end_pos; + if let Capturing::Yes = self.capture_state.capturing { + self.capture_state.inner_attr_ranges.insert(attr.id, (range, vec![])); + } attrs.push(attr); } else { break; @@ -311,6 +326,9 @@ pub fn maybe_needs_tokens(attrs: &[ast::Attribute]) -> bool { // One of the attributes may either itself be a macro, // or expand to macro attributes (`cfg_attr`). attrs.iter().any(|attr| { + if attr.is_doc_comment() { + return false; + } attr.ident().map_or(true, |ident| { ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name) }) diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 36a0fda6458..35759a396e8 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -1,12 +1,14 @@ -use super::attr; -use super::{ForceCollect, Parser, TokenCursor, TrailingToken}; -use rustc_ast::token::{self, Token, TokenKind}; -use rustc_ast::tokenstream::{CreateTokenStream, TokenStream, TokenTree, TreeAndSpacing}; -use rustc_ast::tokenstream::{DelimSpan, LazyTokenStream, Spacing}; -use rustc_ast::AstLike; +use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor, TrailingToken}; +use rustc_ast::token::{self, DelimToken, Token, TokenKind}; +use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttributesData, CreateTokenStream}; +use rustc_ast::tokenstream::{AttrAnnotatedTokenTree, DelimSpan, LazyTokenStream, Spacing}; use rustc_ast::{self as ast}; +use rustc_ast::{AstLike, AttrVec, Attribute}; use rustc_errors::PResult; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{sym, Span, DUMMY_SP}; + +use std::convert::TryInto; +use std::ops::Range; /// A wrapper type to ensure that the parser handles outer attributes correctly. /// When we parse outer attributes, we need to ensure that we capture tokens @@ -23,23 +25,158 @@ use rustc_span::{Span, DUMMY_SP}; /// cannot directly access the `attrs` field #[derive(Debug, Clone)] pub struct AttrWrapper { - attrs: Vec<ast::Attribute>, + attrs: AttrVec, + // The start of the outer attributes in the token cursor. + // This allows us to create a `ReplaceRange` for the entire attribute + // target, including outer attributes. + start_pos: usize, } +// This struct is passed around very frequently, +// so make sure it doesn't accidentally get larger +#[cfg(target_arch = "x86_64")] +rustc_data_structures::static_assert_size!(AttrWrapper, 16); + impl AttrWrapper { - pub fn empty() -> AttrWrapper { - AttrWrapper { attrs: vec![] } + pub(super) fn new(attrs: AttrVec, start_pos: usize) -> AttrWrapper { + AttrWrapper { attrs, start_pos } } - pub fn new(attrs: Vec<ast::Attribute>) -> AttrWrapper { - AttrWrapper { attrs } + pub fn empty() -> AttrWrapper { + AttrWrapper { attrs: AttrVec::new(), start_pos: usize::MAX } } // FIXME: Delay span bug here? - pub(crate) fn take_for_recovery(self) -> Vec<ast::Attribute> { + pub(crate) fn take_for_recovery(self) -> AttrVec { self.attrs } + + // FIXME: require passing an NT to prevent misuse of this method + pub(crate) fn prepend_to_nt_inner(self, attrs: &mut Vec<Attribute>) { + let mut self_attrs: Vec<_> = self.attrs.into(); + std::mem::swap(attrs, &mut self_attrs); + attrs.extend(self_attrs); + } + pub fn is_empty(&self) -> bool { self.attrs.is_empty() } + + pub fn maybe_needs_tokens(&self) -> bool { + crate::parser::attr::maybe_needs_tokens(&self.attrs) + } +} + +/// Returns `true` if `attrs` contains a `cfg` or `cfg_attr` attribute +fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool { + // NOTE: Builtin attributes like `cfg` and `cfg_attr` cannot be renamed via imports. + // Therefore, the absence of a literal `cfg` or `cfg_attr` guarantees that + // we don't need to do any eager expansion. + attrs.iter().any(|attr| { + attr.ident().map_or(false, |ident| ident.name == sym::cfg || ident.name == sym::cfg_attr) + }) +} + +// Produces a `TokenStream` on-demand. Using `cursor_snapshot` +// and `num_calls`, we can reconstruct the `TokenStream` seen +// by the callback. This allows us to avoid producing a `TokenStream` +// if it is never needed - for example, a captured `macro_rules!` +// argument that is never passed to a proc macro. +// In practice token stream creation happens rarely compared to +// calls to `collect_tokens` (see some statistics in #78736), +// so we are doing as little up-front work as possible. +// +// This also makes `Parser` very cheap to clone, since +// there is no intermediate collection buffer to clone. +#[derive(Clone)] +struct LazyTokenStreamImpl { + start_token: (Token, Spacing), + cursor_snapshot: TokenCursor, + num_calls: usize, + break_last_token: bool, + replace_ranges: Box<[ReplaceRange]>, +} + +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +rustc_data_structures::static_assert_size!(LazyTokenStreamImpl, 144); + +impl CreateTokenStream for LazyTokenStreamImpl { + fn create_token_stream(&self) -> AttrAnnotatedTokenStream { + // The token produced by the final call to `next` or `next_desugared` + // was not actually consumed by the callback. The combination + // of chaining the initial token and using `take` produces the desired + // result - we produce an empty `TokenStream` if no calls were made, + // and omit the final token otherwise. + let mut cursor_snapshot = self.cursor_snapshot.clone(); + let tokens = + std::iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1)) + .chain((0..self.num_calls).map(|_| { + let token = if cursor_snapshot.desugar_doc_comments { + cursor_snapshot.next_desugared() + } else { + cursor_snapshot.next() + }; + (FlatToken::Token(token.0), token.1) + })) + .take(self.num_calls); + + if !self.replace_ranges.is_empty() { + let mut tokens: Vec<_> = tokens.collect(); + let mut replace_ranges = self.replace_ranges.clone(); + replace_ranges.sort_by_key(|(range, _)| range.start); + + #[cfg(debug_assertions)] + { + for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() { + assert!( + range.end <= next_range.start || range.end >= next_range.end, + "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", + range, + tokens, + next_range, + next_tokens, + ); + } + } + + // Process the replace ranges, starting from the highest start + // position and working our way back. If have tokens like: + // + // `#[cfg(FALSE)]` struct Foo { #[cfg(FALSE)] field: bool }` + // + // Then we will generate replace ranges for both + // the `#[cfg(FALSE)] field: bool` and the entire + // `#[cfg(FALSE)]` struct Foo { #[cfg(FALSE)] field: bool }` + // + // By starting processing from the replace range with the greatest + // start position, we ensure that any replace range which encloses + // another replace range will capture the *replaced* tokens for the inner + // range, not the original tokens. + for (range, new_tokens) in replace_ranges.iter().rev() { + assert!(!range.is_empty(), "Cannot replace an empty range: {:?}", range); + // Replace ranges are only allowed to decrease the number of tokens. + assert!( + range.len() >= new_tokens.len(), + "Range {:?} has greater len than {:?}", + range, + new_tokens + ); + + // Replace any removed tokens with `FlatToken::Empty`. + // This keeps the total length of `tokens` constant throughout the + // replacement process, allowing us to use all of the `ReplaceRanges` entries + // without adjusting indices. + let filler = std::iter::repeat((FlatToken::Empty, Spacing::Alone)) + .take(range.len() - new_tokens.len()); + + tokens.splice( + (range.start as usize)..(range.end as usize), + new_tokens.clone().into_iter().chain(filler), + ); + } + make_token_stream(tokens.into_iter(), self.break_last_token) + } else { + make_token_stream(tokens, self.break_last_token) + } + } } impl<'a> Parser<'a> { @@ -65,106 +202,195 @@ impl<'a> Parser<'a> { force_collect: ForceCollect, f: impl FnOnce(&mut Self, Vec<ast::Attribute>) -> PResult<'a, (R, TrailingToken)>, ) -> PResult<'a, R> { - if matches!(force_collect, ForceCollect::No) && !attr::maybe_needs_tokens(&attrs.attrs) { - return Ok(f(self, attrs.attrs)?.0); + // We only bail out when nothing could possibly observe the collected tokens: + // 1. We cannot be force collecting tokens (since force-collecting requires tokens + // by definition + if matches!(force_collect, ForceCollect::No) + // None of our outer attributes can require tokens (e.g. a proc-macro) + && !attrs.maybe_needs_tokens() + // If our target supports custom inner attributes, then we cannot bail + // out early, since we may need to capture tokens for a custom inner attribute + // invocation. + && !R::SUPPORTS_CUSTOM_INNER_ATTRS + // Never bail out early in `capture_cfg` mode, since there might be `#[cfg]` + // or `#[cfg_attr]` attributes. + && !self.capture_cfg + { + return Ok(f(self, attrs.attrs.into())?.0); } + let start_token = (self.token.clone(), self.token_spacing); let cursor_snapshot = self.token_cursor.clone(); - let (mut ret, trailing_token) = f(self, attrs.attrs)?; - let tokens = match ret.tokens_mut() { - Some(tokens) if tokens.is_none() => tokens, - _ => return Ok(ret), - }; + let has_outer_attrs = !attrs.attrs.is_empty(); + let prev_capturing = std::mem::replace(&mut self.capture_state.capturing, Capturing::Yes); + let replace_ranges_start = self.capture_state.replace_ranges.len(); + + let ret = f(self, attrs.attrs.into()); + + self.capture_state.capturing = prev_capturing; + + let (mut ret, trailing) = ret?; - // Produces a `TokenStream` on-demand. Using `cursor_snapshot` - // and `num_calls`, we can reconstruct the `TokenStream` seen - // by the callback. This allows us to avoid producing a `TokenStream` - // if it is never needed - for example, a captured `macro_rules!` - // argument that is never passed to a proc macro. - // In practice token stream creation happens rarely compared to - // calls to `collect_tokens` (see some statistics in #78736), - // so we are doing as little up-front work as possible. - // - // This also makes `Parser` very cheap to clone, since - // there is no intermediate collection buffer to clone. - #[derive(Clone)] - struct LazyTokenStreamImpl { - start_token: (Token, Spacing), - cursor_snapshot: TokenCursor, - num_calls: usize, - desugar_doc_comments: bool, - append_unglued_token: Option<TreeAndSpacing>, + // When we're not in `capture-cfg` mode, then bail out early if: + // 1. Our target doesn't support tokens at all (e.g we're parsing an `NtIdent`) + // so there's nothing for us to do. + // 2. Our target already has tokens set (e.g. we've parsed something + // like `#[my_attr] $item`. The actual parsing code takes care of prepending + // any attributes to the nonterminal, so we don't need to modify the + // already captured tokens. + // Note that this check is independent of `force_collect`- if we already + // have tokens, or can't even store them, then there's never a need to + // force collection of new tokens. + if !self.capture_cfg && matches!(ret.tokens_mut(), None | Some(Some(_))) { + return Ok(ret); + } + + // This is very similar to the bail out check at the start of this function. + // Now that we've parsed an AST node, we have more information available. + if matches!(force_collect, ForceCollect::No) + // We now have inner attributes available, so this check is more precise + // than `attrs.maybe_needs_tokens()` at the start of the function. + // As a result, we don't need to check `R::SUPPORTS_CUSTOM_INNER_ATTRS` + && !crate::parser::attr::maybe_needs_tokens(ret.attrs()) + // Subtle: We call `has_cfg_or_cfg_attr` with the attrs from `ret`. + // This ensures that we consider inner attributes (e.g. `#![cfg]`), + // which require us to have tokens available + // We also call `has_cfg_or_cfg_attr` at the beginning of this function, + // but we only bail out if there's no possibility of inner attributes + // (!R::SUPPORTS_CUSTOM_INNER_ATTRS) + // We only catpure about `#[cfg]` or `#[cfg_attr]` in `capture_cfg` + // mode - during normal parsing, we don't need any special capturing + // for those attributes, since they're builtin. + && !(self.capture_cfg && has_cfg_or_cfg_attr(ret.attrs())) + { + return Ok(ret); } - impl CreateTokenStream for LazyTokenStreamImpl { - fn create_token_stream(&self) -> TokenStream { - if self.num_calls == 0 { - return TokenStream::new(vec![]); - } - let mut cursor_snapshot = self.cursor_snapshot.clone(); - // Don't skip `None` delimiters, since we want to pass them to - // proc macros. Normally, we'll end up capturing `TokenKind::Interpolated`, - // which gets converted to a `None`-delimited group when we invoke - // a proc-macro. However, it's possible to already have a `None`-delimited - // group in the stream (such as when parsing the output of a proc-macro, - // or in certain unusual cases with cross-crate `macro_rules!` macros). - cursor_snapshot.skip_none_delims = false; - - // The token produced by the final call to `next` or `next_desugared` - // was not actually consumed by the callback. - let num_calls = self.num_calls - 1; - let mut i = 0; - let tokens = - std::iter::once(self.start_token.clone()).chain(std::iter::from_fn(|| { - if i >= num_calls { - return None; - } - - let token = if self.desugar_doc_comments { - cursor_snapshot.next_desugared() - } else { - cursor_snapshot.next() - }; - - // When the `LazyTokenStreamImpl` was original produced, we did *not* - // include `NoDelim` tokens in `num_calls`, since they are normally ignored - // by the parser. Therefore, we only increment our counter for other types of tokens. - if !matches!( - token.0.kind, - token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) - ) { - i += 1; - } - Some(token) - })); - - make_token_stream(tokens, self.append_unglued_token.clone()) + let mut inner_attr_replace_ranges = Vec::new(); + // Take the captured ranges for any inner attributes that we parsed. + for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) { + if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) { + inner_attr_replace_ranges.push(attr_range); + } else { + self.sess + .span_diagnostic + .delay_span_bug(inner_attr.span, "Missing token range for attribute"); } } - let mut num_calls = self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls; - match trailing_token { + let replace_ranges_end = self.capture_state.replace_ranges.len(); + + let cursor_snapshot_next_calls = cursor_snapshot.num_next_calls; + let mut end_pos = self.token_cursor.num_next_calls; + + // Capture a trailing token if requested by the callback 'f' + match trailing { TrailingToken::None => {} TrailingToken::Semi => { assert_eq!(self.token.kind, token::Semi); - num_calls += 1; + end_pos += 1; } TrailingToken::MaybeComma => { if self.token.kind == token::Comma { - num_calls += 1; + end_pos += 1; } } } - *tokens = Some(LazyTokenStream::new(LazyTokenStreamImpl { + // If we 'broke' the last token (e.g. breaking a '>>' token to two '>' tokens), + // then extend the range of captured tokens to include it, since the parser + // was not actually bumped past it. When the `LazyTokenStream` gets converted + // into a `AttrAnnotatedTokenStream`, we will create the proper token. + if self.token_cursor.break_last_token { + assert_eq!( + trailing, + TrailingToken::None, + "Cannot set `break_last_token` and have trailing token" + ); + end_pos += 1; + } + + let num_calls = end_pos - cursor_snapshot_next_calls; + + // If we have no attributes, then we will never need to + // use any replace ranges. + let replace_ranges: Box<[ReplaceRange]> = if ret.attrs().is_empty() && !self.capture_cfg { + Box::new([]) + } else { + // Grab any replace ranges that occur *inside* the current AST node. + // We will perform the actual replacement when we convert the `LazyTokenStream` + // to a `AttrAnnotatedTokenStream` + let start_calls: u32 = cursor_snapshot_next_calls.try_into().unwrap(); + self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end] + .iter() + .cloned() + .chain(inner_attr_replace_ranges.clone().into_iter()) + .map(|(range, tokens)| { + ((range.start - start_calls)..(range.end - start_calls), tokens) + }) + .collect() + }; + + let tokens = LazyTokenStream::new(LazyTokenStreamImpl { start_token, num_calls, cursor_snapshot, - desugar_doc_comments: self.desugar_doc_comments, - append_unglued_token: self.token_cursor.append_unglued_token.clone(), - })); + break_last_token: self.token_cursor.break_last_token, + replace_ranges, + }); + + // If we support tokens at all + if let Some(target_tokens) = ret.tokens_mut() { + if let Some(target_tokens) = target_tokens { + assert!( + !self.capture_cfg, + "Encountered existing tokens with capture_cfg set: {:?}", + target_tokens + ); + } else { + // Store se our newly captured tokens into the AST node + *target_tokens = Some(tokens.clone()); + }; + } + let final_attrs = ret.attrs(); + + // If `capture_cfg` is set and we're inside a recursive call to + // `collect_tokens_trailing_token`, then we need to register a replace range + // if we have `#[cfg]` or `#[cfg_attr]`. This allows us to run eager cfg-expansion + // on the captured token stream. + if self.capture_cfg + && matches!(self.capture_state.capturing, Capturing::Yes) + && has_cfg_or_cfg_attr(&final_attrs) + { + let attr_data = AttributesData { attrs: final_attrs.to_vec().into(), tokens }; + + // Replace the entire AST node that we just parsed, including attributes, + // with a `FlatToken::AttrTarget`. If this AST node is inside an item + // that has `#[derive]`, then this will allow us to cfg-expand this + // AST node. + let start_pos = + if has_outer_attrs { attrs.start_pos } else { cursor_snapshot_next_calls }; + let new_tokens = vec![(FlatToken::AttrTarget(attr_data), Spacing::Alone)]; + + assert!( + !self.token_cursor.break_last_token, + "Should not have unglued last token with cfg attr" + ); + let range: Range<u32> = (start_pos.try_into().unwrap())..(end_pos.try_into().unwrap()); + self.capture_state.replace_ranges.push((range, new_tokens)); + self.capture_state.replace_ranges.extend(inner_attr_replace_ranges); + } + + // Only clear our `replace_ranges` when we're finished capturing entirely. + if matches!(self.capture_state.capturing, Capturing::No) { + self.capture_state.replace_ranges.clear(); + // We don't clear `inner_attr_ranges`, as doing so repeatedly + // had a measureable performance impact. Most inner attributes that + // we insert will get removed - when we drop the parser, we'll free + // up the memory used by any attributes that we didn't remove from the map. + } Ok(ret) } } @@ -172,43 +398,112 @@ impl<'a> Parser<'a> { /// Converts a flattened iterator of tokens (including open and close delimiter tokens) /// into a `TokenStream`, creating a `TokenTree::Delimited` for each matching pair /// of open and close delims. +// FIXME(#67062): Currently, we don't parse `None`-delimited groups correctly, +// which can cause us to end up with mismatched `None` delimiters in our +// captured tokens. This function contains several hacks to work around this - +// essentially, we throw away mismatched `None` delimiters when we encounter them. +// Once we properly parse `None` delimiters, they can be captured just like any +// other tokens, and these hacks can be removed. fn make_token_stream( - tokens: impl Iterator<Item = (Token, Spacing)>, - append_unglued_token: Option<TreeAndSpacing>, -) -> TokenStream { + mut iter: impl Iterator<Item = (FlatToken, Spacing)>, + break_last_token: bool, +) -> AttrAnnotatedTokenStream { #[derive(Debug)] struct FrameData { open: Span, - inner: Vec<(TokenTree, Spacing)>, + open_delim: DelimToken, + inner: Vec<(AttrAnnotatedTokenTree, Spacing)>, } - let mut stack = vec![FrameData { open: DUMMY_SP, inner: vec![] }]; - for (token, spacing) in tokens { + let mut stack = + vec![FrameData { open: DUMMY_SP, open_delim: DelimToken::NoDelim, inner: vec![] }]; + let mut token_and_spacing = iter.next(); + while let Some((token, spacing)) = token_and_spacing { match token { - Token { kind: TokenKind::OpenDelim(_), span } => { - stack.push(FrameData { open: span, inner: vec![] }); + FlatToken::Token(Token { kind: TokenKind::OpenDelim(delim), span }) => { + stack.push(FrameData { open: span, open_delim: delim, inner: vec![] }); } - Token { kind: TokenKind::CloseDelim(delim), span } => { - let frame_data = stack.pop().expect("Token stack was empty!"); + FlatToken::Token(Token { kind: TokenKind::CloseDelim(delim), span }) => { + // HACK: If we enconter a mismatched `None` delimiter at the top + // level, just ignore it. + if matches!(delim, DelimToken::NoDelim) + && (stack.len() == 1 + || !matches!(stack.last_mut().unwrap().open_delim, DelimToken::NoDelim)) + { + token_and_spacing = iter.next(); + continue; + } + let frame_data = stack + .pop() + .unwrap_or_else(|| panic!("Token stack was empty for token: {:?}", token)); + + // HACK: If our current frame has a mismatched opening `None` delimiter, + // merge our current frame with the one above it. That is, transform + // `[ { < first second } third ]` into `[ { first second } third ]` + if !matches!(delim, DelimToken::NoDelim) + && matches!(frame_data.open_delim, DelimToken::NoDelim) + { + stack.last_mut().unwrap().inner.extend(frame_data.inner); + // Process our closing delimiter again, this time at the previous + // frame in the stack + token_and_spacing = Some((token, spacing)); + continue; + } + + assert_eq!( + frame_data.open_delim, delim, + "Mismatched open/close delims: open={:?} close={:?}", + frame_data.open, span + ); let dspan = DelimSpan::from_pair(frame_data.open, span); - let stream = TokenStream::new(frame_data.inner); - let delimited = TokenTree::Delimited(dspan, delim, stream); + let stream = AttrAnnotatedTokenStream::new(frame_data.inner); + let delimited = AttrAnnotatedTokenTree::Delimited(dspan, delim, stream); stack .last_mut() - .unwrap_or_else(|| panic!("Bottom token frame is missing for tokens!")) + .unwrap_or_else(|| { + panic!("Bottom token frame is missing for token: {:?}", token) + }) .inner .push((delimited, Spacing::Alone)); } - token => { - stack - .last_mut() - .expect("Bottom token frame is missing!") - .inner - .push((TokenTree::Token(token), spacing)); - } + FlatToken::Token(token) => stack + .last_mut() + .expect("Bottom token frame is missing!") + .inner + .push((AttrAnnotatedTokenTree::Token(token), spacing)), + FlatToken::AttrTarget(data) => stack + .last_mut() + .expect("Bottom token frame is missing!") + .inner + .push((AttrAnnotatedTokenTree::Attributes(data), spacing)), + FlatToken::Empty => {} } + token_and_spacing = iter.next(); + } + // HACK: If we don't have a closing `None` delimiter for our last + // frame, merge the frame with the top-level frame. That is, + // turn `< first second` into `first second` + if stack.len() == 2 && stack[1].open_delim == DelimToken::NoDelim { + let temp_buf = stack.pop().unwrap(); + stack.last_mut().unwrap().inner.extend(temp_buf.inner); } let mut final_buf = stack.pop().expect("Missing final buf!"); - final_buf.inner.extend(append_unglued_token); + if break_last_token { + let (last_token, spacing) = final_buf.inner.pop().unwrap(); + if let AttrAnnotatedTokenTree::Token(last_token) = last_token { + let unglued_first = last_token.kind.break_two_token_op().unwrap().0; + + // A 'unglued' token is always two ASCII characters + let mut first_span = last_token.span.shrink_to_lo(); + first_span = first_span.with_hi(first_span.lo() + rustc_span::BytePos(1)); + + final_buf.inner.push(( + AttrAnnotatedTokenTree::Token(Token::new(unglued_first, first_span)), + spacing, + )); + } else { + panic!("Unexpected last token {:?}", last_token) + } + } assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack); - TokenStream::new(final_buf.inner) + AttrAnnotatedTokenStream::new(final_buf.inner) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 77e85c06ff5..70a5ac6f15e 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -666,21 +666,23 @@ impl<'a> Parser<'a> { ); match x { Ok((_, _, false)) => { - self.bump(); // `>` - match self.parse_expr() { - Ok(_) => { - e.span_suggestion_verbose( - binop.span.shrink_to_lo(), - TURBOFISH_SUGGESTION_STR, - "::".to_string(), - Applicability::MaybeIncorrect, - ); - e.emit(); - *expr = self.mk_expr_err(expr.span.to(self.prev_token.span)); - return Ok(()); - } - Err(mut err) => { - err.cancel(); + if self.eat(&token::Gt) { + match self.parse_expr() { + Ok(_) => { + e.span_suggestion_verbose( + binop.span.shrink_to_lo(), + TURBOFISH_SUGGESTION_STR, + "::".to_string(), + Applicability::MaybeIncorrect, + ); + e.emit(); + *expr = + self.mk_expr_err(expr.span.to(self.prev_token.span)); + return Ok(()); + } + Err(mut err) => { + err.cancel(); + } } } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 02ee268b88c..e155b3fa773 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2581,19 +2581,17 @@ impl<'a> Parser<'a> { attrs: AttrWrapper, f: impl FnOnce(&mut Self, Vec<ast::Attribute>) -> PResult<'a, P<Expr>>, ) -> PResult<'a, P<Expr>> { - // FIXME - come up with a nice way to properly forward `ForceCollect`from - // the nonterminal parsing code. TThis approach iscorrect, but will cause - // us to unnecessarily capture tokens for exprs that have only builtin - // attributes. Revisit this before #![feature(stmt_expr_attributes)] is stabilized - let force_collect = if attrs.is_empty() { ForceCollect::No } else { ForceCollect::Yes }; - self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| { + self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let res = f(this, attrs)?; let trailing = if this.restrictions.contains(Restrictions::STMT_EXPR) && this.token.kind == token::Semi { TrailingToken::Semi } else { - TrailingToken::None + // FIXME - pass this through from the place where we know + // we need a comma, rather than assuming that `#[attr] expr,` + // always captures a trailing comma + TrailingToken::MaybeComma }; Ok((res, trailing)) }) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 70dbaa53d38..2b7b58459c0 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -103,20 +103,11 @@ impl<'a> Parser<'a> { // over when we bump the parser if let token::Interpolated(nt) = &self.token.kind { if let token::NtItem(item) = &**nt { - let item = item.clone(); - - return self.collect_tokens_trailing_token( - attrs, - force_collect, - |this, mut attrs| { - let mut item = item; - mem::swap(&mut item.attrs, &mut attrs); - item.attrs.extend(attrs); - // Bump the parser so the we capture the token::Interpolated - this.bump(); - Ok((Some(item.into_inner()), TrailingToken::None)) - }, - ); + let mut item = item.clone(); + self.bump(); + + attrs.prepend_to_nt_inner(&mut item.attrs); + return Ok(Some(item.into_inner())); } }; @@ -530,7 +521,7 @@ impl<'a> Parser<'a> { generics.where_clause = self.parse_where_clause()?; - let impl_items = self.parse_item_list(attrs, |p| p.parse_impl_item())?; + let impl_items = self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?; let item_kind = match ty_second { Some(ty_second) => { @@ -718,22 +709,32 @@ impl<'a> Parser<'a> { } else { // It's a normal trait. tps.where_clause = self.parse_where_clause()?; - let items = self.parse_item_list(attrs, |p| p.parse_trait_item())?; + let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?; Ok((ident, ItemKind::Trait(box TraitKind(is_auto, unsafety, tps, bounds, items)))) } } - pub fn parse_impl_item(&mut self) -> PResult<'a, Option<Option<P<AssocItem>>>> { - self.parse_assoc_item(|_| true) + pub fn parse_impl_item( + &mut self, + force_collect: ForceCollect, + ) -> PResult<'a, Option<Option<P<AssocItem>>>> { + self.parse_assoc_item(|_| true, force_collect) } - pub fn parse_trait_item(&mut self) -> PResult<'a, Option<Option<P<AssocItem>>>> { - self.parse_assoc_item(|edition| edition >= Edition::Edition2018) + pub fn parse_trait_item( + &mut self, + force_collect: ForceCollect, + ) -> PResult<'a, Option<Option<P<AssocItem>>>> { + self.parse_assoc_item(|edition| edition >= Edition::Edition2018, force_collect) } /// Parses associated items. - fn parse_assoc_item(&mut self, req_name: ReqName) -> PResult<'a, Option<Option<P<AssocItem>>>> { - Ok(self.parse_item_(req_name, ForceCollect::No)?.map( + fn parse_assoc_item( + &mut self, + req_name: ReqName, + force_collect: ForceCollect, + ) -> PResult<'a, Option<Option<P<AssocItem>>>> { + Ok(self.parse_item_(req_name, force_collect)?.map( |Item { attrs, id, span, vis, ident, kind, tokens }| { let kind = match AssocItemKind::try_from(kind) { Ok(kind) => kind, @@ -918,14 +919,17 @@ impl<'a> Parser<'a> { unsafety: Unsafe, ) -> PResult<'a, ItemInfo> { let abi = self.parse_abi(); // ABI? - let items = self.parse_item_list(attrs, |p| p.parse_foreign_item())?; + let items = self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No))?; let module = ast::ForeignMod { unsafety, abi, items }; Ok((Ident::invalid(), ItemKind::ForeignMod(module))) } /// Parses a foreign item (one in an `extern { ... }` block). - pub fn parse_foreign_item(&mut self) -> PResult<'a, Option<Option<P<ForeignItem>>>> { - Ok(self.parse_item_(|_| true, ForceCollect::No)?.map( + pub fn parse_foreign_item( + &mut self, + force_collect: ForceCollect, + ) -> PResult<'a, Option<Option<P<ForeignItem>>>> { + Ok(self.parse_item_(|_| true, force_collect)?.map( |Item { attrs, id, span, vis, ident, kind, tokens }| { let kind = match ForeignItemKind::try_from(kind) { Ok(kind) => kind, diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 748a8e2bb49..ed95a5661b1 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -19,13 +19,16 @@ pub use path::PathStyle; use rustc_ast::ptr::P; use rustc_ast::token::{self, DelimToken, Token, TokenKind}; +use rustc_ast::tokenstream::AttributesData; use rustc_ast::tokenstream::{self, DelimSpan, Spacing}; -use rustc_ast::tokenstream::{TokenStream, TokenTree, TreeAndSpacing}; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, AstLike, AttrStyle, AttrVec, Const, CrateSugar, Extern}; use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacDelimiter, Mutability, StrLit, Unsafe}; use rustc_ast::{Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::PResult; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError}; @@ -34,6 +37,7 @@ use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use tracing::debug; +use std::ops::Range; use std::{cmp, mem, slice}; bitflags::bitflags! { @@ -64,6 +68,7 @@ pub enum ForceCollect { No, } +#[derive(Debug, Eq, PartialEq)] pub enum TrailingToken { None, Semi, @@ -111,6 +116,7 @@ pub struct Parser<'a> { pub token_spacing: Spacing, /// The previous token. pub prev_token: Token, + pub capture_cfg: bool, restrictions: Restrictions, expected_tokens: Vec<TokenType>, // Important: This must only be advanced from `next_tok` @@ -134,6 +140,44 @@ pub struct Parser<'a> { pub last_type_ascription: Option<(Span, bool /* likely path typo */)>, /// If present, this `Parser` is not parsing Rust code but rather a macro call. subparser_name: Option<&'static str>, + capture_state: CaptureState, +} + +/// Indicates a range of tokens that should be replaced by +/// the tokens in the provided vector. This is used in two +/// places during token collection: +/// +/// 1. During the parsing of an AST node that may have a `#[derive]` +/// attribute, we parse a nested AST node that has `#[cfg]` or `#[cfg_attr]` +/// In this case, we use a `ReplaceRange` to replace the entire inner AST node +/// with `FlatToken::AttrTarget`, allowing us to perform eager cfg-expansion +/// on a `AttrAnnotatedTokenStream` +/// +/// 2. When we parse an inner attribute while collecting tokens. We +/// remove inner attributes from the token stream entirely, and +/// instead track them through the `attrs` field on the AST node. +/// This allows us to easily manipulate them (for example, removing +/// the first macro inner attribute to invoke a proc-macro). +/// When create a `TokenStream`, the inner attributes get inserted +/// into the proper place in the token stream. +pub type ReplaceRange = (Range<u32>, Vec<(FlatToken, Spacing)>); + +/// Controls how we capture tokens. Capturing can be expensive, +/// so we try to avoid performing capturing in cases where +/// we will never need a `AttrAnnotatedTokenStream` +#[derive(Copy, Clone)] +pub enum Capturing { + /// We aren't performing any capturing - this is the default mode. + No, + /// We are capturing tokens + Yes, +} + +#[derive(Clone)] +struct CaptureState { + capturing: Capturing, + replace_ranges: Vec<ReplaceRange>, + inner_attr_ranges: FxHashMap<AttrId, ReplaceRange>, } impl<'a> Drop for Parser<'a> { @@ -167,18 +211,11 @@ struct TokenCursor { // want to capture just the first 'unglued' token. // For example, capturing the `Vec<u8>` // in `Option<Vec<u8>>` requires us to unglue - // the trailing `>>` token. The `append_unglued_token` + // the trailing `>>` token. The `break_last_token` // field is used to track this token - it gets // appended to the captured stream when // we evaluate a `LazyTokenStream` - append_unglued_token: Option<TreeAndSpacing>, - // If `true`, skip the delimiters for `None`-delimited groups, - // and just yield the inner tokens. This is `true` during - // normal parsing, since the parser code is not currently prepared - // to handle `None` delimiters. When capturing a `TokenStream`, - // however, we want to handle `None`-delimiters, since - // proc-macros always see `None`-delimited groups. - skip_none_delims: bool, + break_last_token: bool, } #[derive(Clone)] @@ -191,13 +228,13 @@ struct TokenCursorFrame { } impl TokenCursorFrame { - fn new(span: DelimSpan, delim: DelimToken, tts: TokenStream, skip_none_delims: bool) -> Self { + fn new(span: DelimSpan, delim: DelimToken, tts: TokenStream) -> Self { TokenCursorFrame { delim, span, - open_delim: delim == token::NoDelim && skip_none_delims, + open_delim: false, tree_cursor: tts.into_trees(), - close_delim: delim == token::NoDelim && skip_none_delims, + close_delim: false, } } } @@ -225,7 +262,7 @@ impl TokenCursor { return (token, spacing); } TokenTree::Delimited(sp, delim, tts) => { - let frame = TokenCursorFrame::new(sp, delim, tts, self.skip_none_delims); + let frame = TokenCursorFrame::new(sp, delim, tts); self.stack.push(mem::replace(&mut self.frame, frame)); } } @@ -283,7 +320,6 @@ impl TokenCursor { .cloned() .collect::<TokenStream>() }, - self.skip_none_delims, ), )); @@ -372,26 +408,24 @@ impl<'a> Parser<'a> { desugar_doc_comments: bool, subparser_name: Option<&'static str>, ) -> Self { + let mut start_frame = TokenCursorFrame::new(DelimSpan::dummy(), token::NoDelim, tokens); + start_frame.open_delim = true; + start_frame.close_delim = true; + let mut parser = Parser { sess, token: Token::dummy(), token_spacing: Spacing::Alone, prev_token: Token::dummy(), + capture_cfg: false, restrictions: Restrictions::empty(), expected_tokens: Vec::new(), - // Skip over the delimiters for `None`-delimited groups token_cursor: TokenCursor { - frame: TokenCursorFrame::new( - DelimSpan::dummy(), - token::NoDelim, - tokens, - /* skip_none_delims */ true, - ), + frame: start_frame, stack: Vec::new(), num_next_calls: 0, desugar_doc_comments, - append_unglued_token: None, - skip_none_delims: true, + break_last_token: false, }, desugar_doc_comments, unmatched_angle_bracket_count: 0, @@ -400,6 +434,11 @@ impl<'a> Parser<'a> { last_unexpected_token_span: None, last_type_ascription: None, subparser_name, + capture_state: CaptureState { + capturing: Capturing::No, + replace_ranges: Vec::new(), + inner_attr_ranges: Default::default(), + }, }; // Make parser point to the first token. @@ -409,21 +448,29 @@ impl<'a> Parser<'a> { } fn next_tok(&mut self, fallback_span: Span) -> (Token, Spacing) { - let (mut next, spacing) = if self.desugar_doc_comments { - self.token_cursor.next_desugared() - } else { - self.token_cursor.next() - }; - self.token_cursor.num_next_calls += 1; - // We've retrieved an token from the underlying - // cursor, so we no longer need to worry about - // an unglued token. See `break_and_eat` for more details - self.token_cursor.append_unglued_token = None; - if next.span.is_dummy() { - // Tweak the location for better diagnostics, but keep syntactic context intact. - next.span = fallback_span.with_ctxt(next.span.ctxt()); + loop { + let (mut next, spacing) = if self.desugar_doc_comments { + self.token_cursor.next_desugared() + } else { + self.token_cursor.next() + }; + self.token_cursor.num_next_calls += 1; + // We've retrieved an token from the underlying + // cursor, so we no longer need to worry about + // an unglued token. See `break_and_eat` for more details + self.token_cursor.break_last_token = false; + if next.span.is_dummy() { + // Tweak the location for better diagnostics, but keep syntactic context intact. + next.span = fallback_span.with_ctxt(next.span.ctxt()); + } + if matches!( + next.kind, + token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) + ) { + continue; + } + return (next, spacing); } - (next, spacing) } pub fn unexpected<T>(&mut self) -> PResult<'a, T> { @@ -621,8 +668,7 @@ impl<'a> Parser<'a> { // If we consume any additional tokens, then this token // is not needed (we'll capture the entire 'glued' token), // and `next_tok` will set this field to `None` - self.token_cursor.append_unglued_token = - Some((TokenTree::Token(self.token.clone()), Spacing::Alone)); + self.token_cursor.break_last_token = true; // Use the spacing of the glued token as the spacing // of the unglued second token. self.bump_with((Token::new(second, second_span), self.token_spacing)); @@ -703,6 +749,8 @@ impl<'a> Parser<'a> { let mut recovered = false; let mut trailing = false; let mut v = vec![]; + let unclosed_delims = !self.unclosed_delims.is_empty(); + while !self.expect_any_with_type(kets, expect) { if let token::CloseDelim(..) | token::Eof = self.token.kind { break; @@ -723,7 +771,7 @@ impl<'a> Parser<'a> { // Attempt to keep parsing if it was a similar separator. if let Some(ref tokens) = t.similar_tokens() { - if tokens.contains(&self.token.kind) { + if tokens.contains(&self.token.kind) && !unclosed_delims { self.bump(); } } @@ -882,15 +930,38 @@ impl<'a> Parser<'a> { } let frame = &self.token_cursor.frame; - match frame.tree_cursor.look_ahead(dist - 1) { - Some(tree) => match tree { - TokenTree::Token(token) => looker(token), - TokenTree::Delimited(dspan, delim, _) => { - looker(&Token::new(token::OpenDelim(*delim), dspan.open)) - } - }, - None => looker(&Token::new(token::CloseDelim(frame.delim), frame.span.close)), + if frame.delim != DelimToken::NoDelim { + let all_normal = (0..dist).all(|i| { + let token = frame.tree_cursor.look_ahead(i); + !matches!(token, Some(TokenTree::Delimited(_, DelimToken::NoDelim, _))) + }); + if all_normal { + return match frame.tree_cursor.look_ahead(dist - 1) { + Some(tree) => match tree { + TokenTree::Token(token) => looker(token), + TokenTree::Delimited(dspan, delim, _) => { + looker(&Token::new(token::OpenDelim(*delim), dspan.open)) + } + }, + None => looker(&Token::new(token::CloseDelim(frame.delim), frame.span.close)), + }; + } } + + let mut cursor = self.token_cursor.clone(); + let mut i = 0; + let mut token = Token::dummy(); + while i < dist { + token = cursor.next().0; + if matches!( + token.kind, + token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) + ) { + continue; + } + i += 1; + } + return looker(&token); } /// Returns whether any of the given keywords are `dist` tokens ahead of the current one. @@ -1302,3 +1373,24 @@ pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, sess: &Pa } } } + +/// A helper struct used when building a `AttrAnnotatedTokenStream` from +/// a `LazyTokenStream`. Both delimiter and non-delimited tokens +/// are stored as `FlatToken::Token`. A vector of `FlatToken`s +/// is then 'parsed' to build up a `AttrAnnotatedTokenStream` with nested +/// `AttrAnnotatedTokenTree::Delimited` tokens +#[derive(Debug, Clone)] +pub enum FlatToken { + /// A token - this holds both delimiter (e.g. '{' and '}') + /// and non-delimiter tokens + Token(Token), + /// Holds the `AttributesData` for an AST node. The + /// `AttributesData` is inserted directly into the + /// constructed `AttrAnnotatedTokenStream` as + /// a `AttrAnnotatedTokenTree::Attributes` + AttrTarget(AttributesData), + /// A special 'empty' token that is ignored during the conversion + /// to a `AttrAnnotatedTokenStream`. This is used to simplify the + /// handling of replace ranges. + Empty, +} diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 0c49d103583..5c4a2785d6e 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -153,9 +153,7 @@ impl<'a> Parser<'a> { NonterminalKind::Path => token::NtPath( self.collect_tokens_no_attrs(|this| this.parse_path(PathStyle::Type))?, ), - NonterminalKind::Meta => { - token::NtMeta(P(self.collect_tokens_no_attrs(|this| this.parse_attr_item(false))?)) - } + NonterminalKind::Meta => token::NtMeta(P(self.parse_attr_item(true)?)), NonterminalKind::TT => token::NtTT(self.parse_token_tree()), NonterminalKind::Vis => token::NtVis( self.collect_tokens_no_attrs(|this| this.parse_visibility(FollowedByType::Yes))?, diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 24fb4301cc2..592f64f4a39 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -48,39 +48,26 @@ impl<'a> Parser<'a> { if let token::Interpolated(nt) = &self.token.kind { if let token::NtStmt(stmt) = &**nt { let mut stmt = stmt.clone(); - return self.collect_tokens_trailing_token( - attrs, - force_collect, - |this, mut attrs| { - stmt.visit_attrs(|stmt_attrs| { - mem::swap(stmt_attrs, &mut attrs); - stmt_attrs.extend(attrs); - }); - // Make sure we capture the token::Interpolated - this.bump(); - Ok((Some(stmt), TrailingToken::None)) - }, - ); + self.bump(); + stmt.visit_attrs(|stmt_attrs| { + attrs.prepend_to_nt_inner(stmt_attrs); + }); + return Ok(Some(stmt)); } } Ok(Some(if self.token.is_keyword(kw::Let) { self.parse_local_mk(lo, attrs, capture_semi, force_collect)? } else if self.is_kw_followed_by_ident(kw::Mut) { - self.recover_stmt_local( - lo, - attrs.take_for_recovery().into(), - "missing keyword", - "let mut", - )? + self.recover_stmt_local(lo, attrs, "missing keyword", "let mut")? } else if self.is_kw_followed_by_ident(kw::Auto) { self.bump(); // `auto` let msg = "write `let` instead of `auto` to introduce a new variable"; - self.recover_stmt_local(lo, attrs.take_for_recovery().into(), msg, "let")? + self.recover_stmt_local(lo, attrs, msg, "let")? } else if self.is_kw_followed_by_ident(sym::var) { self.bump(); // `var` let msg = "write `let` instead of `var` to introduce a new variable"; - self.recover_stmt_local(lo, attrs.take_for_recovery().into(), msg, "let")? + self.recover_stmt_local(lo, attrs, msg, "let")? } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() { // We have avoided contextual keywords like `union`, items with `crate` visibility, // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something @@ -112,7 +99,7 @@ impl<'a> Parser<'a> { attrs: AttrWrapper, force_collect: ForceCollect, ) -> PResult<'a, Stmt> { - self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| { + let stmt = self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| { let path = this.parse_path(PathStyle::Expr)?; if this.eat(&token::Not) { @@ -132,14 +119,22 @@ impl<'a> Parser<'a> { }; let expr = this.with_res(Restrictions::STMT_EXPR, |this| { - let expr = this.parse_dot_or_call_expr_with(expr, lo, attrs)?; + this.parse_dot_or_call_expr_with(expr, lo, attrs) + })?; + // `DUMMY_SP` will get overwritten later in this function + Ok((this.mk_stmt(rustc_span::DUMMY_SP, StmtKind::Expr(expr)), TrailingToken::None)) + })?; + + if let StmtKind::Expr(expr) = stmt.kind { + // Perform this outside of the `collect_tokens_trailing_token` closure, + // since our outer attributes do not apply to this part of the expression + let expr = self.with_res(Restrictions::STMT_EXPR, |this| { this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr)) })?; - Ok(( - this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Expr(expr)), - TrailingToken::None, - )) - }) + Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) + } else { + Ok(stmt) + } } /// Parses a statement macro `mac!(args)` provided a `path` representing `mac`. @@ -183,7 +178,7 @@ impl<'a> Parser<'a> { fn recover_stmt_local( &mut self, lo: Span, - attrs: AttrVec, + attrs: AttrWrapper, msg: &str, sugg: &str, ) -> PResult<'a, Stmt> { @@ -213,9 +208,15 @@ impl<'a> Parser<'a> { }) } - fn recover_local_after_let(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> { - let local = self.parse_local(attrs)?; - Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Local(local))) + fn recover_local_after_let(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> { + self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { + let local = this.parse_local(attrs.into())?; + // FIXME - maybe capture semicolon in recovery? + Ok(( + this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Local(local)), + TrailingToken::None, + )) + }) } /// Parses a local variable declaration. diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index df292b14176..b1debcffd64 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1489,7 +1489,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { sym::path, sym::automatically_derived, sym::start, - sym::main, + sym::rustc_main, ]; for attr in attrs { diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index e53f821e6da..e1b750df33c 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -84,7 +84,7 @@ fn entry_point_type(ctxt: &EntryContext<'_, '_>, item: &Item<'_>, at_root: bool) let attrs = ctxt.map.attrs(item.hir_id()); if ctxt.session.contains_name(attrs, sym::start) { EntryPointType::Start - } else if ctxt.session.contains_name(attrs, sym::main) { + } else if ctxt.session.contains_name(attrs, sym::rustc_main) { EntryPointType::MainAttr } else if item.ident.name == sym::main { if at_root { @@ -111,8 +111,8 @@ fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) { if let Some(attr) = ctxt.session.find_by_name(attrs, sym::start) { throw_attr_err(&ctxt.session, attr.span, "start"); } - if let Some(attr) = ctxt.session.find_by_name(attrs, sym::main) { - throw_attr_err(&ctxt.session, attr.span, "main"); + if let Some(attr) = ctxt.session.find_by_name(attrs, sym::rustc_main) { + throw_attr_err(&ctxt.session, attr.span, "rustc_main"); } } EntryPointType::MainNamed => { @@ -193,10 +193,7 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) { err.span_note(span, "here is a function named `main`"); } err.note("you have one or more functions named `main` not defined at the crate level"); - err.help( - "either move the `main` function definitions or attach the `#[main]` attribute \ - to one of them", - ); + err.help("consider moving the `main` function definitions"); // There were some functions named `main` though. Try to give the user a hint. format!( "the main function must be defined at the crate level{}", diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 327beca218e..87e28f7fcc5 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -758,17 +758,14 @@ impl<'a> Resolver<'a> { { let mut candidates = Vec::new(); let mut seen_modules = FxHashSet::default(); - let not_local_module = crate_name.name != kw::Crate; - let mut worklist = - vec![(start_module, Vec::<ast::PathSegment>::new(), true, not_local_module)]; + let mut worklist = vec![(start_module, Vec::<ast::PathSegment>::new(), true)]; let mut worklist_via_import = vec![]; - while let Some((in_module, path_segments, accessible, in_module_is_extern)) = - match worklist.pop() { - None => worklist_via_import.pop(), - Some(x) => Some(x), - } - { + while let Some((in_module, path_segments, accessible)) = match worklist.pop() { + None => worklist_via_import.pop(), + Some(x) => Some(x), + } { + let in_module_is_extern = !in_module.def_id().unwrap().is_local(); // We have to visit module children in deterministic order to avoid // instabilities in reported imports (#43552). in_module.for_each_child(self, |this, ident, ns, name_binding| { @@ -850,11 +847,10 @@ impl<'a> Resolver<'a> { name_binding.is_extern_crate() && lookup_ident.span.rust_2018(); if !is_extern_crate_that_also_appears_in_prelude { - let is_extern = in_module_is_extern || name_binding.is_extern_crate(); // add the module to the lookup if seen_modules.insert(module.def_id().unwrap()) { if via_import { &mut worklist_via_import } else { &mut worklist } - .push((module, path_segments, child_accessible, is_extern)); + .push((module, path_segments, child_accessible)); } } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 1377bb781d0..9321f11f659 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1031,7 +1031,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } ItemKind::Static(ref ty, _, ref expr) | ItemKind::Const(_, ref ty, ref expr) => { - debug!("resolve_item ItemKind::Const"); self.with_item_rib(HasGenericParams::No, |this| { this.visit_ty(ty); if let Some(expr) = expr { @@ -1597,6 +1596,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .try_resolve_as_non_binding(pat_src, pat, bmode, ident, has_sub) .unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings)); self.r.record_partial_res(pat.id, PartialRes::new(res)); + self.r.record_pat_span(pat.id, pat.span); } PatKind::TupleStruct(ref path, ref sub_patterns) => { self.smart_resolve_path( diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 91bc8ab5ef4..a93afedf7f6 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -2719,6 +2719,13 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { Some(next) => next, None => break None, }; + // See issue #83753. If someone writes an associated type on a non-trait, just treat it as + // there being no supertrait HRTBs. + match tcx.def_kind(def_id) { + DefKind::Trait | DefKind::TraitAlias | DefKind::Impl => {} + _ => break None, + } + if trait_defines_associated_type_named(def_id) { break Some(bound_vars.into_iter().collect()); } @@ -2764,7 +2771,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { | Scope::TraitRefBoundary { ref s, .. } => { scope = *s; } - Scope::Root => bug!("In fn_like_elision without appropriate scope above"), + Scope::Root => { + // See issue #83907. Just bail out from looking inside. + self.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + "In fn_like_elision without appropriate scope above", + ); + return; + } } }; // While not strictly necessary, we gather anon lifetimes *before* actually diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 3fdd47e1ecb..129954381c9 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -891,6 +891,10 @@ pub struct Resolver<'a> { /// "self-confirming" import resolutions during import validation. unusable_binding: Option<&'a NameBinding<'a>>, + // Spans for local variables found during pattern resolution. + // Used for suggestions during error reporting. + pat_span_map: NodeMap<Span>, + /// Resolutions for nodes that have a single resolution. partial_res_map: NodeMap<PartialRes>, /// Resolutions for import nodes, which have multiple resolutions in different namespaces. @@ -1270,6 +1274,7 @@ impl<'a> Resolver<'a> { last_import_segment: false, unusable_binding: None, + pat_span_map: Default::default(), partial_res_map: Default::default(), import_res_map: Default::default(), label_res_map: Default::default(), @@ -1917,7 +1922,6 @@ impl<'a> Resolver<'a> { return Some(LexicalScopeBinding::Item(binding)); } } - self.early_resolve_ident_in_lexical_scope( orig_ident, ScopeSet::Late(ns, module, record_used_id), @@ -2394,7 +2398,59 @@ impl<'a> Resolver<'a> { .next() .map_or(false, |c| c.is_ascii_uppercase()) { - (format!("use of undeclared type `{}`", ident), None) + // Check whether the name refers to an item in the value namespace. + let suggestion = if ribs.is_some() { + let match_span = match self.resolve_ident_in_lexical_scope( + ident, + ValueNS, + parent_scope, + None, + path_span, + &ribs.unwrap()[ValueNS], + ) { + // Name matches a local variable. For example: + // ``` + // fn f() { + // let Foo: &str = ""; + // println!("{}", Foo::Bar); // Name refers to local + // // variable `Foo`. + // } + // ``` + Some(LexicalScopeBinding::Res(Res::Local(id))) => { + Some(*self.pat_span_map.get(&id).unwrap()) + } + + // Name matches item from a local name binding + // created by `use` declaration. For example: + // ``` + // pub Foo: &str = ""; + // + // mod submod { + // use super::Foo; + // println!("{}", Foo::Bar); // Name refers to local + // // binding `Foo`. + // } + // ``` + Some(LexicalScopeBinding::Item(name_binding)) => { + Some(name_binding.span) + } + _ => None, + }; + + if let Some(span) = match_span { + Some(( + vec![(span, String::from(""))], + format!("`{}` is defined here, but is not a type", ident), + Applicability::MaybeIncorrect, + )) + } else { + None + } + } else { + None + }; + + (format!("use of undeclared type `{}`", ident), suggestion) } else { (format!("use of undeclared crate or module `{}`", ident), None) } @@ -2805,6 +2861,11 @@ impl<'a> Resolver<'a> { } } + fn record_pat_span(&mut self, node: NodeId, span: Span) { + debug!("(recording pat) recording {:?} for {:?}", node, span); + self.pat_span_map.insert(node, span); + } + fn is_accessible_from(&self, vis: ty::Visibility, module: Module<'a>) -> bool { vis.is_accessible_from(module.nearest_parent_mod, self) } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 75bd8880b34..b6b349e4a80 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -18,7 +18,7 @@ use rustc_serialize::json; use crate::parse::CrateConfig; use rustc_feature::UnstableFeatures; -use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST}; +use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION}; use rustc_span::source_map::{FileName, FilePathMapping}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::SourceFileHashAlgorithm; @@ -1320,13 +1320,16 @@ pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition { }; if !edition.is_stable() && !nightly_options::is_unstable_enabled(matches) { - early_error( - ErrorOutputType::default(), - &format!( - "edition {} is unstable and only available with -Z unstable-options.", - edition, - ), - ) + let is_nightly = nightly_options::match_is_nightly_build(matches); + let msg = if !is_nightly { + format!( + "the crate requires edition {}, but the latest edition supported by this Rust version is {}", + edition, LATEST_STABLE_EDITION + ) + } else { + format!("edition {} is unstable and only available with -Z unstable-options", edition) + }; + early_error(ErrorOutputType::default(), &msg) } edition diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index a184608ed29..fd26f50da5a 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -814,7 +814,7 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "a single extra argument to append to the linker invocation (can be used several times)"), link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED], "extra arguments to append to the linker invocation (space separated)"), - link_dead_code: Option<bool> = (None, parse_opt_bool, [UNTRACKED], + link_dead_code: Option<bool> = (None, parse_opt_bool, [TRACKED], "keep dead code at link time (useful for code coverage) (default: no)"), link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED], "control whether to link Rust provided C objects/libraries or rely diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3488efacd11..cc2583be944 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -807,8 +807,11 @@ impl Session { // This is used to control the emission of the `uwtable` attribute on // LLVM functions. // - // At the very least, unwind tables are needed when compiling with - // `-C panic=unwind`. + // Unwind tables are needed when compiling with `-C panic=unwind`, but + // LLVM won't omit unwind tables unless the function is also marked as + // `nounwind`, so users are allowed to disable `uwtable` emission. + // Historically rustc always emits `uwtable` attributes by default, so + // even they can be disabled, they're still emitted by default. // // On some targets (including windows), however, exceptions include // other events such as illegal instructions, segfaults, etc. This means @@ -821,13 +824,10 @@ impl Session { // If a target requires unwind tables, then they must be emitted. // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>` // value, if it is provided, or disable them, if not. - if self.panic_strategy() == PanicStrategy::Unwind { - true - } else if self.target.requires_uwtable { - true - } else { - self.opts.cg.force_unwind_tables.unwrap_or(self.target.default_uwtable) - } + self.target.requires_uwtable + || self.opts.cg.force_unwind_tables.unwrap_or( + self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable, + ) } /// Returns the symbol name for the registrar function, @@ -1483,13 +1483,6 @@ fn validate_commandline_args_with_session_available(sess: &Session) { // Unwind tables cannot be disabled if the target requires them. if let Some(include_uwtables) = sess.opts.cg.force_unwind_tables { - if sess.panic_strategy() == PanicStrategy::Unwind && !include_uwtables { - sess.err( - "panic=unwind requires unwind tables, they cannot be disabled \ - with `-C force-unwind-tables=no`.", - ); - } - if sess.target.requires_uwtable && !include_uwtables { sess.err( "target requires unwind tables, they cannot be disabled with \ diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index f3d33309124..e9d597d1ba6 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -1,7 +1,13 @@ +use crate::parse::ParseSess; use crate::session::Session; +use rustc_ast::token::{self, DelimToken, Nonterminal, Token}; +use rustc_ast::tokenstream::CanSynthesizeMissingTokens; +use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_data_structures::profiling::VerboseTimingGuard; use std::path::{Path, PathBuf}; +pub type NtToTokenstream = fn(&Nonterminal, &ParseSess, CanSynthesizeMissingTokens) -> TokenStream; + impl Session { pub fn timer<'a>(&'a self, what: &'static str) -> VerboseTimingGuard<'a> { self.prof.verbose_generic_activity(what) @@ -53,3 +59,52 @@ impl CanonicalizedPath { &self.original } } + +// FIXME: Find a better spot for this - it needs to be accessible from `rustc_ast_lowering`, +// and needs to access `ParseSess +pub struct FlattenNonterminals<'a> { + pub parse_sess: &'a ParseSess, + pub synthesize_tokens: CanSynthesizeMissingTokens, + pub nt_to_tokenstream: NtToTokenstream, +} + +impl<'a> FlattenNonterminals<'a> { + pub fn process_token_stream(&mut self, tokens: TokenStream) -> TokenStream { + fn can_skip(stream: &TokenStream) -> bool { + stream.trees().all(|tree| match tree { + TokenTree::Token(token) => !matches!(token.kind, token::Interpolated(_)), + TokenTree::Delimited(_, _, inner) => can_skip(&inner), + }) + } + + if can_skip(&tokens) { + return tokens; + } + + tokens.into_trees().flat_map(|tree| self.process_token_tree(tree).into_trees()).collect() + } + + pub fn process_token_tree(&mut self, tree: TokenTree) -> TokenStream { + match tree { + TokenTree::Token(token) => self.process_token(token), + TokenTree::Delimited(span, delim, tts) => { + TokenTree::Delimited(span, delim, self.process_token_stream(tts)).into() + } + } + } + + pub fn process_token(&mut self, token: Token) -> TokenStream { + match token.kind { + token::Interpolated(nt) => { + let tts = (self.nt_to_tokenstream)(&nt, self.parse_sess, self.synthesize_tokens); + TokenTree::Delimited( + DelimSpan::from_single(token.span), + DelimToken::NoDelim, + self.process_token_stream(tts), + ) + .into() + } + _ => TokenTree::Token(token).into(), + } + } +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ee1d206095e..52270f0e627 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -900,6 +900,8 @@ symbols! { profiler_runtime, ptr_guaranteed_eq, ptr_guaranteed_ne, + ptr_null, + ptr_null_mut, ptr_offset_from, pub_macro_rules, pub_restricted, @@ -1006,6 +1008,7 @@ symbols! { rustc_layout_scalar_valid_range_start, rustc_legacy_const_generics, rustc_macro_transparency, + rustc_main, rustc_mir, rustc_nonnull_optimization_guaranteed, rustc_object_lifetime_default, @@ -1101,6 +1104,7 @@ symbols! { simd_reduce_or, simd_reduce_xor, simd_rem, + simd_round, simd_saturating_add, simd_saturating_sub, simd_scatter, @@ -1109,6 +1113,7 @@ symbols! { simd_shl, simd_shr, simd_sub, + simd_trunc, simd_xor, since, sinf32, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 57b0a36e009..2af46693449 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1446,8 +1446,8 @@ impl Target { let get_req_field = |name: &str| { obj.find(name) - .map(|s| s.as_string()) - .and_then(|os| os.map(|s| s.to_string())) + .and_then(Json::as_string) + .map(str::to_string) .ok_or_else(|| format!("Field {} in target specification is required", name)) }; diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 845375f3e32..7a297f2c65f 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -82,7 +82,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if param_type.is_suggestable() { err.span_suggestion( tcx.def_span(src_def_id), - "consider changing this type paramater to a `const`-generic", + "consider changing this type parameter to be a `const` generic", format!("const {}: {}", param_name, param_type), Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 16c344e8e2b..b760a54f08c 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -359,6 +359,21 @@ impl<'a, 'tcx> CastCheck<'tcx> { { sugg = Some(format!("&{}", mutbl.prefix_str())); } + } else if let ty::RawPtr(TypeAndMut { mutbl, .. }) = *self.cast_ty.kind() { + if fcx + .try_coerce( + self.expr, + fcx.tcx.mk_ref( + &ty::RegionKind::ReErased, + TypeAndMut { ty: self.expr_ty, mutbl }, + ), + self.cast_ty, + AllowTwoPhase::No, + ) + .is_ok() + { + sugg = Some(format!("&{}", mutbl.prefix_str())); + } } if let Some(sugg) = sugg { err.span_label(self.span, "invalid cast"); diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 37538267b86..fd7c50e9788 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -1492,28 +1492,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) { self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output); } - - if let Some(sp) = fcx.ret_coercion_span.get() { - // If the closure has an explicit return type annotation, - // then a type error may occur at the first return expression we - // see in the closure (if it conflicts with the declared - // return type). Skip adding a note in this case, since it - // would be incorrect. - if !err.span.primary_spans().iter().any(|&span| span == sp) { - let hir = fcx.tcx.hir(); - let body_owner = hir.body_owned_by(hir.enclosing_body_owner(fcx.body_id)); - if fcx.tcx.is_closure(hir.body_owner_def_id(body_owner).to_def_id()) { - err.span_note( - sp, - &format!( - "return type inferred to be `{}` here", - fcx.resolve_vars_if_possible(expected) - ), - ); - } - } - } - err } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index f044daa4509..60ca562f992 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -278,9 +278,8 @@ fn compare_predicate_entailment<'tcx>( if let Err(terr) = sub_result { debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); - let (impl_err_span, trait_err_span) = extract_spans_for_error_reporting( - &infcx, param_env, &terr, &cause, impl_m, impl_sig, trait_m, trait_sig, - ); + let (impl_err_span, trait_err_span) = + extract_spans_for_error_reporting(&infcx, &terr, &cause, impl_m, trait_m); cause.make_mut().span = impl_err_span; @@ -291,18 +290,79 @@ fn compare_predicate_entailment<'tcx>( "method `{}` has an incompatible type for trait", trait_m.ident ); - if let TypeError::Mutability = terr { - if let Some(trait_err_span) = trait_err_span { - if let Ok(trait_err_str) = tcx.sess.source_map().span_to_snippet(trait_err_span) + match &terr { + TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) + if trait_m.fn_has_self_parameter => + { + let ty = trait_sig.inputs()[0]; + let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) { + ExplicitSelf::ByValue => "self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Mut) => { + "&mut self".to_owned() + } + _ => format!("self: {}", ty), + }; + + // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the + // span points only at the type `Box<Self`>, but we want to cover the whole + // argument pattern and type. + let impl_m_hir_id = + tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); + let span = match tcx.hir().expect_impl_item(impl_m_hir_id).kind { + ImplItemKind::Fn(ref sig, body) => tcx + .hir() + .body_param_names(body) + .zip(sig.decl.inputs.iter()) + .map(|(param, ty)| param.span.to(ty.span)) + .next() + .unwrap_or(impl_err_span), + _ => bug!("{:?} is not a method", impl_m), + }; + + diag.span_suggestion( + span, + "change the self-receiver type to match the trait", + sugg, + Applicability::MachineApplicable, + ); + } + TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => { + if trait_sig.inputs().len() == *i { + // Suggestion to change output type. We do not suggest in `async` functions + // to avoid complex logic or incorrect output. + let impl_m_hir_id = + tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); + match tcx.hir().expect_impl_item(impl_m_hir_id).kind { + ImplItemKind::Fn(ref sig, _) + if sig.header.asyncness == hir::IsAsync::NotAsync => + { + let msg = "change the output type to match the trait"; + let ap = Applicability::MachineApplicable; + match sig.decl.output { + hir::FnRetTy::DefaultReturn(sp) => { + let sugg = format!("-> {} ", trait_sig.output()); + diag.span_suggestion_verbose(sp, msg, sugg, ap); + } + hir::FnRetTy::Return(hir_ty) => { + let sugg = trait_sig.output().to_string(); + diag.span_suggestion(hir_ty.span, msg, sugg, ap); + } + }; + } + _ => {} + }; + } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { diag.span_suggestion( impl_err_span, - "consider changing the mutability to match the trait", - trait_err_str, + "change the parameter type to match the trait", + trait_ty.to_string(), Applicability::MachineApplicable, ); } } + _ => {} } infcx.note_type_err( @@ -385,86 +445,35 @@ fn check_region_bounds_on_impl_item<'tcx>( fn extract_spans_for_error_reporting<'a, 'tcx>( infcx: &infer::InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, terr: &TypeError<'_>, cause: &ObligationCause<'tcx>, impl_m: &ty::AssocItem, - impl_sig: ty::FnSig<'tcx>, trait_m: &ty::AssocItem, - trait_sig: ty::FnSig<'tcx>, ) -> (Span, Option<Span>) { let tcx = infcx.tcx; let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); - let (impl_m_output, impl_m_iter) = match tcx.hir().expect_impl_item(impl_m_hir_id).kind { - ImplItemKind::Fn(ref impl_m_sig, _) => { - (&impl_m_sig.decl.output, impl_m_sig.decl.inputs.iter()) + let mut impl_args = match tcx.hir().expect_impl_item(impl_m_hir_id).kind { + ImplItemKind::Fn(ref sig, _) => { + sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())) } _ => bug!("{:?} is not a method", impl_m), }; - - match *terr { - TypeError::Mutability => { - if let Some(def_id) = trait_m.def_id.as_local() { - let trait_m_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let trait_m_iter = match tcx.hir().expect_trait_item(trait_m_hir_id).kind { - TraitItemKind::Fn(ref trait_m_sig, _) => trait_m_sig.decl.inputs.iter(), - _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), - }; - - iter::zip(impl_m_iter, trait_m_iter) - .find(|&(ref impl_arg, ref trait_arg)| { - match (&impl_arg.kind, &trait_arg.kind) { - ( - &hir::TyKind::Rptr(_, ref impl_mt), - &hir::TyKind::Rptr(_, ref trait_mt), - ) - | (&hir::TyKind::Ptr(ref impl_mt), &hir::TyKind::Ptr(ref trait_mt)) => { - impl_mt.mutbl != trait_mt.mutbl - } - _ => false, - } - }) - .map(|(ref impl_arg, ref trait_arg)| (impl_arg.span, Some(trait_arg.span))) - .unwrap_or_else(|| (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id))) - } else { - (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id)) + let trait_args = trait_m.def_id.as_local().map(|def_id| { + let trait_m_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + match tcx.hir().expect_trait_item(trait_m_hir_id).kind { + TraitItemKind::Fn(ref sig, _) => { + sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())) } + _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), } - TypeError::Sorts(ExpectedFound { .. }) => { - if let Some(def_id) = trait_m.def_id.as_local() { - let trait_m_hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let (trait_m_output, trait_m_iter) = - match tcx.hir().expect_trait_item(trait_m_hir_id).kind { - TraitItemKind::Fn(ref trait_m_sig, _) => { - (&trait_m_sig.decl.output, trait_m_sig.decl.inputs.iter()) - } - _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), - }; + }); - let impl_iter = impl_sig.inputs().iter(); - let trait_iter = trait_sig.inputs().iter(); - iter::zip(iter::zip(impl_iter, trait_iter), iter::zip(impl_m_iter, trait_m_iter)) - .find_map(|((&impl_arg_ty, &trait_arg_ty), (impl_arg, trait_arg))| match infcx - .at(&cause, param_env) - .sub(trait_arg_ty, impl_arg_ty) - { - Ok(_) => None, - Err(_) => Some((impl_arg.span, Some(trait_arg.span))), - }) - .unwrap_or_else(|| { - if infcx - .at(&cause, param_env) - .sup(trait_sig.output(), impl_sig.output()) - .is_err() - { - (impl_m_output.span(), Some(trait_m_output.span())) - } else { - (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id)) - } - }) - } else { - (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id)) - } + match *terr { + TypeError::ArgumentMutability(i) => { + (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i))) + } + TypeError::ArgumentSorts(ExpectedFound { .. }, i) => { + (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i))) } _ => (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id)), } @@ -514,8 +523,7 @@ fn compare_self_type<'tcx>( tcx.sess, impl_m_span, E0185, - "method `{}` has a `{}` declaration in the impl, but \ - not in the trait", + "method `{}` has a `{}` declaration in the impl, but not in the trait", trait_m.ident, self_descr ); @@ -535,8 +543,7 @@ fn compare_self_type<'tcx>( tcx.sess, impl_m_span, E0186, - "method `{}` has a `{}` declaration in the trait, but \ - not in the impl", + "method `{}` has a `{}` declaration in the trait, but not in the impl", trait_m.ident, self_descr ); @@ -993,8 +1000,7 @@ crate fn compare_const_impl<'tcx>( tcx.sess, cause.span, E0326, - "implemented const `{}` has an incompatible type for \ - trait", + "implemented const `{}` has an incompatible type for trait", trait_c.ident ); diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index d879b6e97dc..e5fcdcfa743 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -37,6 +37,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_missing_parentheses(err, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); self.note_internal_mutation_in_method(err, expr, expected, expr_ty); + self.report_closure_infered_return_type(err, expected) } // Requires that the two types unify, and prints an error message if @@ -1061,4 +1062,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => false, } } + + // Report the type inferred by the return statement. + fn report_closure_infered_return_type( + &self, + err: &mut DiagnosticBuilder<'_>, + expected: Ty<'tcx>, + ) { + if let Some(sp) = self.ret_coercion_span.get() { + // If the closure has an explicit return type annotation, + // then a type error may occur at the first return expression we + // see in the closure (if it conflicts with the declared + // return type). Skip adding a note in this case, since it + // would be incorrect. + if !err.span.primary_spans().iter().any(|&span| span == sp) { + let hir = self.tcx.hir(); + let body_owner = hir.body_owned_by(hir.enclosing_body_owner(self.body_id)); + if self.tcx.is_closure(hir.body_owner_def_id(body_owner).to_def_id()) { + err.span_note( + sp, + &format!( + "return type inferred to be `{}` here", + self.resolve_vars_if_possible(expected) + ), + ); + } + } + } + } } diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 303a77507cf..5741b6824b5 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -407,8 +407,8 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) | sym::simd_fpow | sym::simd_saturating_add | sym::simd_saturating_sub => (1, vec![param(0), param(0)], param(0)), - sym::simd_neg => (1, vec![param(0)], param(0)), - sym::simd_fsqrt + sym::simd_neg + | sym::simd_fsqrt | sym::simd_fsin | sym::simd_fcos | sym::simd_fexp @@ -417,8 +417,10 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) | sym::simd_flog10 | sym::simd_flog | sym::simd_fabs + | sym::simd_ceil | sym::simd_floor - | sym::simd_ceil => (1, vec![param(0)], param(0)), + | sym::simd_round + | sym::simd_trunc => (1, vec![param(0)], param(0)), sym::simd_fpowi => (1, vec![param(0), tcx.types.i32], param(0)), sym::simd_fma => (1, vec![param(0), param(0), param(0)], param(0)), sym::simd_gather => (3, vec![param(0), param(1), param(2)], param(0)), diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 04033905728..ef37fef0455 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -793,7 +793,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> { /// use std::alloc::{Allocator, Layout, System}; /// /// unsafe { - /// let ptr = System.allocate(Layout::new::<i32>())?.as_mut_ptr(); + /// let ptr = System.allocate(Layout::new::<i32>())?.as_mut_ptr() as *mut i32; /// // In general .write is required to avoid attempting to destruct /// // the (uninitialized) previous contents of `ptr`, though for this /// // simple example `*ptr = 5` would have worked as well. diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index a0dbb289252..971244718b4 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -940,7 +940,6 @@ impl<K, V> BTreeMap<K, V> { /// # Examples /// /// ``` - /// #![feature(btree_retain)] /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap<i32, i32> = (0..8).map(|x| (x, x*10)).collect(); @@ -949,7 +948,7 @@ impl<K, V> BTreeMap<K, V> { /// assert!(map.into_iter().eq(vec![(0, 0), (2, 20), (4, 40), (6, 60)])); /// ``` #[inline] - #[unstable(feature = "btree_retain", issue = "79025")] + #[stable(feature = "btree_retain", since = "1.53.0")] pub fn retain<F>(&mut self, mut f: F) where K: Ord, diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index a331b8d8e4b..737932d931c 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -851,7 +851,6 @@ impl<T> BTreeSet<T> { /// # Examples /// /// ``` - /// #![feature(btree_retain)] /// use std::collections::BTreeSet; /// /// let xs = [1, 2, 3, 4, 5, 6]; @@ -860,7 +859,7 @@ impl<T> BTreeSet<T> { /// set.retain(|&k| k % 2 == 0); /// assert!(set.iter().eq([2, 4, 6].iter())); /// ``` - #[unstable(feature = "btree_retain", issue = "79025")] + #[stable(feature = "btree_retain", since = "1.53.0")] pub fn retain<F>(&mut self, mut f: F) where T: Ord, diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index d3e70991ad5..7d6fbf1c438 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -2403,6 +2403,12 @@ impl<T> VecDeque<T> { /// [`Result::Err`] is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// + /// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`]. + /// + /// [`binary_search_by`]: VecDeque::binary_search_by + /// [`binary_search_by_key`]: VecDeque::binary_search_by_key + /// [`partition_point`]: VecDeque::partition_point + /// /// # Examples /// /// Looks up a series of four elements. The first is found, with a @@ -2457,6 +2463,12 @@ impl<T> VecDeque<T> { /// [`Result::Err`] is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// + /// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`]. + /// + /// [`binary_search`]: VecDeque::binary_search + /// [`binary_search_by_key`]: VecDeque::binary_search_by_key + /// [`partition_point`]: VecDeque::partition_point + /// /// # Examples /// /// Looks up a series of four elements. The first is found, with a @@ -2481,8 +2493,11 @@ impl<T> VecDeque<T> { F: FnMut(&'a T) -> Ordering, { let (front, back) = self.as_slices(); + let cmp_back = back.first().map(|elem| f(elem)); - if let Some(Ordering::Less | Ordering::Equal) = back.first().map(|elem| f(elem)) { + if let Some(Ordering::Equal) = cmp_back { + Ok(front.len()) + } else if let Some(Ordering::Less) = cmp_back { back.binary_search_by(f).map(|idx| idx + front.len()).map_err(|idx| idx + front.len()) } else { front.binary_search_by(f) @@ -2492,8 +2507,7 @@ impl<T> VecDeque<T> { /// Binary searches this sorted `VecDeque` with a key extraction function. /// /// Assumes that the `VecDeque` is sorted by the key, for instance with - /// [`make_contiguous().sort_by_key()`](#method.make_contiguous) using the same - /// key extraction function. + /// [`make_contiguous().sort_by_key()`] using the same key extraction function. /// /// If the value is found then [`Result::Ok`] is returned, containing the /// index of the matching element. If there are multiple matches, then any @@ -2501,6 +2515,13 @@ impl<T> VecDeque<T> { /// [`Result::Err`] is returned, containing the index where a matching /// element could be inserted while maintaining sorted order. /// + /// See also [`binary_search`], [`binary_search_by`], and [`partition_point`]. + /// + /// [`make_contiguous().sort_by_key()`]: VecDeque::make_contiguous + /// [`binary_search`]: VecDeque::binary_search + /// [`binary_search_by`]: VecDeque::binary_search_by + /// [`partition_point`]: VecDeque::partition_point + /// /// # Examples /// /// Looks up a series of four elements in a slice of pairs sorted by @@ -2531,6 +2552,51 @@ impl<T> VecDeque<T> { { self.binary_search_by(|k| f(k).cmp(b)) } + + /// Returns the index of the partition point according to the given predicate + /// (the index of the first element of the second partition). + /// + /// The deque is assumed to be partitioned according to the given predicate. + /// This means that all elements for which the predicate returns true are at the start of the deque + /// and all elements for which the predicate returns false are at the end. + /// For example, [7, 15, 3, 5, 4, 12, 6] is a partitioned under the predicate x % 2 != 0 + /// (all odd numbers are at the start, all even at the end). + /// + /// If this deque is not partitioned, the returned result is unspecified and meaningless, + /// as this method performs a kind of binary search. + /// + /// See also [`binary_search`], [`binary_search_by`], and [`binary_search_by_key`]. + /// + /// [`binary_search`]: VecDeque::binary_search + /// [`binary_search_by`]: VecDeque::binary_search_by + /// [`binary_search_by_key`]: VecDeque::binary_search_by_key + /// + /// # Examples + /// + /// ``` + /// #![feature(vecdeque_binary_search)] + /// use std::collections::VecDeque; + /// + /// let deque: VecDeque<_> = vec![1, 2, 3, 3, 5, 6, 7].into(); + /// let i = deque.partition_point(|&x| x < 5); + /// + /// assert_eq!(i, 4); + /// assert!(deque.iter().take(i).all(|&x| x < 5)); + /// assert!(deque.iter().skip(i).all(|&x| !(x < 5))); + /// ``` + #[unstable(feature = "vecdeque_binary_search", issue = "78021")] + pub fn partition_point<P>(&self, mut pred: P) -> usize + where + P: FnMut(&T) -> bool, + { + let (front, back) = self.as_slices(); + + if let Some(true) = back.first().map(|v| pred(v)) { + back.partition_point(pred) + front.len() + } else { + front.partition_point(pred) + } + } } impl<T: Clone> VecDeque<T> { diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index c81ababf151..cb4af7c5cd1 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2330,8 +2330,8 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for Weak<T> { #[stable(feature = "downgraded_weak", since = "1.10.0")] impl<T> Default for Weak<T> { - /// Constructs a new `Weak<T>`, allocating memory for `T` without initializing - /// it. Calling [`upgrade`] on the return value always gives [`None`]. + /// Constructs a new `Weak<T>`, without allocating any memory. + /// Calling [`upgrade`] on the return value always gives [`None`]. /// /// [`None`]: Option /// [`upgrade`]: Weak::upgrade diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 91c3b16deee..0dab0358d6e 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2567,7 +2567,7 @@ impl<T, A: Allocator> Vec<T, A> { /// # let some_predicate = |x: &mut i32| { *x == 2 || *x == 3 || *x == 6 }; /// # let mut vec = vec![1, 2, 3, 4, 5, 6]; /// let mut i = 0; - /// while i != vec.len() { + /// while i < vec.len() { /// if some_predicate(&mut vec[i]) { /// let val = vec.remove(i); /// // your code here diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs index e132befcfa5..c6f4f22a01f 100644 --- a/library/alloc/src/vec/spec_extend.rs +++ b/library/alloc/src/vec/spec_extend.rs @@ -26,15 +26,13 @@ where default fn spec_extend(&mut self, iterator: I) { // This is the case for a TrustedLen iterator. let (low, high) = iterator.size_hint(); - if let Some(high_value) = high { + if let Some(additional) = high { debug_assert_eq!( low, - high_value, + additional, "TrustedLen iterator's size hint is not exact: {:?}", (low, high) ); - } - if let Some(additional) = high { self.reserve(additional); unsafe { let mut ptr = self.as_mut_ptr().add(self.len()); diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index 0919b1325bc..d7140cf9759 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -1700,6 +1700,24 @@ fn test_binary_search_by_key() { } #[test] +fn test_partition_point() { + // Contiguous (front only) search: + let deque: VecDeque<_> = vec![1, 2, 3, 5, 6].into(); + assert!(deque.as_slices().1.is_empty()); + assert_eq!(deque.partition_point(|&v| v <= 3), 3); + + // Split search (both front & back non-empty): + let mut deque: VecDeque<_> = vec![5, 6].into(); + deque.push_front(3); + deque.push_front(2); + deque.push_front(1); + deque.push_back(10); + assert!(!deque.as_slices().0.is_empty()); + assert!(!deque.as_slices().1.is_empty()); + assert_eq!(deque.partition_point(|&v| v <= 5), 4); +} + +#[test] fn test_zero_sized_push() { const N: usize = 8; diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 8f52985d1df..b6ce825e247 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -12,7 +12,6 @@ use crate::convert::{Infallible, TryFrom}; use crate::fmt; use crate::hash::{self, Hash}; use crate::iter::TrustedLen; -use crate::marker::Unsize; use crate::mem::{self, MaybeUninit}; use crate::ops::{Index, IndexMut}; use crate::slice::{Iter, IterMut}; @@ -36,41 +35,6 @@ pub fn from_mut<T>(s: &mut T) -> &mut [T; 1] { unsafe { &mut *(s as *mut T).cast::<[T; 1]>() } } -/// Utility trait implemented only on arrays of fixed size -/// -/// This trait can be used to implement other traits on fixed-size arrays -/// without causing much metadata bloat. -/// -/// The trait is marked unsafe in order to restrict implementors to fixed-size -/// arrays. A user of this trait can assume that implementors have the exact -/// layout in memory of a fixed size array (for example, for unsafe -/// initialization). -/// -/// Note that the traits [`AsRef`] and [`AsMut`] provide similar methods for types that -/// may not be fixed-size arrays. Implementors should prefer those traits -/// instead. -#[unstable(feature = "fixed_size_array", issue = "27778")] -pub unsafe trait FixedSizeArray<T> { - /// Converts the array to immutable slice - #[unstable(feature = "fixed_size_array", issue = "27778")] - fn as_slice(&self) -> &[T]; - /// Converts the array to mutable slice - #[unstable(feature = "fixed_size_array", issue = "27778")] - fn as_mut_slice(&mut self) -> &mut [T]; -} - -#[unstable(feature = "fixed_size_array", issue = "27778")] -unsafe impl<T, A: Unsize<[T]>> FixedSizeArray<T> for A { - #[inline] - fn as_slice(&self) -> &[T] { - self - } - #[inline] - fn as_mut_slice(&mut self) -> &mut [T] { - self - } -} - /// The error type returned when a conversion from a slice to an array fails. #[stable(feature = "try_from", since = "1.34.0")] #[derive(Debug, Copy, Clone)] diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 1dbf47206e4..67dd1d83415 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -982,6 +982,9 @@ pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> { #[stable(feature = "rust1", since = "1.0.0")] fn le(&self, other: &Rhs) -> bool { // Pattern `Some(Less | Eq)` optimizes worse than negating `None | Some(Greater)`. + // FIXME: The root cause was fixed upstream in LLVM with: + // https://github.com/llvm/llvm-project/commit/9bad7de9a3fb844f1ca2965f35d0c2a3d1e11775 + // Revert this workaround once support for LLVM 12 gets dropped. !matches!(self.partial_cmp(other), None | Some(Greater)) } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index d696ffa8277..59493bb0425 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -2268,7 +2268,7 @@ impl<T: ?Sized + Debug> Debug for RefMut<'_, T> { } #[stable(feature = "core_impl_debug", since = "1.9.0")] -impl<T: ?Sized + Debug> Debug for UnsafeCell<T> { +impl<T: ?Sized> Debug for UnsafeCell<T> { fn fmt(&self, f: &mut Formatter<'_>) -> Result { f.pad("UnsafeCell") } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index b5371d6b69d..1ba0b23ae5b 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1543,7 +1543,7 @@ extern "rust-intrinsic" { /// let num_trailing = unsafe { cttz_nonzero(x) }; /// assert_eq!(num_trailing, 3); /// ``` - #[rustc_const_unstable(feature = "const_cttz", issue = "none")] + #[rustc_const_stable(feature = "const_cttz", since = "1.53.0")] pub fn cttz_nonzero<T: Copy>(x: T) -> T; /// Reverses the bytes in an integer type `T`. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 760b8d8cbb0..d0c52a44591 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -79,7 +79,6 @@ #![feature(const_int_unchecked_arith)] #![feature(const_mut_refs)] #![feature(const_refs_to_cell)] -#![feature(const_cttz)] #![feature(const_panic)] #![feature(const_pin)] #![feature(const_fn)] @@ -112,7 +111,6 @@ #![cfg_attr(bootstrap, feature(doc_spotlight))] #![cfg_attr(not(bootstrap), feature(doc_notable_trait))] #![feature(duration_consts_2)] -#![feature(duration_saturating_ops)] #![feature(extended_key_value_attributes)] #![feature(extern_types)] #![feature(fundamental)] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 64342de6341..4d7d47579ee 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -736,22 +736,22 @@ impl<T> MaybeUninit<T> { /// #![feature(maybe_uninit_ref)] /// use std::mem::MaybeUninit; /// - /// # unsafe extern "C" fn initialize_buffer(buf: *mut [u8; 2048]) { *buf = [0; 2048] } + /// # unsafe extern "C" fn initialize_buffer(buf: *mut [u8; 1024]) { *buf = [0; 1024] } /// # #[cfg(FALSE)] /// extern "C" { /// /// Initializes *all* the bytes of the input buffer. - /// fn initialize_buffer(buf: *mut [u8; 2048]); + /// fn initialize_buffer(buf: *mut [u8; 1024]); /// } /// - /// let mut buf = MaybeUninit::<[u8; 2048]>::uninit(); + /// let mut buf = MaybeUninit::<[u8; 1024]>::uninit(); /// /// // Initialize `buf`: /// unsafe { initialize_buffer(buf.as_mut_ptr()); } /// // Now we know that `buf` has been initialized, so we could `.assume_init()` it. - /// // However, using `.assume_init()` may trigger a `memcpy` of the 2048 bytes. + /// // However, using `.assume_init()` may trigger a `memcpy` of the 1024 bytes. /// // To assert our buffer has been initialized without copying it, we upgrade - /// // the `&mut MaybeUninit<[u8; 2048]>` to a `&mut [u8; 2048]`: - /// let buf: &mut [u8; 2048] = unsafe { + /// // the `&mut MaybeUninit<[u8; 1024]>` to a `&mut [u8; 1024]`: + /// let buf: &mut [u8; 1024] = unsafe { /// // SAFETY: `buf` has been initialized. /// buf.assume_init_mut() /// }; diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 690247bc96d..0d6d919d998 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -500,7 +500,6 @@ impl f32 { /// Returns `true` if the number is [subnormal]. /// /// ``` - /// #![feature(is_subnormal)] /// let min = f32::MIN_POSITIVE; // 1.17549435e-38f32 /// let max = f32::MAX; /// let lower_than_min = 1.0e-40_f32; @@ -516,7 +515,7 @@ impl f32 { /// assert!(lower_than_min.is_subnormal()); /// ``` /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number - #[unstable(feature = "is_subnormal", issue = "79288")] + #[stable(feature = "is_subnormal", since = "1.53.0")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] #[inline] pub const fn is_subnormal(self) -> bool { diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 6d3737224c0..42214e7b50d 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -499,7 +499,6 @@ impl f64 { /// Returns `true` if the number is [subnormal]. /// /// ``` - /// #![feature(is_subnormal)] /// let min = f64::MIN_POSITIVE; // 2.2250738585072014e-308_f64 /// let max = f64::MAX; /// let lower_than_min = 1.0e-308_f64; @@ -515,7 +514,7 @@ impl f64 { /// assert!(lower_than_min.is_subnormal()); /// ``` /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number - #[unstable(feature = "is_subnormal", issue = "79288")] + #[stable(feature = "is_subnormal", since = "1.53.0")] #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] #[inline] pub const fn is_subnormal(self) -> bool { diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 9632e64f180..608deb49a9d 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -79,7 +79,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[doc(alias = "popcount")] #[doc(alias = "popcnt")] - #[inline] + #[inline(always)] pub const fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() } /// Returns the number of zeros in the binary representation of `self`. @@ -93,7 +93,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn count_zeros(self) -> u32 { (!self).count_ones() } @@ -111,7 +111,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn leading_zeros(self) -> u32 { (self as $UnsignedT).leading_zeros() } @@ -129,7 +129,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn trailing_zeros(self) -> u32 { (self as $UnsignedT).trailing_zeros() } @@ -147,7 +147,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "leading_trailing_ones", since = "1.46.0")] #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] + #[inline(always)] pub const fn leading_ones(self) -> u32 { (self as $UnsignedT).leading_ones() } @@ -165,7 +165,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "leading_trailing_ones", since = "1.46.0")] #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] + #[inline(always)] pub const fn trailing_ones(self) -> u32 { (self as $UnsignedT).trailing_ones() } @@ -189,7 +189,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn rotate_left(self, n: u32) -> Self { (self as $UnsignedT).rotate_left(n) as Self } @@ -214,7 +214,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn rotate_right(self, n: u32) -> Self { (self as $UnsignedT).rotate_right(n) as Self } @@ -234,7 +234,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn swap_bytes(self) -> Self { (self as $UnsignedT).swap_bytes() as Self } @@ -255,7 +255,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "reverse_bits", since = "1.37.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] #[must_use] pub const fn reverse_bits(self) -> Self { (self as $UnsignedT).reverse_bits() as Self @@ -416,7 +416,7 @@ macro_rules! int_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub unsafe fn unchecked_add(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_add`. @@ -454,7 +454,7 @@ macro_rules! int_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_sub`. @@ -492,7 +492,7 @@ macro_rules! int_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_mul`. @@ -741,7 +741,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn saturating_add(self, rhs: Self) -> Self { intrinsics::saturating_add(self, rhs) } @@ -762,7 +762,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn saturating_sub(self, rhs: Self) -> Self { intrinsics::saturating_sub(self, rhs) } @@ -783,7 +783,7 @@ macro_rules! int_impl { #[stable(feature = "saturating_neg", since = "1.45.0")] #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] + #[inline(always)] pub const fn saturating_neg(self) -> Self { intrinsics::saturating_sub(0, self) } @@ -883,7 +883,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_add(self, rhs: Self) -> Self { intrinsics::wrapping_add(self, rhs) } @@ -903,7 +903,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_sub(self, rhs: Self) -> Self { intrinsics::wrapping_sub(self, rhs) } @@ -923,7 +923,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_mul(self, rhs: Self) -> Self { intrinsics::wrapping_mul(self, rhs) } @@ -1081,7 +1081,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_shl(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -1110,7 +1110,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_shr(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -1225,7 +1225,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); (a as Self, b) @@ -1249,7 +1249,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); (a as Self, b) @@ -1272,7 +1272,7 @@ macro_rules! int_impl { #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) { let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT); (a as Self, b) @@ -1725,7 +1725,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_sign", since = "1.47.0")] - #[inline] + #[inline(always)] pub const fn signum(self) -> Self { match self { n if n > 0 => 1, @@ -1747,7 +1747,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn is_positive(self) -> bool { self > 0 } /// Returns `true` if `self` is negative and `false` if the number is zero or @@ -1763,7 +1763,7 @@ macro_rules! int_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn is_negative(self) -> bool { self < 0 } /// Return the memory representation of this integer as a byte array in diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 111feb7dbec..81262a2f918 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -191,13 +191,12 @@ macro_rules! nonzero_leading_trailing_zeros { /// Basic usage: /// /// ``` - /// #![feature(nonzero_leading_trailing_zeros)] #[doc = concat!("let n = std::num::", stringify!($Ty), "::new(", stringify!($LeadingTestExpr), ").unwrap();")] /// /// assert_eq!(n.leading_zeros(), 0); /// ``` - #[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] - #[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] + #[stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] + #[rustc_const_stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] #[inline] pub const fn leading_zeros(self) -> u32 { // SAFETY: since `self` can not be zero it is safe to call ctlz_nonzero @@ -214,13 +213,12 @@ macro_rules! nonzero_leading_trailing_zeros { /// Basic usage: /// /// ``` - /// #![feature(nonzero_leading_trailing_zeros)] #[doc = concat!("let n = std::num::", stringify!($Ty), "::new(0b0101000).unwrap();")] /// /// assert_eq!(n.trailing_zeros(), 3); /// ``` - #[unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] - #[rustc_const_unstable(feature = "nonzero_leading_trailing_zeros", issue = "79143")] + #[stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] + #[rustc_const_stable(feature = "nonzero_leading_trailing_zeros", since = "1.53.0")] #[inline] pub const fn trailing_zeros(self) -> u32 { // SAFETY: since `self` can not be zero it is safe to call cttz_nonzero diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 62d539b96c3..46638ea4463 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -79,7 +79,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_math", since = "1.32.0")] #[doc(alias = "popcount")] #[doc(alias = "popcnt")] - #[inline] + #[inline(always)] pub const fn count_ones(self) -> u32 { intrinsics::ctpop(self as $ActualT) as u32 } @@ -95,7 +95,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn count_zeros(self) -> u32 { (!self).count_ones() } @@ -113,7 +113,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn leading_zeros(self) -> u32 { intrinsics::ctlz(self as $ActualT) as u32 } @@ -132,7 +132,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn trailing_zeros(self) -> u32 { intrinsics::cttz(self) as u32 } @@ -150,7 +150,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "leading_trailing_ones", since = "1.46.0")] #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] + #[inline(always)] pub const fn leading_ones(self) -> u32 { (!self).leading_zeros() } @@ -169,7 +169,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "leading_trailing_ones", since = "1.46.0")] #[rustc_const_stable(feature = "leading_trailing_ones", since = "1.46.0")] - #[inline] + #[inline(always)] pub const fn trailing_ones(self) -> u32 { (!self).trailing_zeros() } @@ -193,7 +193,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn rotate_left(self, n: u32) -> Self { intrinsics::rotate_left(self, n as $SelfT) } @@ -218,7 +218,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn rotate_right(self, n: u32) -> Self { intrinsics::rotate_right(self, n as $SelfT) } @@ -237,7 +237,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn swap_bytes(self) -> Self { intrinsics::bswap(self as $ActualT) as Self } @@ -258,7 +258,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "reverse_bits", since = "1.37.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] #[must_use] pub const fn reverse_bits(self) -> Self { intrinsics::bitreverse(self as $ActualT) as Self @@ -284,7 +284,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn from_be(x: Self) -> Self { #[cfg(target_endian = "big")] { @@ -316,7 +316,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn from_le(x: Self) -> Self { #[cfg(target_endian = "little")] { @@ -348,7 +348,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn to_be(self) -> Self { // or not to be? #[cfg(target_endian = "big")] { @@ -380,7 +380,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn to_le(self) -> Self { #[cfg(target_endian = "little")] { @@ -426,7 +426,7 @@ macro_rules! uint_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub unsafe fn unchecked_add(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_add`. @@ -464,7 +464,7 @@ macro_rules! uint_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_sub`. @@ -502,7 +502,7 @@ macro_rules! uint_impl { )] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_mul`. @@ -727,7 +727,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] + #[inline(always)] pub const fn saturating_add(self, rhs: Self) -> Self { intrinsics::saturating_add(self, rhs) } @@ -747,7 +747,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[rustc_const_stable(feature = "const_saturating_int_methods", since = "1.47.0")] - #[inline] + #[inline(always)] pub const fn saturating_sub(self, rhs: Self) -> Self { intrinsics::saturating_sub(self, rhs) } @@ -813,7 +813,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_add(self, rhs: Self) -> Self { intrinsics::wrapping_add(self, rhs) } @@ -833,7 +833,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_sub(self, rhs: Self) -> Self { intrinsics::wrapping_sub(self, rhs) } @@ -856,7 +856,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_mul(self, rhs: Self) -> Self { intrinsics::wrapping_mul(self, rhs) } @@ -878,7 +878,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_div(self, rhs: Self) -> Self { self / rhs } @@ -903,7 +903,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_div_euclid(self, rhs: Self) -> Self { self / rhs } @@ -926,7 +926,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_rem(self, rhs: Self) -> Self { self % rhs } @@ -952,7 +952,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_rem_euclid(self, rhs: Self) -> Self { self % rhs } @@ -1008,7 +1008,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_shl(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -1040,7 +1040,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn wrapping_shr(self, rhs: u32) -> Self { // SAFETY: the masking by the bitsize of the type ensures that we do not shift // out of bounds @@ -1106,7 +1106,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) { let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT); (a as Self, b) @@ -1131,7 +1131,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) { let (a, b) = intrinsics::sub_with_overflow(self as $ActualT, rhs as $ActualT); (a as Self, b) @@ -1158,7 +1158,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_mul(self, rhs: Self) -> (Self, bool) { let (a, b) = intrinsics::mul_with_overflow(self as $ActualT, rhs as $ActualT); (a as Self, b) @@ -1182,7 +1182,7 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div(2), (2, false));")] /// ``` - #[inline] + #[inline(always)] #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_overflowing_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ @@ -1212,7 +1212,7 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_div_euclid(2), (2, false));")] /// ``` - #[inline] + #[inline(always)] #[stable(feature = "euclidean_division", since = "1.38.0")] #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ @@ -1239,7 +1239,7 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem(2), (1, false));")] /// ``` - #[inline] + #[inline(always)] #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_overflowing_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ @@ -1269,7 +1269,7 @@ macro_rules! uint_impl { /// ``` #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".overflowing_rem_euclid(2), (1, false));")] /// ``` - #[inline] + #[inline(always)] #[stable(feature = "euclidean_division", since = "1.38.0")] #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ @@ -1293,7 +1293,7 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".overflowing_neg(), (0, false));")] #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".overflowing_neg(), (-2i32 as ", stringify!($SelfT), ", true));")] /// ``` - #[inline] + #[inline(always)] #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] pub const fn overflowing_neg(self) -> (Self, bool) { @@ -1320,7 +1320,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_shl(self, rhs: u32) -> (Self, bool) { (self.wrapping_shl(rhs), (rhs > ($BITS - 1))) } @@ -1345,7 +1345,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { (self.wrapping_shr(rhs), (rhs > ($BITS - 1))) } @@ -1458,7 +1458,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] #[rustc_inherit_overflow_checks] pub const fn div_euclid(self, rhs: Self) -> Self { self / rhs @@ -1486,7 +1486,7 @@ macro_rules! uint_impl { #[rustc_const_stable(feature = "const_euclidean_int_methods", since = "1.52.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] - #[inline] + #[inline(always)] #[rustc_inherit_overflow_checks] pub const fn rem_euclid(self, rhs: Self) -> Self { self % rhs @@ -1504,7 +1504,7 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_is_power_of_two", since = "1.32.0")] - #[inline] + #[inline(always)] pub const fn is_power_of_two(self) -> bool { self.count_ones() == 1 } diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index ce7d1c3d06d..f4b1ec377d4 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -78,7 +78,7 @@ /// /// In other words, if you tried to explicitly call `Drop::drop` in the above example, you'd get a compiler error. /// -/// If you'd like explicitly call the destructor of a value, [`mem::drop`] can be used instead. +/// If you'd like to explicitly call the destructor of a value, [`mem::drop`] can be used instead. /// /// [`mem::drop`]: drop /// diff --git a/library/core/src/option.rs b/library/core/src/option.rs index da2c0cf476d..1e1e9613696 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -601,8 +601,6 @@ impl<T> Option<T> { /// # Example /// /// ``` - /// #![feature(option_insert)] - /// /// let mut opt = None; /// let val = opt.insert(1); /// assert_eq!(*val, 1); @@ -613,7 +611,7 @@ impl<T> Option<T> { /// assert_eq!(opt.unwrap(), 3); /// ``` #[inline] - #[unstable(feature = "option_insert", reason = "newly added", issue = "78271")] + #[stable(feature = "option_insert", since = "1.53.0")] pub fn insert(&mut self, value: T) -> &mut T { *self = Some(value); diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index f18387d020d..b9b2ba9ae61 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -226,7 +226,7 @@ impl<T: ?Sized> *const T { #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const unsafe fn offset(self, count: isize) -> *const T where T: Sized, @@ -288,7 +288,7 @@ impl<T: ?Sized> *const T { #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const fn wrapping_offset(self, count: isize) -> *const T where T: Sized, @@ -507,7 +507,7 @@ impl<T: ?Sized> *const T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const unsafe fn add(self, count: usize) -> Self where T: Sized, @@ -634,7 +634,7 @@ impl<T: ?Sized> *const T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const fn wrapping_add(self, count: usize) -> Self where T: Sized, @@ -724,7 +724,7 @@ impl<T: ?Sized> *const T { /// #![feature(set_ptr_value)] /// # use core::fmt::Debug; /// let arr: [i32; 3] = [1, 2, 3]; - /// let mut ptr = &arr[0] as *const dyn Debug; + /// let mut ptr = arr.as_ptr() as *const dyn Debug; /// let thin = ptr as *const u8; /// unsafe { /// ptr = ptr.set_ptr_value(thin.add(8)); diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 6e207156b55..ad8696ab927 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -211,6 +211,7 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] #[rustc_const_stable(feature = "const_ptr_null", since = "1.24.0")] +#[rustc_diagnostic_item = "ptr_null"] pub const fn null<T>() -> *const T { 0 as *const T } @@ -229,6 +230,7 @@ pub const fn null<T>() -> *const T { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_promotable] #[rustc_const_stable(feature = "const_ptr_null", since = "1.24.0")] +#[rustc_diagnostic_item = "ptr_null_mut"] pub const fn null_mut<T>() -> *mut T { 0 as *mut T } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 3c6f1978283..55c019c51d5 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -231,7 +231,7 @@ impl<T: ?Sized> *mut T { #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const unsafe fn offset(self, count: isize) -> *mut T where T: Sized, @@ -294,7 +294,7 @@ impl<T: ?Sized> *mut T { #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const fn wrapping_offset(self, count: isize) -> *mut T where T: Sized, @@ -613,7 +613,7 @@ impl<T: ?Sized> *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const unsafe fn add(self, count: usize) -> Self where T: Sized, @@ -740,7 +740,7 @@ impl<T: ?Sized> *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_unstable(feature = "const_ptr_offset", issue = "71499")] - #[inline] + #[inline(always)] pub const fn wrapping_add(self, count: usize) -> Self where T: Sized, @@ -830,7 +830,7 @@ impl<T: ?Sized> *mut T { /// #![feature(set_ptr_value)] /// # use core::fmt::Debug; /// let mut arr: [i32; 3] = [1, 2, 3]; - /// let mut ptr = &mut arr[0] as *mut dyn Debug; + /// let mut ptr = arr.as_mut_ptr() as *mut dyn Debug; /// let thin = ptr as *mut u8; /// unsafe { /// ptr = ptr.set_ptr_value(thin.add(8)); diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index bf70b28579c..f1a115563fd 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -78,7 +78,7 @@ //! ``` //! use std::sync::Arc; //! use std::sync::atomic::{AtomicUsize, Ordering}; -//! use std::thread; +//! use std::{hint, thread}; //! //! fn main() { //! let spinlock = Arc::new(AtomicUsize::new(1)); @@ -89,7 +89,9 @@ //! }); //! //! // Wait for the other thread to release the lock -//! while spinlock.load(Ordering::SeqCst) != 0 {} +//! while spinlock.load(Ordering::SeqCst) != 0 { +//! hint::spin_loop(); +//! } //! //! if let Err(panic) = thread.join() { //! println!("Thread had an error: {:?}", panic); @@ -837,7 +839,6 @@ impl AtomicBool { /// # Examples /// /// ```rust - /// #![feature(atomic_fetch_update)] /// use std::sync::atomic::{AtomicBool, Ordering}; /// /// let x = AtomicBool::new(false); @@ -847,7 +848,7 @@ impl AtomicBool { /// assert_eq!(x.load(Ordering::SeqCst), false); /// ``` #[inline] - #[unstable(feature = "atomic_fetch_update", reason = "recently added", issue = "78639")] + #[stable(feature = "atomic_fetch_update", since = "1.53.0")] #[cfg(target_has_atomic = "8")] pub fn fetch_update<F>( &self, @@ -898,8 +899,10 @@ impl<T> AtomicPtr<T> { /// ``` /// use std::sync::atomic::{AtomicPtr, Ordering}; /// - /// let mut atomic_ptr = AtomicPtr::new(&mut 10); - /// *atomic_ptr.get_mut() = &mut 5; + /// let mut data = 10; + /// let mut atomic_ptr = AtomicPtr::new(&mut data); + /// let mut other_data = 5; + /// *atomic_ptr.get_mut() = &mut other_data; /// assert_eq!(unsafe { *atomic_ptr.load(Ordering::SeqCst) }, 5); /// ``` #[inline] @@ -916,9 +919,11 @@ impl<T> AtomicPtr<T> { /// #![feature(atomic_from_mut)] /// use std::sync::atomic::{AtomicPtr, Ordering}; /// - /// let mut some_ptr = &mut 123 as *mut i32; + /// let mut data = 123; + /// let mut some_ptr = &mut data as *mut i32; /// let a = AtomicPtr::from_mut(&mut some_ptr); - /// a.store(&mut 456, Ordering::Relaxed); + /// let mut other_data = 456; + /// a.store(&mut other_data, Ordering::Relaxed); /// assert_eq!(unsafe { *some_ptr }, 456); /// ``` #[inline] @@ -944,7 +949,8 @@ impl<T> AtomicPtr<T> { /// ``` /// use std::sync::atomic::AtomicPtr; /// - /// let atomic_ptr = AtomicPtr::new(&mut 5); + /// let mut data = 5; + /// let atomic_ptr = AtomicPtr::new(&mut data); /// assert_eq!(unsafe { *atomic_ptr.into_inner() }, 5); /// ``` #[inline] @@ -1220,7 +1226,6 @@ impl<T> AtomicPtr<T> { /// # Examples /// /// ```rust - /// #![feature(atomic_fetch_update)] /// use std::sync::atomic::{AtomicPtr, Ordering}; /// /// let ptr: *mut _ = &mut 5; @@ -1239,7 +1244,7 @@ impl<T> AtomicPtr<T> { /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); /// ``` #[inline] - #[unstable(feature = "atomic_fetch_update", reason = "recently added", issue = "78639")] + #[stable(feature = "atomic_fetch_update", since = "1.53.0")] #[cfg(target_has_atomic = "ptr")] pub fn fetch_update<F>( &self, diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 8c0848c64aa..fa6a6c2cccc 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -124,14 +124,13 @@ impl Duration { /// # Examples /// /// ``` - /// #![feature(duration_zero)] /// use std::time::Duration; /// /// let duration = Duration::ZERO; /// assert!(duration.is_zero()); /// assert_eq!(duration.as_nanos(), 0); /// ``` - #[unstable(feature = "duration_zero", issue = "73544")] + #[stable(feature = "duration_zero", since = "1.53.0")] pub const ZERO: Duration = Duration::from_nanos(0); /// The maximum duration. @@ -269,7 +268,6 @@ impl Duration { /// # Examples /// /// ``` - /// #![feature(duration_zero)] /// use std::time::Duration; /// /// assert!(Duration::ZERO.is_zero()); @@ -281,7 +279,8 @@ impl Duration { /// assert!(!Duration::from_nanos(1).is_zero()); /// assert!(!Duration::from_secs(1).is_zero()); /// ``` - #[unstable(feature = "duration_zero", issue = "73544")] + #[stable(feature = "duration_zero", since = "1.53.0")] + #[rustc_const_stable(feature = "duration_zero", since = "1.53.0")] #[inline] pub const fn is_zero(&self) -> bool { self.secs == 0 && self.nanos == 0 @@ -479,14 +478,13 @@ impl Duration { /// # Examples /// /// ``` - /// #![feature(duration_saturating_ops)] /// #![feature(duration_constants)] /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1)); /// assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX); /// ``` - #[unstable(feature = "duration_saturating_ops", issue = "76416")] + #[stable(feature = "duration_saturating_ops", since = "1.53.0")] #[inline] #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] pub const fn saturating_add(self, rhs: Duration) -> Duration { @@ -537,14 +535,12 @@ impl Duration { /// # Examples /// /// ``` - /// #![feature(duration_saturating_ops)] - /// #![feature(duration_zero)] /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 1).saturating_sub(Duration::new(0, 0)), Duration::new(0, 1)); /// assert_eq!(Duration::new(0, 0).saturating_sub(Duration::new(0, 1)), Duration::ZERO); /// ``` - #[unstable(feature = "duration_saturating_ops", issue = "76416")] + #[stable(feature = "duration_saturating_ops", since = "1.53.0")] #[inline] #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] pub const fn saturating_sub(self, rhs: Duration) -> Duration { @@ -590,14 +586,13 @@ impl Duration { /// # Examples /// /// ``` - /// #![feature(duration_saturating_ops)] /// #![feature(duration_constants)] /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 500_000_001).saturating_mul(2), Duration::new(1, 2)); /// assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX); /// ``` - #[unstable(feature = "duration_saturating_ops", issue = "76416")] + #[stable(feature = "duration_saturating_ops", since = "1.53.0")] #[inline] #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] pub const fn saturating_mul(self, rhs: u32) -> Duration { diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs index 89c2a969c28..ce7480ce2ee 100644 --- a/library/core/tests/array.rs +++ b/library/core/tests/array.rs @@ -1,25 +1,7 @@ -use core::array::{self, FixedSizeArray, IntoIter}; +use core::array::{self, IntoIter}; use core::convert::TryFrom; #[test] -fn fixed_size_array() { - let mut array = [0; 64]; - let mut zero_sized = [(); 64]; - let mut empty_array = [0; 0]; - let mut empty_zero_sized = [(); 0]; - - assert_eq!(FixedSizeArray::as_slice(&array).len(), 64); - assert_eq!(FixedSizeArray::as_slice(&zero_sized).len(), 64); - assert_eq!(FixedSizeArray::as_slice(&empty_array).len(), 0); - assert_eq!(FixedSizeArray::as_slice(&empty_zero_sized).len(), 0); - - assert_eq!(FixedSizeArray::as_mut_slice(&mut array).len(), 64); - assert_eq!(FixedSizeArray::as_mut_slice(&mut zero_sized).len(), 64); - assert_eq!(FixedSizeArray::as_mut_slice(&mut empty_array).len(), 0); - assert_eq!(FixedSizeArray::as_mut_slice(&mut empty_zero_sized).len(), 0); -} - -#[test] fn array_from_ref() { let value: String = "Hello World!".into(); let arr: &[String; 1] = array::from_ref(&value); diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 7dc6e220c08..f6bfe67e1b1 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -24,11 +24,8 @@ #![feature(div_duration)] #![feature(duration_consts_2)] #![feature(duration_constants)] -#![feature(duration_saturating_ops)] -#![feature(duration_zero)] #![feature(exact_size_is_empty)] #![feature(extern_types)] -#![feature(fixed_size_array)] #![feature(flt2dec)] #![feature(fmt_internals)] #![feature(hashmap_internals)] @@ -69,7 +66,6 @@ #![feature(ptr_metadata)] #![feature(once_cell)] #![feature(unsized_tuple_coercion)] -#![feature(nonzero_leading_trailing_zeros)] #![feature(const_option)] #![feature(integer_atomics)] #![feature(slice_group_by)] diff --git a/library/panic_abort/Cargo.toml b/library/panic_abort/Cargo.toml index caa89aa30d0..bdab664cd64 100644 --- a/library/panic_abort/Cargo.toml +++ b/library/panic_abort/Cargo.toml @@ -13,6 +13,7 @@ bench = false doc = false [dependencies] +alloc = { path = "../alloc" } cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } core = { path = "../core" } libc = { version = "0.2", default-features = false } diff --git a/library/panic_abort/src/android.rs b/library/panic_abort/src/android.rs new file mode 100644 index 00000000000..34d77502eab --- /dev/null +++ b/library/panic_abort/src/android.rs @@ -0,0 +1,49 @@ +use alloc::string::String; +use core::mem::transmute; +use core::panic::BoxMeUp; +use core::ptr::copy_nonoverlapping; + +const ANDROID_SET_ABORT_MESSAGE: &[u8] = b"android_set_abort_message\0"; +type SetAbortMessageType = unsafe extern "C" fn(*const libc::c_char) -> (); + +// Forward the abort message to libc's android_set_abort_message. We try our best to populate the +// message but as this function may already be called as part of a failed allocation, it may not be +// possible to do so. +// +// Some methods of core are on purpose avoided (such as try_reserve) as these rely on the correct +// resolution of rust_eh_personality which is loosely defined in panic_abort. +// +// Weakly resolve the symbol for android_set_abort_message. This function is only available +// for API >= 21. +pub(crate) unsafe fn android_set_abort_message(payload: *mut &mut dyn BoxMeUp) { + let func_addr = + libc::dlsym(libc::RTLD_DEFAULT, ANDROID_SET_ABORT_MESSAGE.as_ptr() as *const libc::c_char) + as usize; + if func_addr == 0 { + return; + } + + let payload = (*payload).get(); + let msg = match payload.downcast_ref::<&'static str>() { + Some(msg) => msg.as_bytes(), + None => match payload.downcast_ref::<String>() { + Some(msg) => msg.as_bytes(), + None => &[], + }, + }; + if msg.is_empty() { + return; + } + + // Allocate a new buffer to append the null byte. + let size = msg.len() + 1usize; + let buf = libc::malloc(size) as *mut libc::c_char; + if buf.is_null() { + return; // allocation failure + } + copy_nonoverlapping(msg.as_ptr(), buf as *mut u8, msg.len()); + buf.offset(msg.len() as isize).write(0); + + let func = transmute::<usize, SetAbortMessageType>(func_addr); + func(buf); +} diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index eb2277d8baa..5dcd1e6af36 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -19,6 +19,9 @@ #![feature(rustc_attrs)] #![feature(asm)] +#[cfg(target_os = "android")] +mod android; + use core::any::Any; use core::panic::BoxMeUp; @@ -31,6 +34,10 @@ pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Sen // "Leak" the payload and shim to the relevant abort on the platform in question. #[rustc_std_internal_symbol] pub unsafe extern "C" fn __rust_start_panic(_payload: *mut &mut dyn BoxMeUp) -> u32 { + // Android has the ability to attach a message as part of the abort. + #[cfg(target_os = "android")] + android::android_set_abort_message(_payload); + abort(); cfg_if::cfg_if! { diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 22ca7ed09b4..ab7b142ef3d 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -16,11 +16,12 @@ cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } -libc = { version = "0.2.88", default-features = false, features = ['rustc-dep-of-std'] } +libc = { version = "0.2.93", default-features = false, features = ['rustc-dep-of-std'] } compiler_builtins = { version = "0.1.39" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.11", default-features = false, features = ['rustc-dep-of-std'] } +std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } # Dependencies of the `backtrace` crate addr2line = { version = "0.14.0", optional = true, default-features = false } @@ -70,8 +71,8 @@ panic_immediate_abort = ["core/panic_immediate_abort"] # Enable std_detect default features for stdarch/crates/std_detect: # https://github.com/rust-lang/stdarch/blob/master/crates/std_detect/Cargo.toml -std_detect_file_io = [] -std_detect_dlsym_getauxval = [] +std_detect_file_io = ["std_detect/std_detect_file_io"] +std_detect_dlsym_getauxval = ["std_detect/std_detect_dlsym_getauxval"] [package.metadata.fortanix-sgx] # Maximum possible number of threads when testing diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 860bc130b7d..e6120b8ee31 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -265,8 +265,9 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> { /// ```no_run /// use std::fs; /// use std::net::SocketAddr; +/// use std::error::Error; /// -/// fn main() -> Result<(), Box<dyn std::error::Error + 'static>> { +/// fn main() -> Result<(), Box<dyn Error>> { /// let foo: SocketAddr = fs::read_to_string("address.txt")?.parse()?; /// Ok(()) /// } diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 02b0fc0c57d..d8021d3e99a 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -234,7 +234,7 @@ impl<R: Seek> BufReader<R> { /// the buffer will not be flushed, allowing for more efficient seeks. /// This method does not return the location of the underlying reader, so the caller /// must track this information themselves if it is required. - #[unstable(feature = "bufreader_seek_relative", issue = "31100")] + #[stable(feature = "bufreader_seek_relative", since = "1.53.0")] pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> { let pos = self.pos as u64; if offset < 0 { diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 2a3d44fb17d..39ed62425ce 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -1768,6 +1768,7 @@ mod super_keyword {} /// In the 2015 edition the parameters pattern was not needed for traits: /// /// ```rust,edition2015 +/// # #![allow(anonymous_parameters)] /// trait Tr { /// fn f(i32); /// } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 6baf9f2a464..84f74ef9407 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -208,13 +208,15 @@ // std may use features in a platform-specific way #![allow(unused_features)] #![feature(rustc_allow_const_fn_unstable)] -#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count))] +#![cfg_attr( + test, + feature(internal_output_capture, print_internals, update_panic_count, thread_local_const_init) +)] #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) )] #![deny(rustc::existing_doc_keyword)] -#![cfg_attr(all(test, target_vendor = "fortanix", target_env = "sgx"), feature(fixed_size_array))] // std is implemented with unstable features, many of which are internal // compiler details that will never be stable // NB: the following list is sorted to minimize merge conflicts. @@ -262,7 +264,6 @@ #![cfg_attr(not(bootstrap), feature(doc_notable_trait))] #![feature(dropck_eyepatch)] #![feature(duration_constants)] -#![feature(duration_zero)] #![feature(edition_panic)] #![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] @@ -539,22 +540,16 @@ pub mod rt; #[allow(dead_code, unused_attributes)] mod backtrace_rs; -// Pull in the `std_detect` crate directly into libstd. The contents of -// `std_detect` are in a different repository: rust-lang/stdarch. -// -// `std_detect` depends on libstd, but the contents of this module are -// set up in such a way that directly pulling it here works such that the -// crate uses the this crate as its libstd. -#[path = "../../stdarch/crates/std_detect/src/mod.rs"] -#[allow(missing_debug_implementations, missing_docs, dead_code)] -#[unstable(feature = "stdsimd", issue = "48556")] -#[cfg(not(test))] -mod std_detect; - +#[stable(feature = "simd_x86", since = "1.27.0")] +pub use std_detect::is_x86_feature_detected; #[doc(hidden)] #[unstable(feature = "stdsimd", issue = "48556")] -#[cfg(not(test))] -pub use std_detect::detect; +pub use std_detect::*; +#[unstable(feature = "stdsimd", issue = "48556")] +pub use std_detect::{ + is_aarch64_feature_detected, is_arm_feature_detected, is_mips64_feature_detected, + is_mips_feature_detected, is_powerpc64_feature_detected, is_powerpc_feature_detected, +}; // Re-export macros defined in libcore. #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index da2415e3610..9b629e19be5 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -334,6 +334,8 @@ impl Ipv4Addr { /// An IPv4 address representing an unspecified address: 0.0.0.0 /// + /// This corresponds to the constant `INADDR_ANY` in other languages. + /// /// # Examples /// /// ``` @@ -342,6 +344,7 @@ impl Ipv4Addr { /// let addr = Ipv4Addr::UNSPECIFIED; /// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0)); /// ``` + #[doc(alias = "INADDR_ANY")] #[stable(feature = "ip_constructors", since = "1.30.0")] pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); diff --git a/library/std/src/sys_common/alloc.rs b/library/std/src/sys/common/alloc.rs index 6c1bc0d839a..2a54e99020e 100644 --- a/library/std/src/sys_common/alloc.rs +++ b/library/std/src/sys/common/alloc.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - use crate::alloc::{GlobalAlloc, Layout, System}; use crate::cmp; use crate::ptr; diff --git a/library/std/src/sys/common/mod.rs b/library/std/src/sys/common/mod.rs new file mode 100644 index 00000000000..ff64d2aa825 --- /dev/null +++ b/library/std/src/sys/common/mod.rs @@ -0,0 +1,13 @@ +// This module contains code that is shared between all platforms, mostly utility or fallback code. +// This explicitly does not include code that is shared between only a few platforms, +// such as when reusing an implementation from `unix` or `unsupported`. +// In those cases the desired code should be included directly using the #[path] attribute, +// not moved to this module. +// +// Currently `sys_common` contains a lot of code that should live in this module, +// ideally `sys_common` would only contain platform-independent abstractions on top of `sys`. +// Progress on this is tracked in #84187. + +#![allow(dead_code)] + +pub mod alloc; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 9b359392cf0..50c2660ebcf 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -22,6 +22,8 @@ #![allow(missing_debug_implementations)] +mod common; + cfg_if::cfg_if! { if #[cfg(target_os = "vxworks")] { mod vxworks; diff --git a/library/std/src/sys/unix/alloc.rs b/library/std/src/sys/unix/alloc.rs index 964abe8b8c9..1b71905aa09 100644 --- a/library/std/src/sys/unix/alloc.rs +++ b/library/std/src/sys/unix/alloc.rs @@ -1,6 +1,6 @@ use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ptr; -use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN}; +use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 4a077e2151e..ce2c4e88c7e 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -223,7 +223,7 @@ where impl fmt::Display for JoinPathsError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "path segment contains separator `{}`", PATH_SEPARATOR) + write!(f, "path segment contains separator `{}`", char::from(PATH_SEPARATOR)) } } diff --git a/library/std/src/sys/windows/alloc.rs b/library/std/src/sys/windows/alloc.rs index af93cd7a3e2..2fe71f9f28d 100644 --- a/library/std/src/sys/windows/alloc.rs +++ b/library/std/src/sys/windows/alloc.rs @@ -5,7 +5,7 @@ use crate::ffi::c_void; use crate::ptr; use crate::sync::atomic::{AtomicPtr, Ordering}; use crate::sys::c; -use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN}; +use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN}; #[cfg(test)] mod tests; diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 660f0e0df97..23a3a0e907d 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -8,9 +8,11 @@ //! rest of `std` is complex, with dependencies going in all //! directions: `std` depending on `sys_common`, `sys_common` //! depending on `sys`, and `sys` depending on `sys_common` and `std`. -//! Ideally `sys_common` would be split into two and the dependencies -//! between them all would form a dag, facilitating the extraction of -//! `std::sys` from the standard library. +//! This is because `sys_common` not only contains platform-independent code, +//! but also code that is shared between the different platforms in `sys`. +//! Ideally all that shared code should be moved to `sys::common`, +//! and the dependencies between `std`, `sys_common` and `sys` all would form a dag. +//! Progress on this is tracked in #84187. #![allow(missing_docs)] #![allow(missing_debug_implementations)] @@ -46,7 +48,6 @@ macro_rules! rtunwrap { }; } -pub mod alloc; pub mod at_exit_imp; pub mod backtrace; pub mod bytestring; diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index dd438858c37..37525e50604 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -133,6 +133,15 @@ macro_rules! thread_local { // empty (base case for the recursion) () => {}; + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }; $($rest:tt)*) => ( + $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, const $init); + $crate::thread_local!($($rest)*); + ); + + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }) => ( + $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, const $init); + ); + // process multiple declarations ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( $crate::__thread_local_inner!($(#[$attr])* $vis $name, $t, $init); @@ -151,6 +160,101 @@ macro_rules! thread_local { #[allow_internal_unstable(thread_local_internals, cfg_target_thread_local, thread_local)] #[allow_internal_unsafe] macro_rules! __thread_local_inner { + // used to generate the `LocalKey` value for const-initialized thread locals + (@key $t:ty, const $init:expr) => {{ + unsafe fn __getit() -> $crate::option::Option<&'static $t> { + const _REQUIRE_UNSTABLE: () = $crate::thread::require_unstable_const_init_thread_local(); + + // wasm without atomics maps directly to `static mut`, and dtors + // aren't implemented because thread dtors aren't really a thing + // on wasm right now + // + // FIXME(#84224) this should come after the `target_thread_local` + // block. + #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))] + { + static mut VAL: $t = $init; + Some(&VAL) + } + + // If the platform has support for `#[thread_local]`, use it. + #[cfg(all( + target_thread_local, + not(all(target_arch = "wasm32", not(target_feature = "atomics"))), + ))] + { + // If a dtor isn't needed we can do something "very raw" and + // just get going. + if !$crate::mem::needs_drop::<$t>() { + #[thread_local] + static mut VAL: $t = $init; + unsafe { + return Some(&VAL) + } + } + + #[thread_local] + static mut VAL: $t = $init; + // 0 == dtor not registered + // 1 == dtor registered, dtor not run + // 2 == dtor registered and is running or has run + #[thread_local] + static mut STATE: u8 = 0; + + unsafe extern "C" fn destroy(ptr: *mut u8) { + let ptr = ptr as *mut $t; + + unsafe { + debug_assert_eq!(STATE, 1); + STATE = 2; + $crate::ptr::drop_in_place(ptr); + } + } + + unsafe { + match STATE { + // 0 == we haven't registered a destructor, so do + // so now. + 0 => { + $crate::thread::__FastLocalKeyInner::<$t>::register_dtor( + &VAL as *const _ as *mut u8, + destroy, + ); + STATE = 1; + Some(&VAL) + } + // 1 == the destructor is registered and the value + // is valid, so return the pointer. + 1 => Some(&VAL), + // otherwise the destructor has already run, so we + // can't give access. + _ => None, + } + } + } + + // On platforms without `#[thread_local]` we fall back to the + // same implementation as below for os thread locals. + #[cfg(all( + not(target_thread_local), + not(all(target_arch = "wasm32", not(target_feature = "atomics"))), + ))] + { + #[inline] + const fn __init() -> $t { $init } + static __KEY: $crate::thread::__OsLocalKeyInner<$t> = + $crate::thread::__OsLocalKeyInner::new(); + #[allow(unused_unsafe)] + unsafe { __KEY.get(__init) } + } + } + + unsafe { + $crate::thread::LocalKey::new(__getit) + } + }}; + + // used to generate the `LocalKey` value for `thread_local!` (@key $t:ty, $init:expr) => { { #[inline] @@ -188,9 +292,9 @@ macro_rules! __thread_local_inner { } } }; - ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => { + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = - $crate::__thread_local_inner!(@key $t, $init); + $crate::__thread_local_inner!(@key $t, $($init)*); } } @@ -442,6 +546,15 @@ pub mod fast { Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } } + // note that this is just a publically-callable function only for the + // const-initialized form of thread locals, basically a way to call the + // free `register_dtor` function defined elsewhere in libstd. + pub unsafe fn register_dtor(a: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + unsafe { + register_dtor(a, dtor); + } + } + pub unsafe fn get<F: FnOnce() -> T>(&self, init: F) -> Option<&'static T> { // SAFETY: See the definitions of `LazyKeyInner::get` and // `try_initialize` for more informations. diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs index 4fb0a089082..80e6798d847 100644 --- a/library/std/src/thread/local/tests.rs +++ b/library/std/src/thread/local/tests.rs @@ -1,6 +1,6 @@ use crate::cell::{Cell, UnsafeCell}; use crate::sync::mpsc::{channel, Sender}; -use crate::thread; +use crate::thread::{self, LocalKey}; use crate::thread_local; struct Foo(Sender<()>); @@ -15,74 +15,90 @@ impl Drop for Foo { #[test] fn smoke_no_dtor() { thread_local!(static FOO: Cell<i32> = Cell::new(1)); + run(&FOO); + thread_local!(static FOO2: Cell<i32> = const { Cell::new(1) }); + run(&FOO2); - FOO.with(|f| { - assert_eq!(f.get(), 1); - f.set(2); - }); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - FOO.with(|f| { + fn run(key: &'static LocalKey<Cell<i32>>) { + key.with(|f| { assert_eq!(f.get(), 1); + f.set(2); }); - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); + let t = thread::spawn(move || { + key.with(|f| { + assert_eq!(f.get(), 1); + }); + }); + t.join().unwrap(); - FOO.with(|f| { - assert_eq!(f.get(), 2); - }); + key.with(|f| { + assert_eq!(f.get(), 2); + }); + } } #[test] fn states() { - struct Foo; + struct Foo(&'static LocalKey<Foo>); impl Drop for Foo { fn drop(&mut self) { - assert!(FOO.try_with(|_| ()).is_err()); + assert!(self.0.try_with(|_| ()).is_err()); } } - thread_local!(static FOO: Foo = Foo); - thread::spawn(|| { - assert!(FOO.try_with(|_| ()).is_ok()); - }) - .join() - .ok() - .expect("thread panicked"); + thread_local!(static FOO: Foo = Foo(&FOO)); + run(&FOO); + thread_local!(static FOO2: Foo = const { Foo(&FOO2) }); + run(&FOO2); + + fn run(foo: &'static LocalKey<Foo>) { + thread::spawn(move || { + assert!(foo.try_with(|_| ()).is_ok()); + }) + .join() + .unwrap(); + } } #[test] fn smoke_dtor() { thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell::new(None)); - - let (tx, rx) = channel(); - let _t = thread::spawn(move || unsafe { - let mut tx = Some(tx); - FOO.with(|f| { - *f.get() = Some(Foo(tx.take().unwrap())); + run(&FOO); + thread_local!(static FOO2: UnsafeCell<Option<Foo>> = const { UnsafeCell::new(None) }); + run(&FOO2); + + fn run(key: &'static LocalKey<UnsafeCell<Option<Foo>>>) { + let (tx, rx) = channel(); + let t = thread::spawn(move || unsafe { + let mut tx = Some(tx); + key.with(|f| { + *f.get() = Some(Foo(tx.take().unwrap())); + }); }); - }); - rx.recv().unwrap(); + rx.recv().unwrap(); + t.join().unwrap(); + } } #[test] fn circular() { - struct S1; - struct S2; + struct S1(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>); + struct S2(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>); thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None)); thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell::new(None)); - static mut HITS: u32 = 0; + thread_local!(static K3: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) }); + thread_local!(static K4: UnsafeCell<Option<S2>> = const { UnsafeCell::new(None) }); + static mut HITS: usize = 0; impl Drop for S1 { fn drop(&mut self) { unsafe { HITS += 1; - if K2.try_with(|_| ()).is_err() { + if self.1.try_with(|_| ()).is_err() { assert_eq!(HITS, 3); } else { if HITS == 1 { - K2.with(|s| *s.get() = Some(S2)); + self.1.with(|s| *s.get() = Some(S2(self.0, self.1))); } else { assert_eq!(HITS, 3); } @@ -94,38 +110,54 @@ fn circular() { fn drop(&mut self) { unsafe { HITS += 1; - assert!(K1.try_with(|_| ()).is_ok()); + assert!(self.0.try_with(|_| ()).is_ok()); assert_eq!(HITS, 2); - K1.with(|s| *s.get() = Some(S1)); + self.0.with(|s| *s.get() = Some(S1(self.0, self.1))); } } } thread::spawn(move || { - drop(S1); + drop(S1(&K1, &K2)); + }) + .join() + .unwrap(); + + unsafe { + HITS = 0; + } + + thread::spawn(move || { + drop(S1(&K3, &K4)); }) .join() - .ok() - .expect("thread panicked"); + .unwrap(); } #[test] fn self_referential() { - struct S1; + struct S1(&'static LocalKey<UnsafeCell<Option<S1>>>); + thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None)); + thread_local!(static K2: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) }); impl Drop for S1 { fn drop(&mut self) { - assert!(K1.try_with(|_| ()).is_err()); + assert!(self.0.try_with(|_| ()).is_err()); } } thread::spawn(move || unsafe { - K1.with(|s| *s.get() = Some(S1)); + K1.with(|s| *s.get() = Some(S1(&K1))); }) .join() - .ok() - .expect("thread panicked"); + .unwrap(); + + thread::spawn(move || unsafe { + K2.with(|s| *s.get() = Some(S1(&K2))); + }) + .join() + .unwrap(); } // Note that this test will deadlock if TLS destructors aren't run (this @@ -152,3 +184,26 @@ fn dtors_in_dtors_in_dtors() { }); rx.recv().unwrap(); } + +#[test] +fn dtors_in_dtors_in_dtors_const_init() { + struct S1(Sender<()>); + thread_local!(static K1: UnsafeCell<Option<S1>> = const { UnsafeCell::new(None) }); + thread_local!(static K2: UnsafeCell<Option<Foo>> = const { UnsafeCell::new(None) }); + + impl Drop for S1 { + fn drop(&mut self) { + let S1(ref tx) = *self; + unsafe { + let _ = K2.try_with(|s| *s.get() = Some(Foo(tx.clone()))); + } + } + } + + let (tx, rx) = channel(); + let _t = thread::spawn(move || unsafe { + let mut tx = Some(tx); + K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); + }); + rx.recv().unwrap(); +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index ffdf4be1584..54e39f76274 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -204,6 +204,13 @@ pub use self::local::os::Key as __OsLocalKeyInner; #[doc(hidden)] pub use self::local::statik::Key as __StaticLocalKeyInner; +// This is only used to make thread locals with `const { .. }` initialization +// expressions unstable. If and/or when that syntax is stabilized with thread +// locals this will simply be removed. +#[doc(hidden)] +#[unstable(feature = "thread_local_const_init", issue = "84223")] +pub const fn require_unstable_const_init_thread_local() {} + //////////////////////////////////////////////////////////////////////////////// // Builder //////////////////////////////////////////////////////////////////////////////// diff --git a/library/stdarch b/library/stdarch -Subproject 9c732a56f67f54d12a0b4fd99993154906c95ea +Subproject 19f5459dd0f89e466b7bcaa0f69ecca90f21a4d diff --git a/library/test/src/bench.rs b/library/test/src/bench.rs index d4b37284ea7..169154187f2 100644 --- a/library/test/src/bench.rs +++ b/library/test/src/bench.rs @@ -2,7 +2,11 @@ pub use std::hint::black_box; use super::{ - event::CompletedTest, options::BenchMode, test_result::TestResult, types::TestDesc, Sender, + event::CompletedTest, + options::BenchMode, + test_result::TestResult, + types::{TestDesc, TestId}, + Sender, }; use crate::stats; @@ -177,8 +181,13 @@ where } } -pub fn benchmark<F>(desc: TestDesc, monitor_ch: Sender<CompletedTest>, nocapture: bool, f: F) -where +pub fn benchmark<F>( + id: TestId, + desc: TestDesc, + monitor_ch: Sender<CompletedTest>, + nocapture: bool, + f: F, +) where F: FnMut(&mut Bencher), { let mut bs = Bencher { mode: BenchMode::Auto, summary: None, bytes: 0 }; @@ -213,7 +222,7 @@ where }; let stdout = data.lock().unwrap().to_vec(); - let message = CompletedTest::new(desc, test_result, None, stdout); + let message = CompletedTest::new(id, desc, test_result, None, stdout); monitor_ch.send(message).unwrap(); } diff --git a/library/test/src/event.rs b/library/test/src/event.rs index 2103a0d10f4..206f3e10e84 100644 --- a/library/test/src/event.rs +++ b/library/test/src/event.rs @@ -3,10 +3,11 @@ use super::test_result::TestResult; use super::time::TestExecTime; -use super::types::TestDesc; +use super::types::{TestDesc, TestId}; #[derive(Debug, Clone)] pub struct CompletedTest { + pub id: TestId, pub desc: TestDesc, pub result: TestResult, pub exec_time: Option<TestExecTime>, @@ -15,12 +16,13 @@ pub struct CompletedTest { impl CompletedTest { pub fn new( + id: TestId, desc: TestDesc, result: TestResult, exec_time: Option<TestExecTime>, stdout: Vec<u8>, ) -> Self { - Self { desc, result, exec_time, stdout } + Self { id, desc, result, exec_time, stdout } } } diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 7683f792b8d..2e0864f303c 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -54,7 +54,7 @@ pub mod test { time::{TestExecTime, TestTimeOptions}, types::{ DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, - TestDescAndFn, TestName, TestType, + TestDescAndFn, TestId, TestName, TestType, }, }; } @@ -215,9 +215,10 @@ where // Use a deterministic hasher type TestMap = - HashMap<TestDesc, RunningTest, BuildHasherDefault<collections::hash_map::DefaultHasher>>; + HashMap<TestId, RunningTest, BuildHasherDefault<collections::hash_map::DefaultHasher>>; struct TimeoutEntry { + id: TestId, desc: TestDesc, timeout: Instant, } @@ -249,7 +250,9 @@ where let (filtered_tests, filtered_benchs): (Vec<_>, _) = filtered_tests .into_iter() - .partition(|e| matches!(e.testfn, StaticTestFn(_) | DynTestFn(_))); + .enumerate() + .map(|(i, e)| (TestId(i), e)) + .partition(|(_, e)| matches!(e.testfn, StaticTestFn(_) | DynTestFn(_))); let concurrency = opts.test_threads.unwrap_or_else(get_concurrency); @@ -278,7 +281,7 @@ where break; } let timeout_entry = timeout_queue.pop_front().unwrap(); - if running_tests.contains_key(&timeout_entry.desc) { + if running_tests.contains_key(&timeout_entry.id) { timed_out.push(timeout_entry.desc); } } @@ -294,11 +297,11 @@ where if concurrency == 1 { while !remaining.is_empty() { - let test = remaining.pop().unwrap(); + let (id, test) = remaining.pop().unwrap(); let event = TestEvent::TeWait(test.desc.clone()); notify_about_test_event(event)?; let join_handle = - run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::No); + run_test(opts, !opts.run_tests, id, test, run_strategy, tx.clone(), Concurrent::No); assert!(join_handle.is_none()); let completed_test = rx.recv().unwrap(); @@ -308,7 +311,7 @@ where } else { while pending > 0 || !remaining.is_empty() { while pending < concurrency && !remaining.is_empty() { - let test = remaining.pop().unwrap(); + let (id, test) = remaining.pop().unwrap(); let timeout = time::get_default_test_timeout(); let desc = test.desc.clone(); @@ -317,13 +320,14 @@ where let join_handle = run_test( opts, !opts.run_tests, + id, test, run_strategy, tx.clone(), Concurrent::Yes, ); - running_tests.insert(desc.clone(), RunningTest { join_handle }); - timeout_queue.push_back(TimeoutEntry { desc, timeout }); + running_tests.insert(id, RunningTest { join_handle }); + timeout_queue.push_back(TimeoutEntry { id, desc, timeout }); pending += 1; } @@ -352,13 +356,12 @@ where } let mut completed_test = res.unwrap(); - if let Some(running_test) = running_tests.remove(&completed_test.desc) { - if let Some(join_handle) = running_test.join_handle { - if let Err(_) = join_handle.join() { - if let TrOk = completed_test.result { - completed_test.result = - TrFailedMsg("panicked after reporting success".to_string()); - } + let running_test = running_tests.remove(&completed_test.id).unwrap(); + if let Some(join_handle) = running_test.join_handle { + if let Err(_) = join_handle.join() { + if let TrOk = completed_test.result { + completed_test.result = + TrFailedMsg("panicked after reporting success".to_string()); } } } @@ -371,10 +374,10 @@ where if opts.bench_benchmarks { // All benchmarks run at the end, in serial. - for b in filtered_benchs { + for (id, b) in filtered_benchs { let event = TestEvent::TeWait(b.desc.clone()); notify_about_test_event(event)?; - run_test(opts, false, b, run_strategy, tx.clone(), Concurrent::No); + run_test(opts, false, id, b, run_strategy, tx.clone(), Concurrent::No); let completed_test = rx.recv().unwrap(); let event = TestEvent::TeResult(completed_test); @@ -448,6 +451,7 @@ pub fn convert_benchmarks_to_tests(tests: Vec<TestDescAndFn>) -> Vec<TestDescAnd pub fn run_test( opts: &TestOpts, force_ignore: bool, + id: TestId, test: TestDescAndFn, strategy: RunStrategy, monitor_ch: Sender<CompletedTest>, @@ -461,7 +465,7 @@ pub fn run_test( && !cfg!(target_os = "emscripten"); if force_ignore || desc.ignore || ignore_because_no_process_support { - let message = CompletedTest::new(desc, TrIgnored, None, Vec::new()); + let message = CompletedTest::new(id, desc, TrIgnored, None, Vec::new()); monitor_ch.send(message).unwrap(); return None; } @@ -474,6 +478,7 @@ pub fn run_test( } fn run_test_inner( + id: TestId, desc: TestDesc, monitor_ch: Sender<CompletedTest>, testfn: Box<dyn FnOnce() + Send>, @@ -484,6 +489,7 @@ pub fn run_test( let runtest = move || match opts.strategy { RunStrategy::InProcess => run_test_in_process( + id, desc, opts.nocapture, opts.time.is_some(), @@ -492,6 +498,7 @@ pub fn run_test( opts.time, ), RunStrategy::SpawnPrimary => spawn_test_subprocess( + id, desc, opts.nocapture, opts.time.is_some(), @@ -530,14 +537,14 @@ pub fn run_test( match testfn { DynBenchFn(bencher) => { // Benchmarks aren't expected to panic, so we run them all in-process. - crate::bench::benchmark(desc, monitor_ch, opts.nocapture, |harness| { + crate::bench::benchmark(id, desc, monitor_ch, opts.nocapture, |harness| { bencher.run(harness) }); None } StaticBenchFn(benchfn) => { // Benchmarks aren't expected to panic, so we run them all in-process. - crate::bench::benchmark(desc, monitor_ch, opts.nocapture, benchfn); + crate::bench::benchmark(id, desc, monitor_ch, opts.nocapture, benchfn); None } DynTestFn(f) => { @@ -546,6 +553,7 @@ pub fn run_test( _ => panic!("Cannot run dynamic test fn out-of-process"), }; run_test_inner( + id, desc, monitor_ch, Box::new(move || __rust_begin_short_backtrace(f)), @@ -553,6 +561,7 @@ pub fn run_test( ) } StaticTestFn(f) => run_test_inner( + id, desc, monitor_ch, Box::new(move || __rust_begin_short_backtrace(f)), @@ -571,6 +580,7 @@ fn __rust_begin_short_backtrace<F: FnOnce()>(f: F) { } fn run_test_in_process( + id: TestId, desc: TestDesc, nocapture: bool, report_time: bool, @@ -599,11 +609,12 @@ fn run_test_in_process( Err(e) => calc_result(&desc, Err(e.as_ref()), &time_opts, &exec_time), }; let stdout = data.lock().unwrap_or_else(|e| e.into_inner()).to_vec(); - let message = CompletedTest::new(desc, test_result, exec_time, stdout); + let message = CompletedTest::new(id, desc, test_result, exec_time, stdout); monitor_ch.send(message).unwrap(); } fn spawn_test_subprocess( + id: TestId, desc: TestDesc, nocapture: bool, report_time: bool, @@ -653,7 +664,7 @@ fn spawn_test_subprocess( (result, test_output, exec_time) })(); - let message = CompletedTest::new(desc, result, exec_time, test_output); + let message = CompletedTest::new(id, desc, result, exec_time, test_output); monitor_ch.send(message).unwrap(); } diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index e3c9b386915..6a3f31b74ea 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -94,7 +94,7 @@ pub fn do_not_run_ignored_tests() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; assert_ne!(result, TrOk); } @@ -113,7 +113,7 @@ pub fn ignored_tests_result_in_ignored() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; assert_eq!(result, TrIgnored); } @@ -136,7 +136,7 @@ fn test_should_panic() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; assert_eq!(result, TrOk); } @@ -159,7 +159,7 @@ fn test_should_panic_good_message() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; assert_eq!(result, TrOk); } @@ -187,7 +187,7 @@ fn test_should_panic_bad_message() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; assert_eq!(result, TrFailedMsg(failed_msg.to_string())); } @@ -219,7 +219,7 @@ fn test_should_panic_non_string_message_type() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&TestOpts::new(), false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; assert_eq!(result, TrFailedMsg(failed_msg)); } @@ -243,7 +243,15 @@ fn test_should_panic_but_succeeds() { testfn: DynTestFn(Box::new(f)), }; let (tx, rx) = channel(); - run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test( + &TestOpts::new(), + false, + TestId(0), + desc, + RunStrategy::InProcess, + tx, + Concurrent::No, + ); let result = rx.recv().unwrap().result; assert_eq!( result, @@ -270,7 +278,7 @@ fn report_time_test_template(report_time: bool) -> Option<TestExecTime> { let test_opts = TestOpts { time_options, ..TestOpts::new() }; let (tx, rx) = channel(); - run_test(&test_opts, false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&test_opts, false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let exec_time = rx.recv().unwrap().exec_time; exec_time } @@ -305,7 +313,7 @@ fn time_test_failure_template(test_type: TestType) -> TestResult { let test_opts = TestOpts { time_options: Some(time_options), ..TestOpts::new() }; let (tx, rx) = channel(); - run_test(&test_opts, false, desc, RunStrategy::InProcess, tx, Concurrent::No); + run_test(&test_opts, false, TestId(0), desc, RunStrategy::InProcess, tx, Concurrent::No); let result = rx.recv().unwrap().result; result @@ -637,7 +645,7 @@ pub fn test_bench_no_iter() { test_type: TestType::Unknown, }; - crate::bench::benchmark(desc, tx, true, f); + crate::bench::benchmark(TestId(0), desc, tx, true, f); rx.recv().unwrap(); } @@ -657,7 +665,7 @@ pub fn test_bench_iter() { test_type: TestType::Unknown, }; - crate::bench::benchmark(desc, tx, true, f); + crate::bench::benchmark(TestId(0), desc, tx, true, f); rx.recv().unwrap(); } diff --git a/library/test/src/types.rs b/library/test/src/types.rs index 5b75d2f367f..c5d91f653b3 100644 --- a/library/test/src/types.rs +++ b/library/test/src/types.rs @@ -112,9 +112,13 @@ impl fmt::Debug for TestFn { } } +// A unique integer associated with each test. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct TestId(pub usize); + // The definition of a single test. A test runner will run a list of // these. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug)] pub struct TestDesc { pub name: TestName, pub ignore: bool, diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 23af00d6293..4111420e474 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -429,7 +429,7 @@ class RustBuild(object): lib_dir = "{}/lib".format(bin_root) for lib in os.listdir(lib_dir): if lib.endswith(".so"): - self.fix_bin_or_dylib(os.path.join(lib_dir, lib), rpath_libz=True) + self.fix_bin_or_dylib(os.path.join(lib_dir, lib)) with output(self.rustc_stamp(stage0)) as rust_stamp: rust_stamp.write(key) @@ -477,10 +477,10 @@ class RustBuild(object): if self.program_out_of_date(self.llvm_stamp(), llvm_sha + str(llvm_assertions)): self._download_ci_llvm(llvm_sha, llvm_assertions) for binary in ["llvm-config", "FileCheck"]: - self.fix_bin_or_dylib(os.path.join(llvm_root, "bin", binary), rpath_libz=True) + self.fix_bin_or_dylib(os.path.join(llvm_root, "bin", binary)) for lib in os.listdir(llvm_lib): if lib.endswith(".so"): - self.fix_bin_or_dylib(os.path.join(llvm_lib, lib), rpath_libz=True) + self.fix_bin_or_dylib(os.path.join(llvm_lib, lib)) with output(self.llvm_stamp()) as llvm_stamp: llvm_stamp.write(llvm_sha + str(llvm_assertions)) @@ -548,7 +548,7 @@ class RustBuild(object): match="rust-dev", verbose=self.verbose) - def fix_bin_or_dylib(self, fname, rpath_libz=False): + def fix_bin_or_dylib(self, fname): """Modifies the interpreter section of 'fname' to fix the dynamic linker, or the RPATH section, to fix the dynamic library search path @@ -583,56 +583,49 @@ class RustBuild(object): # Only build `.nix-deps` once. nix_deps_dir = self.nix_deps_dir if not nix_deps_dir: - nix_deps_dir = ".nix-deps" - if not os.path.exists(nix_deps_dir): - os.makedirs(nix_deps_dir) - - nix_deps = [ - # Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). - "stdenv.cc.bintools", - - # Needed as a system dependency of `libLLVM-*.so`. - "zlib", - - # Needed for patching ELF binaries (see doc comment above). - "patchelf", - ] - # Run `nix-build` to "build" each dependency (which will likely reuse # the existing `/nix/store` copy, or at most download a pre-built copy). - # Importantly, we don't rely on `nix-build` printing the `/nix/store` - # path on stdout, but use `-o` to symlink it into `stage0/.nix-deps/$dep`, - # ensuring garbage collection will never remove the `/nix/store` path - # (which would break our patched binaries that hardcode those paths). - for dep in nix_deps: - try: - subprocess.check_output([ - "nix-build", "<nixpkgs>", - "-A", dep, - "-o", "{}/{}".format(nix_deps_dir, dep), - ]) - except subprocess.CalledProcessError as reason: - print("warning: failed to call nix-build:", reason) - return - + # + # Importantly, we create a gc-root called `.nix-deps` in the `build/` + # directory, but still reference the actual `/nix/store` path in the rpath + # as it makes it significantly more robust against changes to the location of + # the `.nix-deps` location. + # + # bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). + # zlib: Needed as a system dependency of `libLLVM-*.so`. + # patchelf: Needed for patching ELF binaries (see doc comment above). + nix_deps_dir = "{}/{}".format(self.build_dir, ".nix-deps") + nix_expr = ''' + with (import <nixpkgs> {}); + symlinkJoin { + name = "rust-stage0-dependencies"; + paths = [ + zlib + patchelf + stdenv.cc.bintools + ]; + } + ''' + try: + subprocess.check_output([ + "nix-build", "-E", nix_expr, "-o", nix_deps_dir, + ]) + except subprocess.CalledProcessError as reason: + print("warning: failed to call nix-build:", reason) + return self.nix_deps_dir = nix_deps_dir - patchelf = "{}/patchelf/bin/patchelf".format(nix_deps_dir) - patchelf_args = [] - - if rpath_libz: - # Patch RPATH to add `zlib` dependency that stems from LLVM - dylib_deps = ["zlib"] - rpath_entries = [ - # Relative default, all binary and dynamic libraries we ship - # appear to have this (even when `../lib` is redundant). - "$ORIGIN/../lib", - ] + ["{}/{}/lib".format(nix_deps_dir, dep) for dep in dylib_deps] - patchelf_args += ["--set-rpath", ":".join(rpath_entries)] + patchelf = "{}/bin/patchelf".format(nix_deps_dir) + rpath_entries = [ + # Relative default, all binary and dynamic libraries we ship + # appear to have this (even when `../lib` is redundant). + "$ORIGIN/../lib", + os.path.join(os.path.realpath(nix_deps_dir), "lib") + ] + patchelf_args = ["--set-rpath", ":".join(rpath_entries)] if not fname.endswith(".so"): # Finally, set the corret .interp for binaries - bintools_dir = "{}/stdenv.cc.bintools".format(nix_deps_dir) - with open("{}/nix-support/dynamic-linker".format(bintools_dir)) as dynamic_linker: + with open("{}/nix-support/dynamic-linker".format(nix_deps_dir)) as dynamic_linker: patchelf_args += ["--set-interpreter", dynamic_linker.read().rstrip()] try: diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 8244c7710ab..66a88e85fea 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -462,11 +462,13 @@ impl Step for StartupObjects { let dst_file = &dst_dir.join(file.to_string() + ".o"); if !up_to_date(src_file, dst_file) { let mut cmd = Command::new(&builder.initial_rustc); + cmd.env("RUSTC_BOOTSTRAP", "1"); + if !builder.local_rebuild { + // a local_rebuild compiler already has stage1 features + cmd.arg("--cfg").arg("bootstrap"); + } builder.run( - cmd.env("RUSTC_BOOTSTRAP", "1") - .arg("--cfg") - .arg("bootstrap") - .arg("--target") + cmd.arg("--target") .arg(target.rustc_target_arg()) .arg("--emit=obj") .arg("-o") diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 117201ab3cd..b9d7ecf8c0e 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1232,6 +1232,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display())); if builder.is_fuse_ld_lld(compiler.host) { hostflags.push("-Clink-args=-fuse-ld=lld".to_string()); + hostflags.push("-Clink-arg=-Wl,--threads=1".to_string()); } cmd.arg("--host-rustcflags").arg(hostflags.join(" ")); @@ -1239,6 +1240,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display())); if builder.is_fuse_ld_lld(target) { targetflags.push("-Clink-args=-fuse-ld=lld".to_string()); + targetflags.push("-Clink-arg=-Wl,--threads=1".to_string()); } cmd.arg("--target-rustcflags").arg(targetflags.join(" ")); diff --git a/src/doc/embedded-book b/src/doc/embedded-book -Subproject d3f2ace94d51610cf3e3c265705bb8416d37f8e +Subproject 569c3391f5c0cc43433bc77831d17f8ff4d7660 diff --git a/src/doc/nomicon b/src/doc/nomicon -Subproject 6fe476943afd53a9a6e91f38a6ea7bb48811d8f +Subproject 8551afbb2ca6f5ea37fe58380318b209785e4e0 diff --git a/src/doc/reference b/src/doc/reference -Subproject fd97729e2d82f8b08d68a31c9bfdf0c37a7fd54 +Subproject e1abb17cd94cd5a8a374b48e1bc8134a2208ed4 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example -Subproject 29d91f591c90dd18fdca6d23f1a9caf9c139d0d +Subproject c80f0b09fc15b9251825343be910c08531938ab diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide -Subproject 0687daac28939c476df51778f5a1d1aff1a3fdd +Subproject a9bd2bbf31e4f92b5d3d8e80b22839d0cc7a202 diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index 946c354fd9d..4f9033cedc3 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -35,7 +35,7 @@ Inline assembly is currently supported on the following architectures: Let us start with the simplest possible example: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] unsafe { asm!("nop"); } @@ -52,7 +52,7 @@ Now inserting an instruction that does nothing is rather boring. Let us do somet actually acts on data: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let x: u64; unsafe { asm!("mov {}, 5", out(reg) x); @@ -74,7 +74,7 @@ the template and will read the variable from there after the inline assembly fin Let us see another example that also uses an input: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let i: u64 = 3; let o: u64; unsafe { @@ -114,7 +114,7 @@ readability, and allows reordering instructions without changing the argument or We can further refine the above example to avoid the `mov` instruction: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let mut x: u64 = 3; unsafe { asm!("add {0}, {number}", inout(reg) x, number = const 5); @@ -128,7 +128,7 @@ This is different from specifying an input and output separately in that it is g It is also possible to specify different variables for the input and output parts of an `inout` operand: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let x: u64 = 3; let y: u64; unsafe { @@ -150,7 +150,7 @@ There is also a `inlateout` variant of this specifier. Here is an example where `inlateout` *cannot* be used: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let mut a: u64 = 4; let b: u64 = 4; let c: u64 = 4; @@ -171,7 +171,7 @@ Here the compiler is free to allocate the same register for inputs `b` and `c` s However the following example can use `inlateout` since the output is only modified after all input registers have been read: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let mut a: u64 = 4; let b: u64 = 4; unsafe { @@ -190,7 +190,7 @@ While `reg` is generally available on any architecture, these are highly archite among others can be addressed by their name. ```rust,allow_fail,no_run -# #![feature(asm)] +#![feature(asm)] let cmd = 0xd1; unsafe { asm!("out 0x64, eax", in("eax") cmd); @@ -206,7 +206,7 @@ Note that unlike other operand types, explicit register operands cannot be used Consider this example which uses the x86 `mul` instruction: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] fn mul(a: u64, b: u64) -> u128 { let lo: u64; let hi: u64; @@ -242,7 +242,7 @@ We need to tell the compiler about this since it may need to save and restore th around the inline assembly block. ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let ebx: u32; let ecx: u32; @@ -272,7 +272,7 @@ However we still need to tell the compiler that `eax` and `edx` have been modifi This can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] // Multiply x by 6 using shifts and adds let mut x: u64 = 4; unsafe { @@ -294,7 +294,7 @@ A special operand type, `sym`, allows you to use the symbol name of a `fn` or `s This allows you to call a function or access a global variable without needing to keep its address in a register. ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] extern "C" fn foo(arg: i32) { println!("arg = {}", arg); } @@ -316,7 +316,7 @@ fn call_foo(arg: i32) { // Also mark AVX-512 registers as clobbered. This is accepted by the // compiler even if AVX-512 is not enabled on the current target. out("xmm16") _, out("xmm17") _, out("xmm18") _, out("xmm19") _, - out("xmm20") _, out("xmm21") _, out("xmm22") _, out("xmm13") _, + out("xmm20") _, out("xmm21") _, out("xmm22") _, out("xmm23") _, out("xmm24") _, out("xmm25") _, out("xmm26") _, out("xmm27") _, out("xmm28") _, out("xmm29") _, out("xmm30") _, out("xmm31") _, ) @@ -336,7 +336,7 @@ By default the compiler will always choose the name that refers to the full regi This default can be overriden by using modifiers on the template string operands, just like you would with format strings: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let mut x: u16 = 0xab; unsafe { @@ -361,7 +361,7 @@ For example, in x86/x86_64 and intel assembly syntax, you should wrap inputs/out to indicate they are memory operands: ```rust,allow_fail -# #![feature(asm, llvm_asm)] +#![feature(asm, llvm_asm)] # fn load_fpu_control_word(control: u16) { unsafe { asm!("fldcw [{}]", in(reg) &control, options(nostack)); @@ -372,6 +372,43 @@ unsafe { # } ``` +## Labels + +The compiler is allowed to instantiate multiple copies an `asm!` block, for example when the function containing it is inlined in multiple places. As a consequence, you should only use GNU assembler [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions. + +Moreover, due to [an llvm bug], you shouldn't use labels exclusively made of `0` and `1` digits, e.g. `0`, `11` or `101010`, as they may end up being interpreted as binary values. + +```rust,allow_fail +#![feature(asm)] + +let mut a = 0; +unsafe { + asm!( + "mov {0}, 10", + "2:", + "sub {0}, 1", + "cmp {0}, 3", + "jle 2f", + "jmp 2b", + "2:", + "add {0}, 2", + out(reg) a + ); +} +assert_eq!(a, 5); +``` + +This will decrement the `{0}` register value from 10 to 3, then add 2 and store it in `a`. + +This example show a few thing: + +First that the same number can be used as a label multiple times in the same inline block. + +Second, that when a numeric label is used as a reference (as an instruction operand, for example), the suffixes b (“backward”) or f (“forward”) should be added to the numeric label. It will then refer to the nearest label defined by this number in this direction. + +[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels +[an llvm bug]: https://bugs.llvm.org/show_bug.cgi?id=36144 + ## Options By default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However in many cases, it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better. @@ -379,7 +416,7 @@ By default, an inline assembly block is treated the same way as an external FFI Let's take our previous example of an `add` instruction: ```rust,allow_fail -# #![feature(asm)] +#![feature(asm)] let mut a: u64 = 4; let b: u64 = 4; unsafe { @@ -787,8 +824,5 @@ The compiler performs some additional checks on options: - You are responsible for switching any target-specific state (e.g. thread-local storage, stack bounds). - The set of memory locations that you may access is the intersection of those allowed by the `asm!` blocks you entered and exited. - You cannot assume that an `asm!` block will appear exactly once in the output binary. The compiler is allowed to instantiate multiple copies of the `asm!` block, for example when the function containing it is inlined in multiple places. - - As a consequence, you should only use [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions. > **Note**: As a general rule, the flags covered by `preserves_flags` are those which are *not* preserved when performing a function call. - -[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 217e899001e..7d33cf21013 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2287,14 +2287,14 @@ impl Clean<Item> for (&hir::MacroDef<'_>, Option<Symbol>) { if matchers.len() <= 1 { format!( "{}macro {}{} {{\n ...\n}}", - vis.print_with_space(cx.tcx, def_id, &cx.cache), + vis.to_src_with_space(cx.tcx, def_id), name, matchers.iter().map(|span| span.to_src(cx)).collect::<String>(), ) } else { format!( "{}macro {} {{\n{}}}", - vis.print_with_space(cx.tcx, def_id, &cx.cache), + vis.to_src_with_space(cx.tcx, def_id), name, matchers .iter() diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index f3c9b987eb0..2b25c6a26bc 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -41,6 +41,7 @@ use crate::core::DocContext; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::render::cache::ExternalLocation; +use crate::html::render::Context; use self::FnRetTy::*; use self::ItemKind::*; @@ -193,19 +194,18 @@ impl Item { self.attrs.collapsed_doc_value() } - crate fn links(&self, cache: &Cache) -> Vec<RenderedLink> { + crate fn links(&self, cx: &Context<'_>) -> Vec<RenderedLink> { use crate::html::format::href; - use crate::html::render::CURRENT_DEPTH; - cache + cx.cache() .intra_doc_links .get(&self.def_id) .map_or(&[][..], |v| v.as_slice()) .iter() - .filter_map(|ItemLink { link: s, link_text, did, fragment }| { + .filter_map(|ItemLink { link: s, link_text, did, ref fragment }| { match *did { Some(did) => { - if let Some((mut href, ..)) = href(did, cache) { + if let Some((mut href, ..)) = href(did, cx) { if let Some(ref fragment) = *fragment { href.push('#'); href.push_str(fragment); @@ -219,16 +219,26 @@ impl Item { None } } + // FIXME(83083): using fragments as a side-channel for + // primitive names is very unfortunate None => { + let relative_to = &cx.current; if let Some(ref fragment) = *fragment { - let url = match cache.extern_locations.get(&self.def_id.krate) { + let url = match cx.cache().extern_locations.get(&self.def_id.krate) { Some(&(_, _, ExternalLocation::Local)) => { - let depth = CURRENT_DEPTH.with(|l| l.get()); - "../".repeat(depth) + if relative_to[0] == "std" { + let depth = relative_to.len() - 1; + "../".repeat(depth) + } else { + let depth = relative_to.len(); + format!("{}std/", "../".repeat(depth)) + } + } + Some(&(_, _, ExternalLocation::Remote(ref s))) => { + format!("{}/std/", s.trim_end_matches('/')) } - Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(), Some(&(_, _, ExternalLocation::Unknown)) | None => format!( - "https://doc.rust-lang.org/{}", + "https://doc.rust-lang.org/{}/std/", crate::doc_rust_lang_org_channel(), ), }; @@ -238,9 +248,8 @@ impl Item { original_text: s.clone(), new_text: link_text.clone(), href: format!( - "{}{}std/primitive.{}.html{}", + "{}primitive.{}.html{}", url, - if !url.ends_with('/') { "/" } else { "" }, &fragment[..tail], &fragment[tail..] ), diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 1e79bd09128..c2a971d6375 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -401,7 +401,7 @@ crate fn resolve_type(cx: &mut DocContext<'_>, path: Path, id: hir::HirId) -> Ty return Generic(kw::SelfUpper); } Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => { - return Generic(Symbol::intern(&format!("{:#}", path.print(&cx.cache, cx.tcx)))); + return Generic(Symbol::intern(&path.whole_name())); } Res::SelfTy(..) | Res::Def(DefKind::TyParam | DefKind::AssocTy, _) => true, _ => false, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index c9fdaa50534..be7bff1a29c 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -5,8 +5,8 @@ use rustc_driver::abort_on_err; use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::json::JsonEmitter; use rustc_feature::UnstableFeatures; -use rustc_hir::def::{Namespace::TypeNS, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def::Res; +use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, LOCAL_CRATE}; use rustc_hir::HirId; use rustc_hir::{ intravisit::{self, NestedVisitorMap, Visitor}, @@ -356,55 +356,7 @@ crate fn create_resolver<'a>( let (krate, resolver, _) = &*parts; let resolver = resolver.borrow().clone(); - // Letting the resolver escape at the end of the function leads to inconsistencies between the - // crates the TyCtxt sees and the resolver sees (because the resolver could load more crates - // after escaping). Hopefully `IntraLinkCrateLoader` gets all the crates we need ... - struct IntraLinkCrateLoader { - current_mod: DefId, - resolver: Rc<RefCell<interface::BoxedResolver>>, - } - impl ast::visit::Visitor<'_> for IntraLinkCrateLoader { - fn visit_attribute(&mut self, attr: &ast::Attribute) { - use crate::html::markdown::{markdown_links, MarkdownLink}; - use crate::passes::collect_intra_doc_links::Disambiguator; - - if let Some(doc) = attr.doc_str() { - for MarkdownLink { link, .. } in markdown_links(&doc.as_str()) { - // FIXME: this misses a *lot* of the preprocessing done in collect_intra_doc_links - // I think most of it shouldn't be necessary since we only need the crate prefix? - let path_str = match Disambiguator::from_str(&link) { - Ok(x) => x.map_or(link.as_str(), |(_, p)| p), - Err(_) => continue, - }; - self.resolver.borrow_mut().access(|resolver| { - let _ = resolver.resolve_str_path_error( - attr.span, - path_str, - TypeNS, - self.current_mod, - ); - }); - } - } - ast::visit::walk_attribute(self, attr); - } - - fn visit_item(&mut self, item: &ast::Item) { - use rustc_ast_lowering::ResolverAstLowering; - - if let ast::ItemKind::Mod(..) = item.kind { - let new_mod = - self.resolver.borrow_mut().access(|resolver| resolver.local_def_id(item.id)); - let old_mod = mem::replace(&mut self.current_mod, new_mod.to_def_id()); - ast::visit::walk_item(self, item); - self.current_mod = old_mod; - } else { - ast::visit::walk_item(self, item); - } - } - } - let crate_id = LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(); - let mut loader = IntraLinkCrateLoader { current_mod: crate_id, resolver }; + let mut loader = crate::passes::collect_intra_doc_links::IntraLinkCrateLoader::new(resolver); ast::visit::walk_crate(&mut loader, krate); loader.resolver diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index a004ee5054e..ca364b9f103 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -20,6 +20,7 @@ use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::render::cache::ExternalLocation; +use crate::html::render::Context; use crate::html::render::CURRENT_DEPTH; crate trait Print { @@ -125,19 +126,18 @@ fn comma_sep<T: fmt::Display>(items: impl Iterator<Item = T>) -> impl fmt::Displ crate fn print_generic_bounds<'a, 'tcx: 'a>( bounds: &'a [clean::GenericBound], - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { let mut bounds_dup = FxHashSet::default(); for (i, bound) in - bounds.iter().filter(|b| bounds_dup.insert(b.print(cache, tcx).to_string())).enumerate() + bounds.iter().filter(|b| bounds_dup.insert(b.print(cx).to_string())).enumerate() { if i > 0 { f.write_str(" + ")?; } - fmt::Display::fmt(&bound.print(cache, tcx), f)?; + fmt::Display::fmt(&bound.print(cx), f)?; } Ok(()) }) @@ -146,8 +146,7 @@ crate fn print_generic_bounds<'a, 'tcx: 'a>( impl clean::GenericParamDef { crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self.kind { clean::GenericParamDefKind::Lifetime => write!(f, "{}", self.name), @@ -156,17 +155,17 @@ impl clean::GenericParamDef { if !bounds.is_empty() { if f.alternate() { - write!(f, ": {:#}", print_generic_bounds(bounds, cache, tcx))?; + write!(f, ": {:#}", print_generic_bounds(bounds, cx))?; } else { - write!(f, ": {}", print_generic_bounds(bounds, cache, tcx))?; + write!(f, ": {}", print_generic_bounds(bounds, cx))?; } } if let Some(ref ty) = default { if f.alternate() { - write!(f, " = {:#}", ty.print(cache, tcx))?; + write!(f, " = {:#}", ty.print(cx))?; } else { - write!(f, " = {}", ty.print(cache, tcx))?; + write!(f, " = {}", ty.print(cx))?; } } @@ -174,9 +173,9 @@ impl clean::GenericParamDef { } clean::GenericParamDefKind::Const { ref ty, .. } => { if f.alternate() { - write!(f, "const {}: {:#}", self.name, ty.print(cache, tcx)) + write!(f, "const {}: {:#}", self.name, ty.print(cx)) } else { - write!(f, "const {}: {}", self.name, ty.print(cache, tcx)) + write!(f, "const {}: {}", self.name, ty.print(cx)) } } }) @@ -186,8 +185,7 @@ impl clean::GenericParamDef { impl clean::Generics { crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { let real_params = @@ -196,9 +194,9 @@ impl clean::Generics { return Ok(()); } if f.alternate() { - write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print(cache, tcx)))) + write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print(cx)))) } else { - write!(f, "<{}>", comma_sep(real_params.iter().map(|g| g.print(cache, tcx)))) + write!(f, "<{}>", comma_sep(real_params.iter().map(|g| g.print(cx)))) } }) } @@ -209,8 +207,7 @@ impl clean::Generics { /// * Whether the where-clause needs to add a comma and newline after the last bound. crate fn print_where_clause<'a, 'tcx: 'a>( gens: &'a clean::Generics, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, indent: usize, end_newline: bool, ) -> impl fmt::Display + 'a + Captures<'tcx> { @@ -241,14 +238,14 @@ crate fn print_where_clause<'a, 'tcx: 'a>( if f.alternate() { clause.push_str(&format!( "{:#}: {:#}", - ty.print(cache, tcx), - print_generic_bounds(bounds, cache, tcx) + ty.print(cx), + print_generic_bounds(bounds, cx) )); } else { clause.push_str(&format!( "{}: {}", - ty.print(cache, tcx), - print_generic_bounds(bounds, cache, tcx) + ty.print(cx), + print_generic_bounds(bounds, cx) )); } } @@ -258,24 +255,16 @@ crate fn print_where_clause<'a, 'tcx: 'a>( lifetime.print(), bounds .iter() - .map(|b| b.print(cache, tcx).to_string()) + .map(|b| b.print(cx).to_string()) .collect::<Vec<_>>() .join(" + ") )); } clean::WherePredicate::EqPredicate { lhs, rhs } => { if f.alternate() { - clause.push_str(&format!( - "{:#} == {:#}", - lhs.print(cache, tcx), - rhs.print(cache, tcx), - )); + clause.push_str(&format!("{:#} == {:#}", lhs.print(cx), rhs.print(cx),)); } else { - clause.push_str(&format!( - "{} == {}", - lhs.print(cache, tcx), - rhs.print(cache, tcx), - )); + clause.push_str(&format!("{} == {}", lhs.print(cx), rhs.print(cx),)); } } } @@ -327,8 +316,7 @@ impl clean::Constant { impl clean::PolyTrait { fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { if !self.generic_params.is_empty() { @@ -336,20 +324,20 @@ impl clean::PolyTrait { write!( f, "for<{:#}> ", - comma_sep(self.generic_params.iter().map(|g| g.print(cache, tcx))) + comma_sep(self.generic_params.iter().map(|g| g.print(cx))) )?; } else { write!( f, "for<{}> ", - comma_sep(self.generic_params.iter().map(|g| g.print(cache, tcx))) + comma_sep(self.generic_params.iter().map(|g| g.print(cx))) )?; } } if f.alternate() { - write!(f, "{:#}", self.trait_.print(cache, tcx)) + write!(f, "{:#}", self.trait_.print(cx)) } else { - write!(f, "{}", self.trait_.print(cache, tcx)) + write!(f, "{}", self.trait_.print(cx)) } }) } @@ -358,8 +346,7 @@ impl clean::PolyTrait { impl clean::GenericBound { crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self { clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()), @@ -370,9 +357,9 @@ impl clean::GenericBound { hir::TraitBoundModifier::MaybeConst => "?const", }; if f.alternate() { - write!(f, "{}{:#}", modifier_str, ty.print(cache, tcx)) + write!(f, "{}{:#}", modifier_str, ty.print(cx)) } else { - write!(f, "{}{}", modifier_str, ty.print(cache, tcx)) + write!(f, "{}{}", modifier_str, ty.print(cx)) } } }) @@ -382,8 +369,7 @@ impl clean::GenericBound { impl clean::GenericArgs { fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { match self { @@ -401,9 +387,9 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", arg.print(cache, tcx))?; + write!(f, "{:#}", arg.print(cx))?; } else { - write!(f, "{}", arg.print(cache, tcx))?; + write!(f, "{}", arg.print(cx))?; } } for binding in bindings { @@ -412,9 +398,9 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", binding.print(cache, tcx))?; + write!(f, "{:#}", binding.print(cx))?; } else { - write!(f, "{}", binding.print(cache, tcx))?; + write!(f, "{}", binding.print(cx))?; } } if f.alternate() { @@ -433,17 +419,17 @@ impl clean::GenericArgs { } comma = true; if f.alternate() { - write!(f, "{:#}", ty.print(cache, tcx))?; + write!(f, "{:#}", ty.print(cx))?; } else { - write!(f, "{}", ty.print(cache, tcx))?; + write!(f, "{}", ty.print(cx))?; } } f.write_str(")")?; if let Some(ref ty) = *output { if f.alternate() { - write!(f, " -> {:#}", ty.print(cache, tcx))?; + write!(f, " -> {:#}", ty.print(cx))?; } else { - write!(f, " -> {}", ty.print(cache, tcx))?; + write!(f, " -> {}", ty.print(cx))?; } } } @@ -453,98 +439,89 @@ impl clean::GenericArgs { } } -impl clean::PathSegment { - crate fn print<'a, 'tcx: 'a>( - &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { - display_fn(move |f| { - if f.alternate() { - write!(f, "{}{:#}", self.name, self.args.print(cache, tcx)) - } else { - write!(f, "{}{}", self.name, self.args.print(cache, tcx)) - } - }) +crate fn href(did: DefId, cx: &Context<'_>) -> Option<(String, ItemType, Vec<String>)> { + let cache = &cx.cache(); + let relative_to = &cx.current; + fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] { + if shortty == ItemType::Module { &fqp[..] } else { &fqp[..fqp.len() - 1] } } -} - -impl clean::Path { - crate fn print<'a, 'tcx: 'a>( - &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, - ) -> impl fmt::Display + 'a + Captures<'tcx> { - display_fn(move |f| { - if self.global { - f.write_str("::")? - } - for (i, seg) in self.segments.iter().enumerate() { - if i > 0 { - f.write_str("::")? - } - if f.alternate() { - write!(f, "{:#}", seg.print(cache, tcx))?; - } else { - write!(f, "{}", seg.print(cache, tcx))?; - } - } - Ok(()) - }) - } -} - -crate fn href(did: DefId, cache: &Cache) -> Option<(String, ItemType, Vec<String>)> { if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private { return None; } - let depth = CURRENT_DEPTH.with(|l| l.get()); - let (fqp, shortty, mut url) = match cache.paths.get(&did) { - Some(&(ref fqp, shortty)) => (fqp, shortty, "../".repeat(depth)), + let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) { + Some(&(ref fqp, shortty)) => (fqp, shortty, { + let module_fqp = to_module_fqp(shortty, fqp); + href_relative_parts(module_fqp, relative_to) + }), None => { let &(ref fqp, shortty) = cache.external_paths.get(&did)?; + let module_fqp = to_module_fqp(shortty, fqp); ( fqp, shortty, match cache.extern_locations[&did.krate] { - (.., ExternalLocation::Remote(ref s)) => s.to_string(), - (.., ExternalLocation::Local) => "../".repeat(depth), + (.., ExternalLocation::Remote(ref s)) => { + let s = s.trim_end_matches('/'); + let mut s = vec![&s[..]]; + s.extend(module_fqp[..].iter().map(String::as_str)); + s + } + (.., ExternalLocation::Local) => href_relative_parts(module_fqp, relative_to), (.., ExternalLocation::Unknown) => return None, }, ) } }; - for component in &fqp[..fqp.len() - 1] { - url.push_str(component); - url.push('/'); - } + let last = &fqp.last().unwrap()[..]; + let filename; match shortty { ItemType::Module => { - url.push_str(fqp.last().unwrap()); - url.push_str("/index.html"); + url_parts.push("index.html"); } _ => { - url.push_str(shortty.as_str()); - url.push('.'); - url.push_str(fqp.last().unwrap()); - url.push_str(".html"); + filename = format!("{}.{}.html", shortty.as_str(), last); + url_parts.push(&filename); + } + } + Some((url_parts.join("/"), shortty, fqp.to_vec())) +} + +/// Both paths should only be modules. +/// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will +/// both need `../iter/trait.Iterator.html` to get at the iterator trait. +crate fn href_relative_parts<'a>(fqp: &'a [String], relative_to_fqp: &'a [String]) -> Vec<&'a str> { + for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() { + // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1) + if f != r { + let dissimilar_part_count = relative_to_fqp.len() - i; + let fqp_module = fqp[i..fqp.len()].iter().map(String::as_str); + return std::iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect(); } } - Some((url, shortty, fqp.to_vec())) + // e.g. linking to std::sync::atomic from std::sync + if relative_to_fqp.len() < fqp.len() { + fqp[relative_to_fqp.len()..fqp.len()].iter().map(String::as_str).collect() + // e.g. linking to std::sync from std::sync::atomic + } else if fqp.len() < relative_to_fqp.len() { + let dissimilar_part_count = relative_to_fqp.len() - fqp.len(); + std::iter::repeat("..").take(dissimilar_part_count).collect() + // linking to the same module + } else { + Vec::new() + } } /// Used when rendering a `ResolvedPath` structure. This invokes the `path` /// rendering function with the necessary arguments for linking to a local path. -fn resolved_path<'a, 'tcx: 'a>( +fn resolved_path<'a, 'cx: 'a>( w: &mut fmt::Formatter<'_>, did: DefId, path: &clean::Path, print_all: bool, use_absolute: bool, - cache: &Cache, - tcx: TyCtxt<'tcx>, + cx: &'cx Context<'_>, ) -> fmt::Result { let last = path.segments.last().unwrap(); @@ -554,22 +531,22 @@ fn resolved_path<'a, 'tcx: 'a>( } } if w.alternate() { - write!(w, "{}{:#}", &last.name, last.args.print(cache, tcx))?; + write!(w, "{}{:#}", &last.name, last.args.print(cx))?; } else { let path = if use_absolute { - if let Some((_, _, fqp)) = href(did, cache) { + if let Some((_, _, fqp)) = href(did, cx) { format!( "{}::{}", fqp[..fqp.len() - 1].join("::"), - anchor(did, fqp.last().unwrap(), cache) + anchor(did, fqp.last().unwrap(), cx) ) } else { last.name.to_string() } } else { - anchor(did, &*last.name.as_str(), cache).to_string() + anchor(did, &*last.name.as_str(), cx).to_string() }; - write!(w, "{}{}", path, last.args.print(cache, tcx))?; + write!(w, "{}{}", path, last.args.print(cx))?; } Ok(()) } @@ -627,14 +604,13 @@ fn primitive_link( /// Helper to render type parameters fn tybounds<'a, 'tcx: 'a>( param_names: &'a Option<Vec<clean::GenericBound>>, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match *param_names { Some(ref params) => { for param in params { write!(f, " + ")?; - fmt::Display::fmt(¶m.print(cache, tcx), f)?; + fmt::Display::fmt(¶m.print(cx), f)?; } Ok(()) } @@ -642,9 +618,14 @@ fn tybounds<'a, 'tcx: 'a>( }) } -crate fn anchor<'a>(did: DefId, text: &'a str, cache: &'a Cache) -> impl fmt::Display + 'a { +crate fn anchor<'a, 'cx: 'a>( + did: DefId, + text: &'a str, + cx: &'cx Context<'_>, +) -> impl fmt::Display + 'a { + let parts = href(did, cx); display_fn(move |f| { - if let Some((url, short_ty, fqp)) = href(did, cache) { + if let Some((url, short_ty, fqp)) = parts { write!( f, r#"<a class="{}" href="{}" title="{} {}">{}</a>"#, @@ -660,12 +641,11 @@ crate fn anchor<'a>(did: DefId, text: &'a str, cache: &'a Cache) -> impl fmt::Di }) } -fn fmt_type( +fn fmt_type<'cx>( t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool, - cache: &Cache, - tcx: TyCtxt<'_>, + cx: &'cx Context<'_>, ) -> fmt::Result { debug!("fmt_type(t = {:?})", t); @@ -676,69 +656,69 @@ fn fmt_type( f.write_str("dyn ")?; } // Paths like `T::Output` and `Self::Output` should be rendered with all segments. - resolved_path(f, did, path, is_generic, use_absolute, cache, tcx)?; - fmt::Display::fmt(&tybounds(param_names, cache, tcx), f) + resolved_path(f, did, path, is_generic, use_absolute, cx)?; + fmt::Display::fmt(&tybounds(param_names, cx), f) } clean::Infer => write!(f, "_"), - clean::Primitive(prim) => primitive_link(f, prim, prim.as_str(), cache), + clean::Primitive(prim) => primitive_link(f, prim, prim.as_str(), &cx.cache()), clean::BareFunction(ref decl) => { if f.alternate() { write!( f, "{:#}{}{:#}fn{:#}", - decl.print_hrtb_with_space(cache, tcx), + decl.print_hrtb_with_space(cx), decl.unsafety.print_with_space(), print_abi_with_space(decl.abi), - decl.decl.print(cache, tcx), + decl.decl.print(cx), ) } else { write!( f, "{}{}{}", - decl.print_hrtb_with_space(cache, tcx), + decl.print_hrtb_with_space(cx), decl.unsafety.print_with_space(), print_abi_with_space(decl.abi) )?; - primitive_link(f, PrimitiveType::Fn, "fn", cache)?; - write!(f, "{}", decl.decl.print(cache, tcx)) + primitive_link(f, PrimitiveType::Fn, "fn", &cx.cache())?; + write!(f, "{}", decl.decl.print(cx)) } } clean::Tuple(ref typs) => { match &typs[..] { - &[] => primitive_link(f, PrimitiveType::Unit, "()", cache), + &[] => primitive_link(f, PrimitiveType::Unit, "()", &cx.cache()), &[ref one] => { - primitive_link(f, PrimitiveType::Tuple, "(", cache)?; + primitive_link(f, PrimitiveType::Tuple, "(", &cx.cache())?; // Carry `f.alternate()` into this display w/o branching manually. - fmt::Display::fmt(&one.print(cache, tcx), f)?; - primitive_link(f, PrimitiveType::Tuple, ",)", cache) + fmt::Display::fmt(&one.print(cx), f)?; + primitive_link(f, PrimitiveType::Tuple, ",)", &cx.cache()) } many => { - primitive_link(f, PrimitiveType::Tuple, "(", cache)?; + primitive_link(f, PrimitiveType::Tuple, "(", &cx.cache())?; for (i, item) in many.iter().enumerate() { if i != 0 { write!(f, ", ")?; } - fmt::Display::fmt(&item.print(cache, tcx), f)?; + fmt::Display::fmt(&item.print(cx), f)?; } - primitive_link(f, PrimitiveType::Tuple, ")", cache) + primitive_link(f, PrimitiveType::Tuple, ")", &cx.cache()) } } } clean::Slice(ref t) => { - primitive_link(f, PrimitiveType::Slice, "[", cache)?; - fmt::Display::fmt(&t.print(cache, tcx), f)?; - primitive_link(f, PrimitiveType::Slice, "]", cache) + primitive_link(f, PrimitiveType::Slice, "[", &cx.cache())?; + fmt::Display::fmt(&t.print(cx), f)?; + primitive_link(f, PrimitiveType::Slice, "]", &cx.cache()) } clean::Array(ref t, ref n) => { - primitive_link(f, PrimitiveType::Array, "[", cache)?; - fmt::Display::fmt(&t.print(cache, tcx), f)?; + primitive_link(f, PrimitiveType::Array, "[", &cx.cache())?; + fmt::Display::fmt(&t.print(cx), f)?; if f.alternate() { - primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), cache) + primitive_link(f, PrimitiveType::Array, &format!("; {}]", n), &cx.cache()) } else { - primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), cache) + primitive_link(f, PrimitiveType::Array, &format!("; {}]", Escape(n)), &cx.cache()) } } - clean::Never => primitive_link(f, PrimitiveType::Never, "!", cache), + clean::Never => primitive_link(f, PrimitiveType::Never, "!", &cx.cache()), clean::RawPointer(m, ref t) => { let m = match m { hir::Mutability::Mut => "mut", @@ -750,15 +730,15 @@ fn fmt_type( primitive_link( f, clean::PrimitiveType::RawPointer, - &format!("*{} {:#}", m, t.print(cache, tcx)), - cache, + &format!("*{} {:#}", m, t.print(cx)), + &cx.cache(), ) } else { primitive_link( f, clean::PrimitiveType::RawPointer, - &format!("*{} {}", m, t.print(cache, tcx)), - cache, + &format!("*{} {}", m, t.print(cx)), + &cx.cache(), ) } } @@ -767,9 +747,9 @@ fn fmt_type( f, clean::PrimitiveType::RawPointer, &format!("*{} ", m), - cache, + &cx.cache(), )?; - fmt::Display::fmt(&t.print(cache, tcx), f) + fmt::Display::fmt(&t.print(cx), f) } } } @@ -789,15 +769,15 @@ fn fmt_type( primitive_link( f, PrimitiveType::Slice, - &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cache, tcx)), - cache, + &format!("{}{}{}[{:#}]", amp, lt, m, bt.print(cx)), + &cx.cache(), ) } else { primitive_link( f, PrimitiveType::Slice, - &format!("{}{}{}[{}]", amp, lt, m, bt.print(cache, tcx)), - cache, + &format!("{}{}{}[{}]", amp, lt, m, bt.print(cx)), + &cx.cache(), ) } } @@ -806,20 +786,20 @@ fn fmt_type( f, PrimitiveType::Slice, &format!("{}{}{}[", amp, lt, m), - cache, + &cx.cache(), )?; if f.alternate() { - write!(f, "{:#}", bt.print(cache, tcx))?; + write!(f, "{:#}", bt.print(cx))?; } else { - write!(f, "{}", bt.print(cache, tcx))?; + write!(f, "{}", bt.print(cx))?; } - primitive_link(f, PrimitiveType::Slice, "]", cache) + primitive_link(f, PrimitiveType::Slice, "]", &cx.cache()) } } } clean::ResolvedPath { param_names: Some(ref v), .. } if !v.is_empty() => { write!(f, "{}{}{}(", amp, lt, m)?; - fmt_type(&ty, f, use_absolute, cache, tcx)?; + fmt_type(&ty, f, use_absolute, cx)?; write!(f, ")") } clean::Generic(..) => { @@ -827,21 +807,21 @@ fn fmt_type( f, PrimitiveType::Reference, &format!("{}{}{}", amp, lt, m), - cache, + &cx.cache(), )?; - fmt_type(&ty, f, use_absolute, cache, tcx) + fmt_type(&ty, f, use_absolute, cx) } _ => { write!(f, "{}{}{}", amp, lt, m)?; - fmt_type(&ty, f, use_absolute, cache, tcx) + fmt_type(&ty, f, use_absolute, cx) } } } clean::ImplTrait(ref bounds) => { if f.alternate() { - write!(f, "impl {:#}", print_generic_bounds(bounds, cache, tcx)) + write!(f, "impl {:#}", print_generic_bounds(bounds, cx)) } else { - write!(f, "impl {}", print_generic_bounds(bounds, cache, tcx)) + write!(f, "impl {}", print_generic_bounds(bounds, cx)) } } clean::QPath { ref name, ref self_type, ref trait_ } => { @@ -853,25 +833,15 @@ fn fmt_type( }; if f.alternate() { if should_show_cast { - write!( - f, - "<{:#} as {:#}>::", - self_type.print(cache, tcx), - trait_.print(cache, tcx) - )? + write!(f, "<{:#} as {:#}>::", self_type.print(cx), trait_.print(cx))? } else { - write!(f, "{:#}::", self_type.print(cache, tcx))? + write!(f, "{:#}::", self_type.print(cx))? } } else { if should_show_cast { - write!( - f, - "<{} as {}>::", - self_type.print(cache, tcx), - trait_.print(cache, tcx) - )? + write!(f, "<{} as {}>::", self_type.print(cx), trait_.print(cx))? } else { - write!(f, "{}::", self_type.print(cache, tcx))? + write!(f, "{}::", self_type.print(cx))? } }; match *trait_ { @@ -886,7 +856,7 @@ fn fmt_type( // everything comes in as a fully resolved QPath (hard to // look at). box clean::ResolvedPath { did, ref param_names, .. } => { - match href(did, cache) { + match href(did, cx) { Some((ref url, _, ref path)) if !f.alternate() => { write!( f, @@ -914,42 +884,40 @@ fn fmt_type( impl clean::Type { crate fn print<'b, 'a: 'b, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'b + Captures<'tcx> { - display_fn(move |f| fmt_type(self, f, false, cache, tcx)) + display_fn(move |f| fmt_type(self, f, false, cx)) } } impl clean::Impl { crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, use_absolute: bool, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { if f.alternate() { - write!(f, "impl{:#} ", self.generics.print(cache, tcx))?; + write!(f, "impl{:#} ", self.generics.print(cx))?; } else { - write!(f, "impl{} ", self.generics.print(cache, tcx))?; + write!(f, "impl{} ", self.generics.print(cx))?; } if let Some(ref ty) = self.trait_ { if self.negative_polarity { write!(f, "!")?; } - fmt::Display::fmt(&ty.print(cache, tcx), f)?; + fmt::Display::fmt(&ty.print(cx), f)?; write!(f, " for ")?; } if let Some(ref ty) = self.blanket_impl { - fmt_type(ty, f, use_absolute, cache, tcx)?; + fmt_type(ty, f, use_absolute, cx)?; } else { - fmt_type(&self.for_, f, use_absolute, cache, tcx)?; + fmt_type(&self.for_, f, use_absolute, cx)?; } - fmt::Display::fmt(&print_where_clause(&self.generics, cache, tcx, 0, true), f)?; + fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, true), f)?; Ok(()) }) } @@ -958,8 +926,7 @@ impl clean::Impl { impl clean::Arguments { crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { for (i, input) in self.values.iter().enumerate() { @@ -967,9 +934,9 @@ impl clean::Arguments { write!(f, "{}: ", input.name)?; } if f.alternate() { - write!(f, "{:#}", input.type_.print(cache, tcx))?; + write!(f, "{:#}", input.type_.print(cx))?; } else { - write!(f, "{}", input.type_.print(cache, tcx))?; + write!(f, "{}", input.type_.print(cx))?; } if i + 1 < self.values.len() { write!(f, ", ")?; @@ -983,13 +950,14 @@ impl clean::Arguments { impl clean::FnRetTy { crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self { clean::Return(clean::Tuple(tys)) if tys.is_empty() => Ok(()), - clean::Return(ty) if f.alternate() => write!(f, " -> {:#}", ty.print(cache, tcx)), - clean::Return(ty) => write!(f, " -> {}", ty.print(cache, tcx)), + clean::Return(ty) if f.alternate() => { + write!(f, " -> {:#}", ty.print(cx)) + } + clean::Return(ty) => write!(f, " -> {}", ty.print(cx)), clean::DefaultReturn => Ok(()), }) } @@ -998,16 +966,11 @@ impl clean::FnRetTy { impl clean::BareFunctionDecl { fn print_hrtb_with_space<'a, 'tcx: 'a>( &'a self, - cache: &'a Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { if !self.generic_params.is_empty() { - write!( - f, - "for<{}> ", - comma_sep(self.generic_params.iter().map(|g| g.print(cache, tcx))) - ) + write!(f, "for<{}> ", comma_sep(self.generic_params.iter().map(|g| g.print(cx)))) } else { Ok(()) } @@ -1018,8 +981,7 @@ impl clean::BareFunctionDecl { impl clean::FnDecl { crate fn print<'b, 'a: 'b, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'b + Captures<'tcx> { display_fn(move |f| { let ellipsis = if self.c_variadic { ", ..." } else { "" }; @@ -1027,17 +989,17 @@ impl clean::FnDecl { write!( f, "({args:#}{ellipsis}){arrow:#}", - args = self.inputs.print(cache, tcx), + args = self.inputs.print(cx), ellipsis = ellipsis, - arrow = self.output.print(cache, tcx) + arrow = self.output.print(cx) ) } else { write!( f, "({args}{ellipsis}){arrow}", - args = self.inputs.print(cache, tcx), + args = self.inputs.print(cx), ellipsis = ellipsis, - arrow = self.output.print(cache, tcx) + arrow = self.output.print(cx) ) } }) @@ -1049,25 +1011,23 @@ impl clean::FnDecl { /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is /// necessary. /// * `asyncness`: Whether the function is async or not. - crate fn full_print<'b, 'a: 'b, 'tcx: 'a>( + crate fn full_print<'a, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, header_len: usize, indent: usize, asyncness: hir::IsAsync, - ) -> impl fmt::Display + 'b + Captures<'tcx> { - display_fn(move |f| self.inner_full_print(cache, tcx, header_len, indent, asyncness, f)) + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { + display_fn(move |f| self.inner_full_print(header_len, indent, asyncness, f, cx)) } fn inner_full_print( &self, - cache: &Cache, - tcx: TyCtxt<'_>, header_len: usize, indent: usize, asyncness: hir::IsAsync, f: &mut fmt::Formatter<'_>, + cx: &Context<'_>, ) -> fmt::Result { let amp = if f.alternate() { "&" } else { "&" }; let mut args = String::new(); @@ -1102,11 +1062,11 @@ impl clean::FnDecl { } clean::SelfExplicit(ref typ) => { if f.alternate() { - args.push_str(&format!("self: {:#}", typ.print(cache, tcx))); + args.push_str(&format!("self: {:#}", typ.print(cx))); } else { - args.push_str(&format!("self: {}", typ.print(cache, tcx))); + args.push_str(&format!("self: {}", typ.print(cx))); } - args_plain.push_str(&format!("self: {:#}", typ.print(cache, tcx))); + args_plain.push_str(&format!("self: {:#}", typ.print(cx))); } } } else { @@ -1120,11 +1080,11 @@ impl clean::FnDecl { } if f.alternate() { - args.push_str(&format!("{:#}", input.type_.print(cache, tcx))); + args.push_str(&format!("{:#}", input.type_.print(cx))); } else { - args.push_str(&input.type_.print(cache, tcx).to_string()); + args.push_str(&input.type_.print(cx).to_string()); } - args_plain.push_str(&format!("{:#}", input.type_.print(cache, tcx))); + args_plain.push_str(&format!("{:#}", input.type_.print(cx))); } if i + 1 < self.inputs.values.len() { args.push(','); @@ -1142,19 +1102,11 @@ impl clean::FnDecl { let arrow_plain; let arrow = if let hir::IsAsync::Async = asyncness { let output = self.sugared_async_return_type(); - arrow_plain = format!("{:#}", output.print(cache, tcx)); - if f.alternate() { - arrow_plain.clone() - } else { - format!("{}", output.print(cache, tcx)) - } + arrow_plain = format!("{:#}", output.print(cx)); + if f.alternate() { arrow_plain.clone() } else { format!("{}", output.print(cx)) } } else { - arrow_plain = format!("{:#}", self.output.print(cache, tcx)); - if f.alternate() { - arrow_plain.clone() - } else { - format!("{}", self.output.print(cache, tcx)) - } + arrow_plain = format!("{:#}", self.output.print(cx)); + if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) } }; let declaration_len = header_len + args_plain.len() + arrow_plain.len(); @@ -1182,12 +1134,9 @@ impl clean::FnDecl { impl clean::Visibility { crate fn print_with_space<'a, 'tcx: 'a>( self, - tcx: TyCtxt<'tcx>, item_did: DefId, - cache: &'a Cache, + cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { - use rustc_span::symbol::kw; - let to_print = match self { clean::Public => "pub ".to_owned(), clean::Inherited => String::new(), @@ -1195,7 +1144,7 @@ impl clean::Visibility { // FIXME(camelid): This may not work correctly if `item_did` is a module. // However, rustdoc currently never displays a module's // visibility, so it shouldn't matter. - let parent_module = find_nearest_parent_module(tcx, item_did); + let parent_module = find_nearest_parent_module(cx.tcx(), item_did); if vis_did.index == CRATE_DEF_INDEX { "pub(crate) ".to_owned() @@ -1204,26 +1153,19 @@ impl clean::Visibility { // is the same as no visibility modifier String::new() } else if parent_module - .map(|parent| find_nearest_parent_module(tcx, parent)) + .map(|parent| find_nearest_parent_module(cx.tcx(), parent)) .flatten() == Some(vis_did) { "pub(super) ".to_owned() } else { - let path = tcx.def_path(vis_did); + let path = cx.tcx().def_path(vis_did); debug!("path={:?}", path); - let first_name = - path.data[0].data.get_opt_name().expect("modules are always named"); // modified from `resolved_path()` to work with `DefPathData` let last_name = path.data.last().unwrap().data.get_opt_name().unwrap(); - let anchor = anchor(vis_did, &last_name.as_str(), cache).to_string(); + let anchor = anchor(vis_did, &last_name.as_str(), cx).to_string(); - let mut s = "pub(".to_owned(); - if path.data.len() != 1 - || (first_name != kw::SelfLower && first_name != kw::Super) - { - s.push_str("in "); - } + let mut s = "pub(in ".to_owned(); for seg in &path.data[..path.data.len() - 1] { s.push_str(&format!("{}::", seg.data.get_opt_name().unwrap())); } @@ -1234,6 +1176,43 @@ impl clean::Visibility { }; display_fn(move |f| f.write_str(&to_print)) } + + /// This function is the same as print_with_space, except that it renders no links. + /// It's used for macros' rendered source view, which is syntax highlighted and cannot have + /// any HTML in it. + crate fn to_src_with_space<'a, 'tcx: 'a>( + self, + tcx: TyCtxt<'tcx>, + item_did: DefId, + ) -> impl fmt::Display + 'a + Captures<'tcx> { + let to_print = match self { + clean::Public => "pub ".to_owned(), + clean::Inherited => String::new(), + clean::Visibility::Restricted(vis_did) => { + // FIXME(camelid): This may not work correctly if `item_did` is a module. + // However, rustdoc currently never displays a module's + // visibility, so it shouldn't matter. + let parent_module = find_nearest_parent_module(tcx, item_did); + + if vis_did.index == CRATE_DEF_INDEX { + "pub(crate) ".to_owned() + } else if parent_module == Some(vis_did) { + // `pub(in foo)` where `foo` is the parent module + // is the same as no visibility modifier + String::new() + } else if parent_module + .map(|parent| find_nearest_parent_module(tcx, parent)) + .flatten() + == Some(vis_did) + { + "pub(super) ".to_owned() + } else { + format!("pub(in {}) ", tcx.def_path_str(vis_did)) + } + } + }; + display_fn(move |f| f.write_str(&to_print)) + } } crate trait PrintWithSpace { @@ -1277,24 +1256,23 @@ impl PrintWithSpace for hir::Mutability { } impl clean::Import { - crate fn print<'b, 'a: 'b, 'tcx: 'a>( + crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, - ) -> impl fmt::Display + 'b + Captures<'tcx> { + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self.kind { clean::ImportKind::Simple(name) => { if name == self.source.path.last() { - write!(f, "use {};", self.source.print(cache, tcx)) + write!(f, "use {};", self.source.print(cx)) } else { - write!(f, "use {} as {};", self.source.print(cache, tcx), name) + write!(f, "use {} as {};", self.source.print(cx), name) } } clean::ImportKind::Glob => { if self.source.path.segments.is_empty() { write!(f, "use *;") } else { - write!(f, "use {}::*;", self.source.print(cache, tcx)) + write!(f, "use {}::*;", self.source.print(cx)) } } }) @@ -1302,20 +1280,19 @@ impl clean::Import { } impl clean::ImportSource { - crate fn print<'b, 'a: 'b, 'tcx: 'a>( + crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, - ) -> impl fmt::Display + 'b + Captures<'tcx> { + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self.did { - Some(did) => resolved_path(f, did, &self.path, true, false, cache, tcx), + Some(did) => resolved_path(f, did, &self.path, true, false, cx), _ => { for seg in &self.path.segments[..self.path.segments.len() - 1] { write!(f, "{}::", seg.name)?; } let name = self.path.last_name(); if let hir::def::Res::PrimTy(p) = self.path.res { - primitive_link(f, PrimitiveType::from(p), &*name, cache)?; + primitive_link(f, PrimitiveType::from(p), &*name, &cx.cache())?; } else { write!(f, "{}", name)?; } @@ -1326,27 +1303,26 @@ impl clean::ImportSource { } impl clean::TypeBinding { - crate fn print<'b, 'a: 'b, 'tcx: 'a>( + crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, - ) -> impl fmt::Display + 'b + Captures<'tcx> { + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| { f.write_str(&*self.name.as_str())?; match self.kind { clean::TypeBindingKind::Equality { ref ty } => { if f.alternate() { - write!(f, " = {:#}", ty.print(cache, tcx))?; + write!(f, " = {:#}", ty.print(cx))?; } else { - write!(f, " = {}", ty.print(cache, tcx))?; + write!(f, " = {}", ty.print(cx))?; } } clean::TypeBindingKind::Constraint { ref bounds } => { if !bounds.is_empty() { if f.alternate() { - write!(f, ": {:#}", print_generic_bounds(bounds, cache, tcx))?; + write!(f, ": {:#}", print_generic_bounds(bounds, cx))?; } else { - write!(f, ": {}", print_generic_bounds(bounds, cache, tcx))?; + write!(f, ": {}", print_generic_bounds(bounds, cx))?; } } } @@ -1371,15 +1347,14 @@ crate fn print_default_space<'a>(v: bool) -> &'a str { } impl clean::GenericArg { - crate fn print<'b, 'a: 'b, 'tcx: 'a>( + crate fn print<'a, 'tcx: 'a>( &'a self, - cache: &'b Cache, - tcx: TyCtxt<'tcx>, - ) -> impl fmt::Display + 'b + Captures<'tcx> { + cx: &'a Context<'tcx>, + ) -> impl fmt::Display + 'a + Captures<'tcx> { display_fn(move |f| match self { clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(<.print(), f), - clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cache, tcx), f), - clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(tcx), f), + clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f), + clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f), }) } } diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index 68d70f27c8c..dc67a63d333 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -113,7 +113,8 @@ crate fn render<T: Print, S: Print>( <section class=\"footer\"></section>\ {after_content}\ <div id=\"rustdoc-vars\" data-root-path=\"{root_path}\" data-current-crate=\"{krate}\" \ - data-search-js=\"{root_path}search-index{suffix}.js\"></div> + data-search-index-js=\"{root_path}search-index{suffix}.js\" \ + data-search-js=\"{static_root_path}search{suffix}.js\"></div> <script src=\"{static_root_path}main{suffix}.js\"></script>\ {extra_scripts}\ </body>\ diff --git a/src/librustdoc/html/mod.rs b/src/librustdoc/html/mod.rs index 4318be898ce..60ebdf5690d 100644 --- a/src/librustdoc/html/mod.rs +++ b/src/librustdoc/html/mod.rs @@ -8,3 +8,6 @@ crate mod render; crate mod sources; crate mod static_files; crate mod toc; + +#[cfg(test)] +mod tests; diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index df5ff6e106d..d866cf4f4cf 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -40,7 +40,7 @@ use crate::html::{layout, sources}; crate struct Context<'tcx> { /// Current hierarchy of components leading down to what's currently being /// rendered - pub(super) current: Vec<String>, + pub(crate) current: Vec<String>, /// The current destination folder of where HTML artifacts should be placed. /// This changes as the context descends into the module hierarchy. pub(super) dst: PathBuf, @@ -144,10 +144,14 @@ impl SharedContext<'_> { } impl<'tcx> Context<'tcx> { - pub(super) fn tcx(&self) -> TyCtxt<'tcx> { + pub(crate) fn tcx(&self) -> TyCtxt<'tcx> { self.shared.tcx } + pub(crate) fn cache(&self) -> &Cache { + &self.cache + } + fn sess(&self) -> &'tcx Session { &self.shared.tcx.sess } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index fbe799e7184..d10b612a737 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -43,7 +43,6 @@ use std::path::PathBuf; use std::str; use std::string::ToString; -use itertools::Itertools; use rustc_ast_pretty::pprust; use rustc_attr::{Deprecation, StabilityLevel}; use rustc_data_structures::fx::FxHashSet; @@ -52,7 +51,6 @@ use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefId; use rustc_hir::Mutability; use rustc_middle::middle::stability; -use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{kw, sym, Symbol}; use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; @@ -62,7 +60,7 @@ use crate::docfs::PathError; use crate::error::Error; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; -use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode}; +use crate::formats::{AssocItemRender, Impl, RenderMode}; use crate::html::escape::Escape; use crate::html::format::{ href, print_abi_with_space, print_default_space, print_generic_bounds, print_where_clause, @@ -486,18 +484,7 @@ fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result<Strin ], ) .into(), - ( - "Auto-hide item declarations", - vec![ - ("auto-hide-struct", "Auto-hide structs declaration", true), - ("auto-hide-enum", "Auto-hide enums declaration", false), - ("auto-hide-union", "Auto-hide unions declaration", true), - ("auto-hide-trait", "Auto-hide traits declaration", true), - ("auto-hide-macro", "Auto-hide macros declaration", false), - ], - ) - .into(), - ("auto-hide-attributes", "Auto-hide item attributes.", true).into(), + ("auto-hide-large-items", "Auto-hide item contents for large items.", true).into(), ("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(), ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", true) .into(), @@ -572,11 +559,10 @@ fn document_short( return; } if let Some(s) = item.doc_value() { - let mut summary_html = MarkdownSummaryLine(&s, &item.links(&cx.cache)).into_string(); + let mut summary_html = MarkdownSummaryLine(&s, &item.links(cx)).into_string(); if s.contains('\n') { - let link = - format!(r#" <a href="{}">Read more</a>"#, naive_assoc_href(item, link, cx.cache())); + let link = format!(r#" <a href="{}">Read more</a>"#, naive_assoc_href(item, link, cx)); if let Some(idx) = summary_html.rfind("</p>") { summary_html.insert_str(idx, &link); @@ -611,7 +597,7 @@ fn document_full( ) { if let Some(s) = cx.shared.maybe_collapsed_doc_value(item) { debug!("Doc block: =====\n{}\n=====", s); - render_markdown(w, cx, &*s, item.links(&cx.cache), prefix, is_hidden); + render_markdown(w, cx, &*s, item.links(cx), prefix, is_hidden); } else if !prefix.is_empty() { if is_hidden { w.write_str("<div class=\"docblock hidden\">"); @@ -797,7 +783,7 @@ fn render_impls( w.write_str(&impls.join("")); } -fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cache: &Cache) -> String { +fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String { use crate::formats::item_type::ItemType::*; let name = it.name.as_ref().unwrap(); @@ -811,7 +797,7 @@ fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cache: &Cache) -> AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id), AssocItemLink::Anchor(None) => anchor, AssocItemLink::GotoSource(did, _) => { - href(did, cache).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor) + href(did, cx).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor) } } } @@ -825,16 +811,14 @@ fn assoc_const( extra: &str, cx: &Context<'_>, ) { - let cache = cx.cache(); - let tcx = cx.tcx(); write!( w, "{}{}const <a href=\"{}\" class=\"constant\"><b>{}</b></a>: {}", extra, - it.visibility.print_with_space(tcx, it.def_id, cache), - naive_assoc_href(it, link, cache), + it.visibility.print_with_space(it.def_id, cx), + naive_assoc_href(it, link, cx), it.name.as_ref().unwrap(), - ty.print(cache, tcx) + ty.print(cx) ); } @@ -845,21 +829,20 @@ fn assoc_type( default: Option<&clean::Type>, link: AssocItemLink<'_>, extra: &str, - cache: &Cache, - tcx: TyCtxt<'_>, + cx: &Context<'_>, ) { write!( w, "{}type <a href=\"{}\" class=\"type\">{}</a>", extra, - naive_assoc_href(it, link, cache), + naive_assoc_href(it, link, cx), it.name.as_ref().unwrap() ); if !bounds.is_empty() { - write!(w, ": {}", print_generic_bounds(bounds, cache, tcx)) + write!(w, ": {}", print_generic_bounds(bounds, cx)) } if let Some(default) = default { - write!(w, " = {}", default.print(cache, tcx)) + write!(w, " = {}", default.print(cx)) } } @@ -909,13 +892,10 @@ fn render_assoc_item( parent: ItemType, cx: &Context<'_>, ) { - let cache = cx.cache(); - let tcx = cx.tcx(); let name = meth.name.as_ref().unwrap(); - let anchor = format!("#{}.{}", meth.type_(), name); let href = match link { AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id), - AssocItemLink::Anchor(None) => anchor, + AssocItemLink::Anchor(None) => format!("#{}.{}", meth.type_(), name), AssocItemLink::GotoSource(did, provided_methods) => { // We're creating a link from an impl-item to the corresponding // trait-item and need to map the anchored type accordingly. @@ -925,17 +905,19 @@ fn render_assoc_item( ItemType::TyMethod }; - href(did, cache).map(|p| format!("{}#{}.{}", p.0, ty, name)).unwrap_or(anchor) + href(did, cx) + .map(|p| format!("{}#{}.{}", p.0, ty, name)) + .unwrap_or_else(|| format!("#{}.{}", ty, name)) } }; - let vis = meth.visibility.print_with_space(tcx, meth.def_id, cache).to_string(); + let vis = meth.visibility.print_with_space(meth.def_id, cx).to_string(); let constness = header.constness.print_with_space(); let asyncness = header.asyncness.print_with_space(); let unsafety = header.unsafety.print_with_space(); let defaultness = print_default_space(meth.is_default()); let abi = print_abi_with_space(header.abi).to_string(); // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`. - let generics_len = format!("{:#}", g.print(cache, tcx)).len(); + let generics_len = format!("{:#}", g.print(cx)).len(); let mut header_len = "fn ".len() + vis.len() + constness.len() @@ -946,19 +928,21 @@ fn render_assoc_item( + name.as_str().len() + generics_len; - let (indent, end_newline) = if parent == ItemType::Trait { + let (indent, indent_str, end_newline) = if parent == ItemType::Trait { header_len += 4; - (4, false) + let indent_str = " "; + render_attributes_in_pre(w, meth, indent_str); + (4, indent_str, false) } else { - (0, true) + render_attributes_in_code(w, meth); + (0, "", true) }; - render_attributes(w, meth, false); w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len()); write!( w, "{}{}{}{}{}{}{}fn <a href=\"{href}\" class=\"fnname\">{name}</a>\ {generics}{decl}{notable_traits}{where_clause}", - if parent == ItemType::Trait { " " } else { "" }, + indent_str, vis, constness, asyncness, @@ -967,10 +951,10 @@ fn render_assoc_item( abi, href = href, name = name, - generics = g.print(cache, tcx), - decl = d.full_print(cache, tcx, header_len, indent, header.asyncness), - notable_traits = notable_traits_decl(&d, cache, tcx), - where_clause = print_where_clause(g, cache, tcx, indent, end_newline), + generics = g.print(cx), + decl = d.full_print(header_len, indent, header.asyncness, cx), + notable_traits = notable_traits_decl(&d, cx), + where_clause = print_where_clause(g, cx, indent, end_newline), ) } match *item.kind { @@ -997,8 +981,7 @@ fn render_assoc_item( default.as_ref(), link, if parent == ItemType::Trait { " " } else { "" }, - cx.cache(), - cx.tcx(), + cx, ), _ => panic!("render_assoc_item called on non-associated-item"), } @@ -1014,35 +997,33 @@ const ALLOWED_ATTRIBUTES: &[Symbol] = &[ sym::non_exhaustive, ]; -// The `top` parameter is used when generating the item declaration to ensure it doesn't have a -// left padding. For example: -// -// #[foo] <----- "top" attribute -// struct Foo { -// #[bar] <---- not "top" attribute -// bar: usize, -// } -fn render_attributes(w: &mut Buffer, it: &clean::Item, top: bool) { - let attrs = it - .attrs +fn attributes(it: &clean::Item) -> Vec<String> { + it.attrs .other_attrs .iter() .filter_map(|attr| { if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) { - Some(pprust::attribute_to_string(&attr)) + Some(pprust::attribute_to_string(&attr).replace("\n", "").replace(" ", " ")) } else { None } }) - .join("\n"); + .collect() +} - if !attrs.is_empty() { - write!( - w, - "<span class=\"docblock attributes{}\">{}</span>", - if top { " top-attr" } else { "" }, - &attrs - ); +// When an attribute is rendered inside a `<pre>` tag, it is formatted using +// a whitespace prefix and newline. +fn render_attributes_in_pre(w: &mut Buffer, it: &clean::Item, prefix: &str) { + for a in attributes(it) { + write!(w, "{}{}\n", prefix, a); + } +} + +// When an attribute is rendered inside a <code> tag, it is formatted using +// a div to produce a newline after it. +fn render_attributes_in_code(w: &mut Buffer, it: &clean::Item) { + for a in attributes(it) { + write!(w, "<div class=\"code-attribute\">{}</div>", a); } } @@ -1087,11 +1068,9 @@ fn render_assoc_items( RenderMode::Normal } AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { - let id = cx.derive_id(small_url_encode(format!( - "deref-methods-{:#}", - type_.print(cache, tcx) - ))); - debug!("Adding {} to deref id map", type_.print(cache, tcx)); + let id = + cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx)))); + debug!("Adding {} to deref id map", type_.print(cx)); cx.deref_id_map.borrow_mut().insert(type_.def_id_full(cache).unwrap(), id.clone()); write!( w, @@ -1100,8 +1079,8 @@ fn render_assoc_items( <a href=\"#{id}\" class=\"anchor\"></a>\ </h2>", id = id, - trait_ = trait_.print(cache, tcx), - type_ = type_.print(cache, tcx), + trait_ = trait_.print(cx), + type_ = type_.print(cx), ); RenderMode::ForDeref { mut_: deref_mut_ } } @@ -1253,36 +1232,34 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool, cache: &Cache) -> bo } } -fn notable_traits_decl(decl: &clean::FnDecl, cache: &Cache, tcx: TyCtxt<'_>) -> String { +fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String { let mut out = Buffer::html(); let mut trait_ = String::new(); - if let Some(did) = decl.output.def_id_full(cache) { - if let Some(impls) = cache.impls.get(&did) { + if let Some(did) = decl.output.def_id_full(cx.cache()) { + if let Some(impls) = cx.cache().impls.get(&did) { for i in impls { let impl_ = i.inner_impl(); - if impl_ - .trait_ - .def_id() - .map_or(false, |d| cache.traits.get(&d).map(|t| t.is_notable).unwrap_or(false)) - { + if impl_.trait_.def_id().map_or(false, |d| { + cx.cache().traits.get(&d).map(|t| t.is_notable).unwrap_or(false) + }) { if out.is_empty() { write!( &mut out, "<h3 class=\"notable\">Notable traits for {}</h3>\ <code class=\"content\">", - impl_.for_.print(cache, tcx) + impl_.for_.print(cx) ); - trait_.push_str(&impl_.for_.print(cache, tcx).to_string()); + trait_.push_str(&impl_.for_.print(cx).to_string()); } //use the "where" class here to make it small write!( &mut out, "<span class=\"where fmt-newline\">{}</span>", - impl_.print(cache, false, tcx) + impl_.print(false, cx) ); - let t_did = impl_.trait_.def_id_full(cache).unwrap(); + let t_did = impl_.trait_.def_id_full(cx.cache()).unwrap(); for it in &impl_.items { if let clean::TypedefItem(ref tydef, _) = *it.kind { out.push_str("<span class=\"where fmt-newline\"> "); @@ -1293,8 +1270,7 @@ fn notable_traits_decl(decl: &clean::FnDecl, cache: &Cache, tcx: TyCtxt<'_>) -> Some(&tydef.type_), AssocItemLink::GotoSource(t_did, &FxHashSet::default()), "", - cache, - tcx, + cx, ); out.push_str(";</span>"); } @@ -1333,18 +1309,18 @@ fn render_impl( // in documentation pages for trait with automatic implementations like "Send" and "Sync". aliases: &[String], ) { - let traits = &cx.cache.traits; let tcx = cx.tcx(); let cache = cx.cache(); + let traits = &cache.traits; let trait_ = i.trait_did_full(cache).map(|did| &traits[&did]); if render_mode == RenderMode::Normal { let id = cx.derive_id(match i.inner_impl().trait_ { Some(ref t) => { if is_on_foreign_type { - get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cache, tcx) + get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cx) } else { - format!("impl-{}", small_url_encode(format!("{:#}", t.print(cache, tcx)))) + format!("impl-{}", small_url_encode(format!("{:#}", t.print(cx)))) } } None => "impl".to_string(), @@ -1356,7 +1332,7 @@ fn render_impl( }; if let Some(use_absolute) = use_absolute { write!(w, "<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">", id, aliases); - write!(w, "{}", i.inner_impl().print(cache, use_absolute, tcx)); + write!(w, "{}", i.inner_impl().print(use_absolute, cx)); if show_def_docs { for it in &i.inner_impl().items { if let clean::TypedefItem(ref tydef, _) = *it.kind { @@ -1368,8 +1344,7 @@ fn render_impl( Some(&tydef.type_), AssocItemLink::Anchor(None), "", - cache, - tcx, + cx, ); w.write_str(";</span>"); } @@ -1382,7 +1357,7 @@ fn render_impl( "<h3 id=\"{}\" class=\"impl\"{}><code class=\"in-band\">{}</code>", id, aliases, - i.inner_impl().print(cache, false, tcx) + i.inner_impl().print(false, cx) ); } write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id); @@ -1409,7 +1384,7 @@ fn render_impl( "<div class=\"docblock\">{}</div>", Markdown( &*dox, - &i.impl_item.links(&cx.cache), + &i.impl_item.links(cx), &mut ids, cx.shared.codes, cx.shared.edition, @@ -1452,14 +1427,32 @@ fn render_impl( } else { (true, " hidden") }; + let in_trait_class = if trait_.is_some() { " trait-impl" } else { "" }; match *item.kind { clean::MethodItem(..) | clean::TyMethodItem(_) => { // Only render when the method is not static or we allow static methods if render_method_item { let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "<h4 id=\"{}\" class=\"{}{}\">", id, item_type, extra_class); + let source_id = trait_ + .and_then(|trait_| { + trait_.items.iter().find(|item| { + item.name.map(|n| n.as_str().eq(&name.as_str())).unwrap_or(false) + }) + }) + .map(|item| format!("{}.{}", item.type_(), name)); + write!( + w, + "<h4 id=\"{}\" class=\"{}{}{}\">", + id, item_type, extra_class, in_trait_class, + ); w.write_str("<code>"); - render_assoc_item(w, item, link.anchor(&id), ItemType::Impl, cx); + render_assoc_item( + w, + item, + link.anchor(source_id.as_ref().unwrap_or(&id)), + ItemType::Impl, + cx, + ); w.write_str("</code>"); render_stability_since_raw( w, @@ -1468,29 +1461,49 @@ fn render_impl( outer_version, outer_const_version, ); + write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id); write_srclink(cx, item, w); w.write_str("</h4>"); } } clean::TypedefItem(ref tydef, _) => { - let id = cx.derive_id(format!("{}.{}", ItemType::AssocType, name)); - write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, extra_class); + let source_id = format!("{}.{}", ItemType::AssocType, name); + let id = cx.derive_id(source_id.clone()); + write!( + w, + "<h4 id=\"{}\" class=\"{}{}{}\"><code>", + id, item_type, extra_class, in_trait_class + ); assoc_type( w, item, &Vec::new(), Some(&tydef.type_), - link.anchor(&id), + link.anchor(if trait_.is_some() { &source_id } else { &id }), "", - cx.cache(), - tcx, + cx, ); - w.write_str("</code></h4>"); + w.write_str("</code>"); + write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id); + w.write_str("</h4>"); } clean::AssocConstItem(ref ty, ref default) => { - let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, extra_class); - assoc_const(w, item, ty, default.as_ref(), link.anchor(&id), "", cx); + let source_id = format!("{}.{}", item_type, name); + let id = cx.derive_id(source_id.clone()); + write!( + w, + "<h4 id=\"{}\" class=\"{}{}{}\"><code>", + id, item_type, extra_class, in_trait_class + ); + assoc_const( + w, + item, + ty, + default.as_ref(), + link.anchor(if trait_.is_some() { &source_id } else { &id }), + "", + cx, + ); w.write_str("</code>"); render_stability_since_raw( w, @@ -1499,23 +1512,30 @@ fn render_impl( outer_version, outer_const_version, ); + write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id); write_srclink(cx, item, w); w.write_str("</h4>"); } clean::AssocTypeItem(ref bounds, ref default) => { - let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "<h4 id=\"{}\" class=\"{}{}\"><code>", id, item_type, extra_class); + let source_id = format!("{}.{}", item_type, name); + let id = cx.derive_id(source_id.clone()); + write!( + w, + "<h4 id=\"{}\" class=\"{}{}{}\"><code>", + id, item_type, extra_class, in_trait_class + ); assoc_type( w, item, bounds, default.as_ref(), - link.anchor(&id), + link.anchor(if trait_.is_some() { &source_id } else { &id }), "", - cx.cache(), - tcx, + cx, ); - w.write_str("</code></h4>"); + w.write_str("</code>"); + write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id); + w.write_str("</h4>"); } clean::StrippedItem(..) => return, _ => panic!("can't make docs for trait item with name {:?}", item.name), @@ -1605,7 +1625,7 @@ fn render_impl( true, outer_version, outer_const_version, - None, + Some(t), show_def_docs, ); } @@ -1817,7 +1837,6 @@ fn small_url_encode(s: String) -> String { fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { if let Some(v) = cx.cache.impls.get(&it.def_id) { let mut used_links = FxHashSet::default(); - let tcx = cx.tcx(); let cache = cx.cache(); { @@ -1852,9 +1871,9 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { .iter() .filter_map(|it| { if let Some(ref i) = it.inner_impl().trait_ { - let i_display = format!("{:#}", i.print(cache, tcx)); + let i_display = format!("{:#}", i.print(cx)); let out = Escape(&i_display); - let encoded = small_url_encode(format!("{:#}", i.print(cache, tcx))); + let encoded = small_url_encode(format!("{:#}", i.print(cx))); let generated = format!( "<a href=\"#impl-{}\">{}{}</a>", encoded, @@ -1926,7 +1945,6 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &Vec<Impl>) { let c = cx.cache(); - let tcx = cx.tcx(); debug!("found Deref: {:?}", impl_); if let Some((target, real_target)) = @@ -1975,11 +1993,8 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &V out, "<a class=\"sidebar-title\" href=\"#{}\">Methods from {}<Target={}></a>", id, - Escape(&format!( - "{:#}", - impl_.inner_impl().trait_.as_ref().unwrap().print(c, tcx) - )), - Escape(&format!("{:#}", real_target.print(c, tcx))), + Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))), + Escape(&format!("{:#}", real_target.print(cx))), ); // We want links' order to be reproducible so we don't use unstable sort. ret.sort(); @@ -2035,27 +2050,20 @@ fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clea fn get_id_for_impl_on_foreign_type( for_: &clean::Type, trait_: &clean::Type, - cache: &Cache, - tcx: TyCtxt<'_>, + cx: &Context<'_>, ) -> String { - small_url_encode(format!( - "impl-{:#}-for-{:#}", - trait_.print(cache, tcx), - for_.print(cache, tcx) - )) + small_url_encode(format!("impl-{:#}-for-{:#}", trait_.print(cx), for_.print(cx),)) } -fn extract_for_impl_name( - item: &clean::Item, - cache: &Cache, - tcx: TyCtxt<'_>, -) -> Option<(String, String)> { +fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> { match *item.kind { clean::ItemKind::ImplItem(ref i) => { if let Some(ref trait_) = i.trait_ { + // Alternative format produces no URLs, + // so this parameter does nothing. Some(( - format!("{:#}", i.for_.print(cache, tcx)), - get_id_for_impl_on_foreign_type(&i.for_, trait_, cache, tcx), + format!("{:#}", i.for_.print(cx)), + get_id_for_impl_on_foreign_type(&i.for_, trait_, cx), )) } else { None @@ -2136,7 +2144,6 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean if let Some(implementors) = cx.cache.implementors.get(&it.def_id) { let cache = cx.cache(); - let tcx = cx.tcx(); let mut res = implementors .iter() .filter(|i| { @@ -2145,7 +2152,7 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean .def_id_full(cache) .map_or(false, |d| !cx.cache.paths.contains_key(&d)) }) - .filter_map(|i| extract_for_impl_name(&i.impl_item, cache, tcx)) + .filter_map(|i| extract_for_impl_name(&i.impl_item, cx)) .collect::<Vec<_>>(); if !res.is_empty() { diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 0cdfe435b9c..42b79503017 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -11,13 +11,12 @@ use rustc_span::symbol::{kw, sym, Symbol}; use super::{ collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_strs, notable_traits_decl, - render_assoc_item, render_assoc_items, render_attributes, render_impl, - render_stability_since_raw, write_srclink, AssocItemLink, Context, + render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre, + render_impl, render_stability_since_raw, write_srclink, AssocItemLink, Context, }; use crate::clean::{self, GetDefId}; -use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; -use crate::formats::{AssocItemRender, FormatRenderer, Impl, RenderMode}; +use crate::formats::{AssocItemRender, Impl, RenderMode}; use crate::html::escape::Escape; use crate::html::format::{print_abi_with_space, print_where_clause, Buffer, PrintWithSpace}; use crate::html::highlight; @@ -131,6 +130,26 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) } } +/// For large structs, enums, unions, etc, determine whether to hide their fields +fn should_hide_fields(n_fields: usize) -> bool { + n_fields > 12 +} + +fn toggle_open(w: &mut Buffer, text: &str) { + write!( + w, + "<details class=\"rustdoc-toggle type-contents-toggle\">\ + <summary class=\"hideme\">\ + <span>Show {}</span>\ + </summary>", + text + ); +} + +fn toggle_close(w: &mut Buffer) { + w.write_str("</details>"); +} + fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) { document(w, cx, item, None); @@ -248,15 +267,15 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl Some(ref src) => write!( w, "<tr><td><code>{}extern crate {} as {};", - myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()), - anchor(myitem.def_id, &*src.as_str(), cx.cache()), + myitem.visibility.print_with_space(myitem.def_id, cx), + anchor(myitem.def_id, &*src.as_str(), cx), myitem.name.as_ref().unwrap(), ), None => write!( w, "<tr><td><code>{}extern crate {};", - myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()), - anchor(myitem.def_id, &*myitem.name.as_ref().unwrap().as_str(), cx.cache()), + myitem.visibility.print_with_space(myitem.def_id, cx), + anchor(myitem.def_id, &*myitem.name.as_ref().unwrap().as_str(), cx), ), } w.write_str("</code></td></tr>"); @@ -266,8 +285,8 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl write!( w, "<tr><td><code>{}{}</code></td></tr>", - myitem.visibility.print_with_space(cx.tcx(), myitem.def_id, cx.cache()), - import.print(cx.cache(), cx.tcx()), + myitem.visibility.print_with_space(myitem.def_id, cx), + import.print(cx), ); } @@ -298,7 +317,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl </tr>", name = *myitem.name.as_ref().unwrap(), stab_tags = extra_info_tags(myitem, item, cx.tcx()), - docs = MarkdownSummaryLine(&doc_value, &myitem.links(&cx.cache)).into_string(), + docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string(), class = myitem.type_(), add = add, stab = stab.unwrap_or_else(String::new), @@ -367,37 +386,37 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) { let header_len = format!( "{}{}{}{}{:#}fn {}{:#}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.visibility.print_with_space(it.def_id, cx), f.header.constness.print_with_space(), f.header.asyncness.print_with_space(), f.header.unsafety.print_with_space(), print_abi_with_space(f.header.abi), it.name.as_ref().unwrap(), - f.generics.print(cx.cache(), cx.tcx()) + f.generics.print(cx), ) .len(); w.write_str("<pre class=\"rust fn\">"); - render_attributes(w, it, false); + render_attributes_in_pre(w, it, ""); write!( w, "{vis}{constness}{asyncness}{unsafety}{abi}fn \ {name}{generics}{decl}{notable_traits}{where_clause}</pre>", - vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + vis = it.visibility.print_with_space(it.def_id, cx), constness = f.header.constness.print_with_space(), asyncness = f.header.asyncness.print_with_space(), unsafety = f.header.unsafety.print_with_space(), abi = print_abi_with_space(f.header.abi), name = it.name.as_ref().unwrap(), - generics = f.generics.print(cx.cache(), cx.tcx()), - where_clause = print_where_clause(&f.generics, cx.cache(), cx.tcx(), 0, true), - decl = f.decl.full_print(cx.cache(), cx.tcx(), header_len, 0, f.header.asyncness), - notable_traits = notable_traits_decl(&f.decl, cx.cache(), cx.tcx()), + generics = f.generics.print(cx), + where_clause = print_where_clause(&f.generics, cx, 0, true), + decl = f.decl.full_print(header_len, 0, f.header.asyncness, cx), + notable_traits = notable_traits_decl(&f.decl, cx), ); document(w, cx, it, None) } fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) { - let bounds = bounds(&t.bounds, false, cx.cache(), cx.tcx()); + let bounds = bounds(&t.bounds, false, cx); let types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>(); let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>(); let required = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>(); @@ -406,20 +425,20 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra // Output the trait definition wrap_into_docblock(w, |w| { w.write_str("<pre class=\"rust trait\">"); - render_attributes(w, it, true); + render_attributes_in_pre(w, it, ""); write!( w, "{}{}{}trait {}{}{}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.visibility.print_with_space(it.def_id, cx), t.unsafety.print_with_space(), if t.is_auto { "auto " } else { "" }, it.name.as_ref().unwrap(), - t.generics.print(cx.cache(), cx.tcx()), + t.generics.print(cx), bounds ); if !t.generics.where_predicates.is_empty() { - write!(w, "{}", print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true)); + write!(w, "{}", print_where_clause(&t.generics, cx, 0, true)); } else { w.write_str(" "); } @@ -429,10 +448,25 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra } else { // FIXME: we should be using a derived_id for the Anchors here w.write_str("{\n"); + let mut toggle = false; + + // If there are too many associated types, hide _everything_ + if should_hide_fields(types.len()) { + toggle = true; + toggle_open(w, "associated items"); + } for t in &types { render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx); w.write_str(";\n"); } + // If there are too many associated constants, hide everything after them + // We also do this if the types + consts is large because otherwise we could + // render a bunch of types and _then_ a bunch of consts just because both were + // _just_ under the limit + if !toggle && should_hide_fields(types.len() + consts.len()) { + toggle = true; + toggle_open(w, "associated constants and methods"); + } if !types.is_empty() && !consts.is_empty() { w.write_str("\n"); } @@ -440,6 +474,10 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx); w.write_str(";\n"); } + if !toggle && should_hide_fields(required.len() + provided.len()) { + toggle = true; + toggle_open(w, "methods"); + } if !consts.is_empty() && !required.is_empty() { w.write_str("\n"); } @@ -470,6 +508,9 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra w.write_str("<div class=\"item-spacer\"></div>"); } } + if toggle { + toggle_close(w); + } w.write_str("}"); } w.write_str("</pre>") @@ -592,8 +633,8 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) = local.iter().partition(|i| i.inner_impl().synthetic); - synthetic.sort_by(|a, b| compare_impl(a, b, cx.cache(), cx.tcx())); - concrete.sort_by(|a, b| compare_impl(a, b, cx.cache(), cx.tcx())); + synthetic.sort_by(|a, b| compare_impl(a, b, cx)); + concrete.sort_by(|a, b| compare_impl(a, b, cx)); if !foreign.is_empty() { write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", ""); @@ -693,14 +734,14 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { w.write_str("<pre class=\"rust trait-alias\">"); - render_attributes(w, it, false); + render_attributes_in_pre(w, it, ""); write!( w, "trait {}{}{} = {};</pre>", it.name.as_ref().unwrap(), - t.generics.print(cx.cache(), cx.tcx()), - print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true), - bounds(&t.bounds, true, cx.cache(), cx.tcx()) + t.generics.print(cx), + print_where_clause(&t.generics, cx, 0, true), + bounds(&t.bounds, true, cx) ); document(w, cx, it, None); @@ -714,14 +755,14 @@ fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clea fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { w.write_str("<pre class=\"rust opaque\">"); - render_attributes(w, it, false); + render_attributes_in_pre(w, it, ""); write!( w, "type {}{}{where_clause} = impl {bounds};</pre>", it.name.as_ref().unwrap(), - t.generics.print(cx.cache(), cx.tcx()), - where_clause = print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true), - bounds = bounds(&t.bounds, false, cx.cache(), cx.tcx()), + t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, true), + bounds = bounds(&t.bounds, false, cx), ); document(w, cx, it, None); @@ -735,14 +776,14 @@ fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean: fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) { w.write_str("<pre class=\"rust typedef\">"); - render_attributes(w, it, false); + render_attributes_in_pre(w, it, ""); write!( w, "type {}{}{where_clause} = {type_};</pre>", it.name.as_ref().unwrap(), - t.generics.print(cx.cache(), cx.tcx()), - where_clause = print_where_clause(&t.generics, cx.cache(), cx.tcx(), 0, true), - type_ = t.type_.print(cx.cache(), cx.tcx()), + t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, true), + type_ = t.type_.print(cx), ); document(w, cx, it, None); @@ -757,7 +798,7 @@ fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::T fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) { wrap_into_docblock(w, |w| { w.write_str("<pre class=\"rust union\">"); - render_attributes(w, it, true); + render_attributes_in_pre(w, it, ""); render_union(w, it, Some(&s.generics), &s.fields, "", true, cx); w.write_str("</pre>") }); @@ -789,7 +830,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni id = id, name = name, shortty = ItemType::StructField, - ty = ty.print(cx.cache(), cx.tcx()), + ty = ty.print(cx), ); if let Some(stability_class) = field.stability_class(cx.tcx()) { write!(w, "<span class=\"stab {stab}\"></span>", stab = stability_class); @@ -803,19 +844,23 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) { wrap_into_docblock(w, |w| { w.write_str("<pre class=\"rust enum\">"); - render_attributes(w, it, true); + render_attributes_in_pre(w, it, ""); write!( w, "{}enum {}{}{}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.visibility.print_with_space(it.def_id, cx), it.name.as_ref().unwrap(), - e.generics.print(cx.cache(), cx.tcx()), - print_where_clause(&e.generics, cx.cache(), cx.tcx(), 0, true), + e.generics.print(cx), + print_where_clause(&e.generics, cx, 0, true), ); if e.variants.is_empty() && !e.variants_stripped { w.write_str(" {}"); } else { w.write_str(" {\n"); + let toggle = should_hide_fields(e.variants.len()); + if toggle { + toggle_open(w, "variants"); + } for v in &e.variants { w.write_str(" "); let name = v.name.as_ref().unwrap(); @@ -828,7 +873,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum if i > 0 { w.write_str(", ") } - write!(w, "{}", ty.print(cx.cache(), cx.tcx())); + write!(w, "{}", ty.print(cx)); } w.write_str(")"); } @@ -844,6 +889,9 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum if e.variants_stripped { w.write_str(" // some variants omitted\n"); } + if toggle { + toggle_close(w); + } w.write_str("}"); } w.write_str("</pre>") @@ -875,7 +923,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum if i > 0 { w.write_str(", "); } - write!(w, "{}", ty.print(cx.cache(), cx.tcx())); + write!(w, "{}", ty.print(cx)); } w.write_str(")"); } @@ -912,7 +960,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum </span>", id = id, f = field.name.as_ref().unwrap(), - t = ty.print(cx.cache(), cx.tcx()) + t = ty.print(cx) ); document(w, cx, field, Some(variant)); } @@ -976,14 +1024,14 @@ fn item_primitive(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) { w.write_str("<pre class=\"rust const\">"); - render_attributes(w, it, false); + render_attributes_in_code(w, it); write!( w, "{vis}const {name}: {typ}", - vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + vis = it.visibility.print_with_space(it.def_id, cx), name = it.name.as_ref().unwrap(), - typ = c.type_.print(cx.cache(), cx.tcx()), + typ = c.type_.print(cx), ); let value = c.value(cx.tcx()); @@ -1015,7 +1063,7 @@ fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean:: fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) { wrap_into_docblock(w, |w| { w.write_str("<pre class=\"rust struct\">"); - render_attributes(w, it, true); + render_attributes_in_code(w, it); render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx); w.write_str("</pre>") }); @@ -1053,7 +1101,7 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St item_type = ItemType::StructField, id = id, name = field.name.as_ref().unwrap(), - ty = ty.print(cx.cache(), cx.tcx()) + ty = ty.print(cx) ); document(w, cx, field, Some(it)); } @@ -1064,25 +1112,25 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) { w.write_str("<pre class=\"rust static\">"); - render_attributes(w, it, false); + render_attributes_in_code(w, it); write!( w, "{vis}static {mutability}{name}: {typ}</pre>", - vis = it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + vis = it.visibility.print_with_space(it.def_id, cx), mutability = s.mutability.print_with_space(), name = it.name.as_ref().unwrap(), - typ = s.type_.print(cx.cache(), cx.tcx()) + typ = s.type_.print(cx) ); document(w, cx, it, None) } fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { w.write_str("<pre class=\"rust foreigntype\">extern {\n"); - render_attributes(w, it, false); + render_attributes_in_code(w, it); write!( w, " {}type {};\n}}</pre>", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.visibility.print_with_space(it.def_id, cx), it.name.as_ref().unwrap(), ); @@ -1146,12 +1194,7 @@ pub(super) fn item_path(ty: ItemType, name: &str) -> String { } } -fn bounds( - t_bounds: &[clean::GenericBound], - trait_alias: bool, - cache: &Cache, - tcx: TyCtxt<'_>, -) -> String { +fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) -> String { let mut bounds = String::new(); if !t_bounds.is_empty() { if !trait_alias { @@ -1161,7 +1204,7 @@ fn bounds( if i > 0 { bounds.push_str(" + "); } - bounds.push_str(&p.print(cache, tcx).to_string()); + bounds.push_str(&p.print(cx).to_string()); } } bounds @@ -1171,7 +1214,7 @@ fn wrap_into_docblock<F>(w: &mut Buffer, f: F) where F: FnOnce(&mut Buffer), { - w.write_str("<div class=\"docblock type-decl hidden-by-usual-hider\">"); + w.write_str("<div class=\"docblock type-decl\">"); f(w); w.write_str("</div>") } @@ -1191,17 +1234,12 @@ fn render_stability_since( ) } -fn compare_impl<'a, 'b>( - lhs: &'a &&Impl, - rhs: &'b &&Impl, - cache: &Cache, - tcx: TyCtxt<'_>, -) -> Ordering { - let lhs = format!("{}", lhs.inner_impl().print(cache, false, tcx)); - let rhs = format!("{}", rhs.inner_impl().print(cache, false, tcx)); +fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering { + let lhss = format!("{}", lhs.inner_impl().print(false, cx)); + let rhss = format!("{}", rhs.inner_impl().print(false, cx)); // lhs and rhs are formatted as HTML, which may be unnecessary - compare_names(&lhs, &rhs) + compare_names(&lhss, &rhss) } fn render_implementor( @@ -1251,24 +1289,31 @@ fn render_union( write!( w, "{}{}{}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.visibility.print_with_space(it.def_id, cx), if structhead { "union " } else { "" }, it.name.as_ref().unwrap() ); if let Some(g) = g { - write!(w, "{}", g.print(cx.cache(), cx.tcx())); - write!(w, "{}", print_where_clause(&g, cx.cache(), cx.tcx(), 0, true)); + write!(w, "{}", g.print(cx)); + write!(w, "{}", print_where_clause(&g, cx, 0, true)); } write!(w, " {{\n{}", tab); + let count_fields = + fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count(); + let toggle = should_hide_fields(count_fields); + if toggle { + toggle_open(w, "fields"); + } + for field in fields { if let clean::StructFieldItem(ref ty) = *field.kind { write!( w, " {}{}: {},\n{}", - field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()), + field.visibility.print_with_space(field.def_id, cx), field.name.as_ref().unwrap(), - ty.print(cx.cache(), cx.tcx()), + ty.print(cx), tab ); } @@ -1277,6 +1322,9 @@ fn render_union( if it.has_stripped_fields().unwrap() { write!(w, " // some fields omitted\n{}", tab); } + if toggle { + toggle_close(w); + } w.write_str("}"); } @@ -1293,31 +1341,36 @@ fn render_struct( write!( w, "{}{}{}", - it.visibility.print_with_space(cx.tcx(), it.def_id, cx.cache()), + it.visibility.print_with_space(it.def_id, cx), if structhead { "struct " } else { "" }, it.name.as_ref().unwrap() ); if let Some(g) = g { - write!(w, "{}", g.print(cx.cache(), cx.tcx())) + write!(w, "{}", g.print(cx)) } match ty { CtorKind::Fictive => { if let Some(g) = g { - write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, true),) + write!(w, "{}", print_where_clause(g, cx, 0, true),) } - let mut has_visible_fields = false; w.write_str(" {"); + let count_fields = + fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count(); + let has_visible_fields = count_fields > 0; + let toggle = should_hide_fields(count_fields); + if toggle { + toggle_open(w, "fields"); + } for field in fields { if let clean::StructFieldItem(ref ty) = *field.kind { write!( w, "\n{} {}{}: {},", tab, - field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()), + field.visibility.print_with_space(field.def_id, cx), field.name.as_ref().unwrap(), - ty.print(cx.cache(), cx.tcx()), + ty.print(cx), ); - has_visible_fields = true; } } @@ -1331,6 +1384,9 @@ fn render_struct( // `{ /* fields omitted */ }` to save space. write!(w, " /* fields omitted */ "); } + if toggle { + toggle_close(w); + } w.write_str("}"); } CtorKind::Fn => { @@ -1345,8 +1401,8 @@ fn render_struct( write!( w, "{}{}", - field.visibility.print_with_space(cx.tcx(), field.def_id, cx.cache()), - ty.print(cx.cache(), cx.tcx()), + field.visibility.print_with_space(field.def_id, cx), + ty.print(cx), ) } _ => unreachable!(), @@ -1354,14 +1410,14 @@ fn render_struct( } w.write_str(")"); if let Some(g) = g { - write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, false),) + write!(w, "{}", print_where_clause(g, cx, 0, false),) } w.write_str(";"); } CtorKind::Const => { // Needed for PhantomData. if let Some(g) = g { - write!(w, "{}", print_where_clause(g, cx.cache(), cx.tcx(), 0, false),) + write!(w, "{}", print_where_clause(g, cx, 0, false),) } w.write_str(";"); } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 8fb6d68f3c6..78bcd40af75 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -16,7 +16,6 @@ use crate::clean::Crate; use crate::config::{EmitType, RenderOptions}; use crate::docfs::PathError; use crate::error::Error; -use crate::formats::FormatRenderer; use crate::html::{layout, static_files}; crate static FILES_UNVERSIONED: Lazy<FxHashMap<&str, &[u8]>> = Lazy::new(|| { @@ -223,6 +222,7 @@ pub(super) fn write_shared( &format!(" = {}", serde_json::to_string(&themes).unwrap()), ), )?; + write_minify("search.js", static_files::SEARCH_JS)?; write_minify("settings.js", static_files::SETTINGS_JS)?; if cx.shared.include_sources { write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT)?; @@ -410,7 +410,7 @@ pub(super) fn write_shared( write_crate("search-index.js", &|| { let mut v = String::from("var searchIndex = JSON.parse('{\\\n"); v.push_str(&all_indexes.join(",\\\n")); - v.push_str("\\\n}');\ninitSearch(searchIndex);"); + v.push_str("\\\n}');\nif (window.initSearch) {window.initSearch(searchIndex)};"); Ok(v.into_bytes()) })?; @@ -500,7 +500,7 @@ pub(super) fn write_shared( None } else { Some(Implementor { - text: imp.inner_impl().print(cx.cache(), false, cx.tcx()).to_string(), + text: imp.inner_impl().print(false, cx).to_string(), synthetic: imp.inner_impl().synthetic, types: collect_paths_for_type(imp.inner_impl().for_.clone(), cx.cache()), }) diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 80dc6b923f6..f017fd846b1 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -1,4 +1,3 @@ -// ignore-tidy-filelength // Local js definitions: /* global addClass, getSettingValue, hasClass */ /* global onEach, onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */ @@ -44,6 +43,7 @@ if (!DOMTokenList.prototype.remove) { window.rootPath = rustdocVars.attributes["data-root-path"].value; window.currentCrate = rustdocVars.attributes["data-current-crate"].value; window.searchJS = rustdocVars.attributes["data-search-js"].value; + window.searchIndexJS = rustdocVars.attributes["data-search-index-js"].value; } var sidebarVars = document.getElementById("sidebar-vars"); if (sidebarVars) { @@ -77,14 +77,6 @@ function getVirtualKey(ev) { return String.fromCharCode(c); } -function getSearchInput() { - return document.getElementsByClassName("search-input")[0]; -} - -function getSearchElement() { - return document.getElementById("search"); -} - var THEME_PICKER_ELEMENT_ID = "theme-picker"; var THEMES_ELEMENT_ID = "theme-choices"; @@ -101,16 +93,6 @@ function getNakedUrl() { return window.location.href.split("?")[0].split("#")[0]; } -// Sets the focus on the search bar at the top of the page -function focusSearchBar() { - getSearchInput().focus(); -} - -// Removes the focus from the search bar. -function defocusSearchBar() { - getSearchInput().blur(); -} - function showThemeButtonState() { var themePicker = getThemePickerElement(); var themeChoices = getThemesElement(); @@ -173,67 +155,154 @@ function hideThemeButtonState() { (function() { "use strict"; - // This mapping table should match the discriminants of - // `rustdoc::html::item_type::ItemType` type in Rust. - var itemTypes = ["mod", - "externcrate", - "import", - "struct", - "enum", - "fn", - "type", - "static", - "trait", - "impl", - "tymethod", - "method", - "structfield", - "variant", - "macro", - "primitive", - "associatedtype", - "constant", - "associatedconstant", - "union", - "foreigntype", - "keyword", - "existential", - "attr", - "derive", - "traitalias"]; + window.searchState = { + loadingText: "Loading search results...", + input: document.getElementsByClassName("search-input")[0], + outputElement: function() { + return document.getElementById("search"); + }, + title: null, + titleBeforeSearch: document.title, + timeout: null, + // On the search screen, so you remain on the last tab you opened. + // + // 0 for "In Names" + // 1 for "In Parameters" + // 2 for "In Return Types" + currentTab: 0, + mouseMovedAfterSearch: true, + clearInputTimeout: function() { + if (searchState.timeout !== null) { + clearTimeout(searchState.timeout); + searchState.timeout = null; + } + }, + // Sets the focus on the search bar at the top of the page + focus: function() { + searchState.input.focus(); + }, + // Removes the focus from the search bar. + defocus: function() { + searchState.input.blur(); + }, + showResults: function(search) { + if (search === null || typeof search === 'undefined') { + search = searchState.outputElement(); + } + addClass(main, "hidden"); + removeClass(search, "hidden"); + searchState.mouseMovedAfterSearch = false; + document.title = searchState.title; + }, + hideResults: function(search) { + if (search === null || typeof search === 'undefined') { + search = searchState.outputElement(); + } + addClass(search, "hidden"); + removeClass(main, "hidden"); + document.title = searchState.titleBeforeSearch; + // We also remove the query parameter from the URL. + if (searchState.browserSupportsHistoryApi()) { + history.replaceState("", window.currentCrate + " - Rust", + getNakedUrl() + window.location.hash); + } + }, + getQueryStringParams: function() { + var params = {}; + window.location.search.substring(1).split("&"). + map(function(s) { + var pair = s.split("="); + params[decodeURIComponent(pair[0])] = + typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]); + }); + return params; + }, + putBackSearch: function(search_input) { + var search = searchState.outputElement(); + if (search_input.value !== "" && hasClass(search, "hidden")) { + searchState.showResults(search); + if (searchState.browserSupportsHistoryApi()) { + var extra = "?search=" + encodeURIComponent(search_input.value); + history.replaceState(search_input.value, "", + getNakedUrl() + extra + window.location.hash); + } + document.title = searchState.title; + } + }, + browserSupportsHistoryApi: function() { + return window.history && typeof window.history.pushState === "function"; + }, + setup: function() { + var search_input = searchState.input; + if (!searchState.input) { + return; + } + function loadScript(url) { + var script = document.createElement('script'); + script.src = url; + document.head.append(script); + } - var disableShortcuts = getSettingValue("disable-shortcuts") === "true"; - var search_input = getSearchInput(); - var searchTimeout = null; - var toggleAllDocsId = "toggle-all-docs"; + var searchLoaded = false; + function loadSearch() { + if (!searchLoaded) { + searchLoaded = true; + loadScript(window.searchJS); + loadScript(window.searchIndexJS); + } + } - // On the search screen, so you remain on the last tab you opened. - // - // 0 for "In Names" - // 1 for "In Parameters" - // 2 for "In Return Types" - var currentTab = 0; + search_input.addEventListener("focus", function() { + searchState.putBackSearch(this); + search_input.origPlaceholder = searchState.input.placeholder; + search_input.placeholder = "Type your search here."; + loadSearch(); + }); + search_input.addEventListener("blur", function() { + search_input.placeholder = searchState.input.origPlaceholder; + }); - var mouseMovedAfterSearch = true; + document.addEventListener("mousemove", function() { + searchState.mouseMovedAfterSearch = true; + }); - var titleBeforeSearch = document.title; - var searchTitle = null; + search_input.removeAttribute('disabled'); - function removeEmptyStringsFromArray(x) { - for (var i = 0, len = x.length; i < len; ++i) { - if (x[i] === "") { - x.splice(i, 1); - i -= 1; - } + // `crates{version}.js` should always be loaded before this script, so we can use it safely. + searchState.addCrateDropdown(window.ALL_CRATES); + var params = searchState.getQueryStringParams(); + if (params.search !== undefined) { + var search = searchState.outputElement(); + search.innerHTML = "<h3 style=\"text-align: center;\">" + + searchState.loadingText + "</h3>"; + searchState.showResults(search); + loadSearch(); } - } + }, + addCrateDropdown: function(crates) { + var elem = document.getElementById("crate-search"); - function clearInputTimeout() { - if (searchTimeout !== null) { - clearTimeout(searchTimeout); - searchTimeout = null; + if (!elem) { + return; } - } + var savedCrate = getSettingValue("saved-filter-crate"); + for (var i = 0, len = crates.length; i < len; ++i) { + var option = document.createElement("option"); + option.value = crates[i]; + option.innerText = crates[i]; + elem.appendChild(option); + // Set the crate filter from saved storage, if the current page has the saved crate + // filter. + // + // If not, ignore the crate filter -- we want to support filtering for crates on sites + // like doc.rust-lang.org where the crates may differ from page to page while on the + // same domain. + if (crates[i] === savedCrate) { + elem.value = savedCrate; + } + } + }, + }; function getPageId() { if (window.location.hash) { @@ -276,65 +345,23 @@ function hideThemeButtonState() { document.getElementsByTagName("body")[0].style.marginTop = ""; } - function showSearchResults(search) { - if (search === null || typeof search === 'undefined') { - search = getSearchElement(); - } - addClass(main, "hidden"); - removeClass(search, "hidden"); - mouseMovedAfterSearch = false; - document.title = searchTitle; - } - - function hideSearchResults(search) { - if (search === null || typeof search === 'undefined') { - search = getSearchElement(); - } - addClass(search, "hidden"); - removeClass(main, "hidden"); - document.title = titleBeforeSearch; - // We also remove the query parameter from the URL. - if (browserSupportsHistoryApi()) { - history.replaceState("", window.currentCrate + " - Rust", - getNakedUrl() + window.location.hash); - } - } - - // used for special search precedence - var TY_PRIMITIVE = itemTypes.indexOf("primitive"); - var TY_KEYWORD = itemTypes.indexOf("keyword"); - - function getQueryStringParams() { - var params = {}; - window.location.search.substring(1).split("&"). - map(function(s) { - var pair = s.split("="); - params[decodeURIComponent(pair[0])] = - typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]); - }); - return params; - } - - function browserSupportsHistoryApi() { - return window.history && typeof window.history.pushState === "function"; - } - function isHidden(elem) { return elem.offsetHeight === 0; } + var toggleAllDocsId = "toggle-all-docs"; var main = document.getElementById("main"); var savedHash = ""; function handleHashes(ev) { var elem; - var search = getSearchElement(); + var search = searchState.outputElement(); if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) { // This block occurs when clicking on an element in the navbar while // in a search. - hideSearchResults(search); + searchState.hideResults(search); var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1); - if (browserSupportsHistoryApi()) { + if (searchState.browserSupportsHistoryApi()) { // `window.location.search`` contains all the query parameters, not just `search`. history.replaceState(hash, "", getNakedUrl() + window.location.search + "#" + hash); @@ -475,18 +502,19 @@ function hideThemeButtonState() { function handleEscape(ev) { var help = getHelpElement(false); - var search = getSearchElement(); + var search = searchState.outputElement(); if (hasClass(help, "hidden") === false) { displayHelp(false, ev, help); } else if (hasClass(search, "hidden") === false) { - clearInputTimeout(); + searchState.clearInputTimeout(); ev.preventDefault(); - hideSearchResults(search); + searchState.hideResults(search); } - defocusSearchBar(); + searchState.defocus(); hideThemeButtonState(); } + var disableShortcuts = getSettingValue("disable-shortcuts") === "true"; function handleShortcut(ev) { // Don't interfere with browser shortcuts if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts === true) { @@ -509,7 +537,7 @@ function hideThemeButtonState() { case "S": displayHelp(false, ev); ev.preventDefault(); - focusSearchBar(); + searchState.focus(); break; case "+": @@ -596,15 +624,13 @@ function hideThemeButtonState() { document.addEventListener("keypress", handleShortcut); document.addEventListener("keydown", handleShortcut); - document.addEventListener("mousemove", function() { mouseMovedAfterSearch = true; }); - var handleSourceHighlight = (function() { var prev_line_id = 0; var set_fragment = function(name) { var x = window.scrollX, y = window.scrollY; - if (browserSupportsHistoryApi()) { + if (searchState.browserSupportsHistoryApi()) { history.replaceState(null, null, "#" + name); highlightSourceLines(); } else { @@ -686,1444 +712,6 @@ function hideThemeButtonState() { } }()); - /** - * A function to compute the Levenshtein distance between two strings - * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported - * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode - * This code is an unmodified version of the code written by Marco de Wit - * and was found at http://stackoverflow.com/a/18514751/745719 - */ - var levenshtein_row2 = []; - function levenshtein(s1, s2) { - if (s1 === s2) { - return 0; - } - var s1_len = s1.length, s2_len = s2.length; - if (s1_len && s2_len) { - var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2; - while (i1 < s1_len) { - row[i1] = ++i1; - } - while (i2 < s2_len) { - c2 = s2.charCodeAt(i2); - a = i2; - ++i2; - b = i2; - for (i1 = 0; i1 < s1_len; ++i1) { - c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0); - a = row[i1]; - b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c); - row[i1] = b; - } - } - return b; - } - return s1_len + s2_len; - } - - window.initSearch = function(rawSearchIndex) { - var MAX_LEV_DISTANCE = 3; - var MAX_RESULTS = 200; - var GENERICS_DATA = 1; - var NAME = 0; - var INPUTS_DATA = 0; - var OUTPUT_DATA = 1; - var NO_TYPE_FILTER = -1; - var currentResults, index, searchIndex; - var ALIASES = {}; - var params = getQueryStringParams(); - - // Populate search bar with query string search term when provided, - // but only if the input bar is empty. This avoid the obnoxious issue - // where you start trying to do a search, and the index loads, and - // suddenly your search is gone! - if (search_input.value === "") { - search_input.value = params.search || ""; - } - - /** - * Executes the query and builds an index of results - * @param {[Object]} query [The user query] - * @param {[type]} searchWords [The list of search words to query - * against] - * @param {[type]} filterCrates [Crate to search in if defined] - * @return {[type]} [A search index of results] - */ - function execQuery(query, searchWords, filterCrates) { - function itemTypeFromName(typename) { - for (var i = 0, len = itemTypes.length; i < len; ++i) { - if (itemTypes[i] === typename) { - return i; - } - } - return NO_TYPE_FILTER; - } - - var valLower = query.query.toLowerCase(), - val = valLower, - typeFilter = itemTypeFromName(query.type), - results = {}, results_in_args = {}, results_returned = {}, - split = valLower.split("::"); - - removeEmptyStringsFromArray(split); - - function transformResults(results, isType) { - var out = []; - for (var i = 0, len = results.length; i < len; ++i) { - if (results[i].id > -1) { - var obj = searchIndex[results[i].id]; - obj.lev = results[i].lev; - if (isType !== true || obj.type) { - var res = buildHrefAndPath(obj); - obj.displayPath = pathSplitter(res[0]); - obj.fullPath = obj.displayPath + obj.name; - // To be sure than it some items aren't considered as duplicate. - obj.fullPath += "|" + obj.ty; - obj.href = res[1]; - out.push(obj); - if (out.length >= MAX_RESULTS) { - break; - } - } - } - } - return out; - } - - function sortResults(results, isType) { - var ar = []; - for (var entry in results) { - if (hasOwnProperty(results, entry)) { - ar.push(results[entry]); - } - } - results = ar; - var i, len, result; - for (i = 0, len = results.length; i < len; ++i) { - result = results[i]; - result.word = searchWords[result.id]; - result.item = searchIndex[result.id] || {}; - } - // if there are no results then return to default and fail - if (results.length === 0) { - return []; - } - - results.sort(function(aaa, bbb) { - var a, b; - - // sort by exact match with regard to the last word (mismatch goes later) - a = (aaa.word !== val); - b = (bbb.word !== val); - if (a !== b) { return a - b; } - - // Sort by non levenshtein results and then levenshtein results by the distance - // (less changes required to match means higher rankings) - a = (aaa.lev); - b = (bbb.lev); - if (a !== b) { return a - b; } - - // sort by crate (non-current crate goes later) - a = (aaa.item.crate !== window.currentCrate); - b = (bbb.item.crate !== window.currentCrate); - if (a !== b) { return a - b; } - - // sort by item name length (longer goes later) - a = aaa.word.length; - b = bbb.word.length; - if (a !== b) { return a - b; } - - // sort by item name (lexicographically larger goes later) - a = aaa.word; - b = bbb.word; - if (a !== b) { return (a > b ? +1 : -1); } - - // sort by index of keyword in item name (no literal occurrence goes later) - a = (aaa.index < 0); - b = (bbb.index < 0); - if (a !== b) { return a - b; } - // (later literal occurrence, if any, goes later) - a = aaa.index; - b = bbb.index; - if (a !== b) { return a - b; } - - // special precedence for primitive and keyword pages - if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) || - (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) { - return -1; - } - if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) || - (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) { - return 1; - } - - // sort by description (no description goes later) - a = (aaa.item.desc === ""); - b = (bbb.item.desc === ""); - if (a !== b) { return a - b; } - - // sort by type (later occurrence in `itemTypes` goes later) - a = aaa.item.ty; - b = bbb.item.ty; - if (a !== b) { return a - b; } - - // sort by path (lexicographically larger goes later) - a = aaa.item.path; - b = bbb.item.path; - if (a !== b) { return (a > b ? +1 : -1); } - - // que sera, sera - return 0; - }); - - for (i = 0, len = results.length; i < len; ++i) { - var result = results[i]; - - // this validation does not make sense when searching by types - if (result.dontValidate) { - continue; - } - var name = result.item.name.toLowerCase(), - path = result.item.path.toLowerCase(), - parent = result.item.parent; - - if (isType !== true && - validateResult(name, path, split, parent) === false) - { - result.id = -1; - } - } - return transformResults(results); - } - - function extractGenerics(val) { - val = val.toLowerCase(); - if (val.indexOf("<") !== -1) { - var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">")); - return { - name: val.substring(0, val.indexOf("<")), - generics: values.split(/\s*,\s*/), - }; - } - return { - name: val, - generics: [], - }; - } - - function getObjectNameFromId(id) { - if (typeof id === "number") { - return searchIndex[id].name; - } - return id; - } - - function checkGenerics(obj, val) { - // The names match, but we need to be sure that all generics kinda - // match as well. - var tmp_lev, elem_name; - if (val.generics.length > 0) { - if (obj.length > GENERICS_DATA && - obj[GENERICS_DATA].length >= val.generics.length) { - var elems = Object.create(null); - var elength = object[GENERICS_DATA].length; - for (var x = 0; x < elength; ++x) { - elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1; - } - var total = 0; - var done = 0; - // We need to find the type that matches the most to remove it in order - // to move forward. - var vlength = val.generics.length; - for (x = 0; x < vlength; ++x) { - var lev = MAX_LEV_DISTANCE + 1; - var firstGeneric = getObjectNameFromId(val.generics[x]); - var match = null; - if (elems[firstGeneric]) { - match = firstGeneric; - lev = 0; - } else { - for (elem_name in elems) { - tmp_lev = levenshtein(elem_name, firstGeneric); - if (tmp_lev < lev) { - lev = tmp_lev; - match = elem_name; - } - } - } - if (match !== null) { - elems[match] -= 1; - if (elems[match] == 0) { - delete elems[match]; - } - total += lev; - done += 1; - } else { - return MAX_LEV_DISTANCE + 1; - } - } - return Math.ceil(total / done); - } - } - return MAX_LEV_DISTANCE + 1; - } - - // Check for type name and type generics (if any). - function checkType(obj, val, literalSearch) { - var lev_distance = MAX_LEV_DISTANCE + 1; - var len, x, firstGeneric; - if (obj[NAME] === val.name) { - if (literalSearch === true) { - if (val.generics && val.generics.length !== 0) { - if (obj.length > GENERICS_DATA && - obj[GENERICS_DATA].length >= val.generics.length) { - var elems = Object.create(null); - len = obj[GENERICS_DATA].length; - for (x = 0; x < len; ++x) { - elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1; - } - - var allFound = true; - len = val.generics.length; - for (x = 0; x < len; ++x) { - firstGeneric = getObjectNameFromId(val.generics[x]); - if (elems[firstGeneric]) { - elems[firstGeneric] -= 1; - } else { - allFound = false; - break; - } - } - if (allFound === true) { - return true; - } - } else { - return false; - } - } - return true; - } - // If the type has generics but don't match, then it won't return at this point. - // Otherwise, `checkGenerics` will return 0 and it'll return. - if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) { - var tmp_lev = checkGenerics(obj, val); - if (tmp_lev <= MAX_LEV_DISTANCE) { - return tmp_lev; - } - } else { - return 0; - } - } - // Names didn't match so let's check if one of the generic types could. - if (literalSearch === true) { - if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) { - return obj[GENERICS_DATA].some( - function(name) { - return name === val.name; - }); - } - return false; - } - lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance); - if (lev_distance <= MAX_LEV_DISTANCE) { - // The generics didn't match but the name kinda did so we give it - // a levenshtein distance value that isn't *this* good so it goes - // into the search results but not too high. - lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2); - } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) { - // We can check if the type we're looking for is inside the generics! - var olength = obj[GENERICS_DATA].length; - for (x = 0; x < olength; ++x) { - lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name), - lev_distance); - } - } - // Now whatever happens, the returned distance is "less good" so we should mark it - // as such, and so we add 1 to the distance to make it "less good". - return lev_distance + 1; - } - - function findArg(obj, val, literalSearch, typeFilter) { - var lev_distance = MAX_LEV_DISTANCE + 1; - - if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) { - var length = obj.type[INPUTS_DATA].length; - for (var i = 0; i < length; i++) { - var tmp = obj.type[INPUTS_DATA][i]; - if (typePassesFilter(typeFilter, tmp[1]) === false) { - continue; - } - tmp = checkType(tmp, val, literalSearch); - if (literalSearch === true) { - if (tmp === true) { - return true; - } - continue; - } - lev_distance = Math.min(tmp, lev_distance); - if (lev_distance === 0) { - return 0; - } - } - } - return literalSearch === true ? false : lev_distance; - } - - function checkReturned(obj, val, literalSearch, typeFilter) { - var lev_distance = MAX_LEV_DISTANCE + 1; - - if (obj && obj.type && obj.type.length > OUTPUT_DATA) { - var ret = obj.type[OUTPUT_DATA]; - if (typeof ret[0] === "string") { - ret = [ret]; - } - for (var x = 0, len = ret.length; x < len; ++x) { - var tmp = ret[x]; - if (typePassesFilter(typeFilter, tmp[1]) === false) { - continue; - } - tmp = checkType(tmp, val, literalSearch); - if (literalSearch === true) { - if (tmp === true) { - return true; - } - continue; - } - lev_distance = Math.min(tmp, lev_distance); - if (lev_distance === 0) { - return 0; - } - } - } - return literalSearch === true ? false : lev_distance; - } - - function checkPath(contains, lastElem, ty) { - if (contains.length === 0) { - return 0; - } - var ret_lev = MAX_LEV_DISTANCE + 1; - var path = ty.path.split("::"); - - if (ty.parent && ty.parent.name) { - path.push(ty.parent.name.toLowerCase()); - } - - var length = path.length; - var clength = contains.length; - if (clength > length) { - return MAX_LEV_DISTANCE + 1; - } - for (var i = 0; i < length; ++i) { - if (i + clength > length) { - break; - } - var lev_total = 0; - var aborted = false; - for (var x = 0; x < clength; ++x) { - var lev = levenshtein(path[i + x], contains[x]); - if (lev > MAX_LEV_DISTANCE) { - aborted = true; - break; - } - lev_total += lev; - } - if (aborted === false) { - ret_lev = Math.min(ret_lev, Math.round(lev_total / clength)); - } - } - return ret_lev; - } - - function typePassesFilter(filter, type) { - // No filter - if (filter <= NO_TYPE_FILTER) return true; - - // Exact match - if (filter === type) return true; - - // Match related items - var name = itemTypes[type]; - switch (itemTypes[filter]) { - case "constant": - return name === "associatedconstant"; - case "fn": - return name === "method" || name === "tymethod"; - case "type": - return name === "primitive" || name === "associatedtype"; - case "trait": - return name === "traitalias"; - } - - // No match - return false; - } - - function createAliasFromItem(item) { - return { - crate: item.crate, - name: item.name, - path: item.path, - desc: item.desc, - ty: item.ty, - parent: item.parent, - type: item.type, - is_alias: true, - }; - } - - function handleAliases(ret, query, filterCrates) { - // We separate aliases and crate aliases because we want to have current crate - // aliases to be before the others in the displayed results. - var aliases = []; - var crateAliases = []; - if (filterCrates !== undefined) { - if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) { - var query_aliases = ALIASES[filterCrates][query.search]; - var len = query_aliases.length; - for (var i = 0; i < len; ++i) { - aliases.push(createAliasFromItem(searchIndex[query_aliases[i]])); - } - } - } else { - Object.keys(ALIASES).forEach(function(crate) { - if (ALIASES[crate][query.search]) { - var pushTo = crate === window.currentCrate ? crateAliases : aliases; - var query_aliases = ALIASES[crate][query.search]; - var len = query_aliases.length; - for (var i = 0; i < len; ++i) { - pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]])); - } - } - }); - } - - var sortFunc = function(aaa, bbb) { - if (aaa.path < bbb.path) { - return 1; - } else if (aaa.path === bbb.path) { - return 0; - } - return -1; - }; - crateAliases.sort(sortFunc); - aliases.sort(sortFunc); - - var pushFunc = function(alias) { - alias.alias = query.raw; - var res = buildHrefAndPath(alias); - alias.displayPath = pathSplitter(res[0]); - alias.fullPath = alias.displayPath + alias.name; - alias.href = res[1]; - - ret.others.unshift(alias); - if (ret.others.length > MAX_RESULTS) { - ret.others.pop(); - } - }; - onEach(aliases, pushFunc); - onEach(crateAliases, pushFunc); - } - - // quoted values mean literal search - var nSearchWords = searchWords.length; - var i, it; - var ty; - var fullId; - var returned; - var in_args; - var len; - if ((val.charAt(0) === "\"" || val.charAt(0) === "'") && - val.charAt(val.length - 1) === val.charAt(0)) - { - val = extractGenerics(val.substr(1, val.length - 2)); - for (i = 0; i < nSearchWords; ++i) { - if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) { - continue; - } - in_args = findArg(searchIndex[i], val, true, typeFilter); - returned = checkReturned(searchIndex[i], val, true, typeFilter); - ty = searchIndex[i]; - fullId = ty.id; - - if (searchWords[i] === val.name - && typePassesFilter(typeFilter, searchIndex[i].ty) - && results[fullId] === undefined) { - results[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - if (in_args === true && results_in_args[fullId] === undefined) { - results_in_args[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - if (returned === true && results_returned[fullId] === undefined) { - results_returned[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - } - query.inputs = [val]; - query.output = val; - query.search = val; - // searching by type - } else if (val.search("->") > -1) { - var trimmer = function(s) { return s.trim(); }; - var parts = val.split("->").map(trimmer); - var input = parts[0]; - // sort inputs so that order does not matter - var inputs = input.split(",").map(trimmer).sort(); - for (i = 0, len = inputs.length; i < len; ++i) { - inputs[i] = extractGenerics(inputs[i]); - } - var output = extractGenerics(parts[1]); - - for (i = 0; i < nSearchWords; ++i) { - if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) { - continue; - } - var type = searchIndex[i].type; - ty = searchIndex[i]; - if (!type) { - continue; - } - fullId = ty.id; - - returned = checkReturned(ty, output, true, NO_TYPE_FILTER); - if (output.name === "*" || returned === true) { - in_args = false; - var is_module = false; - - if (input === "*") { - is_module = true; - } else { - var allFound = true; - for (it = 0, len = inputs.length; allFound === true && it < len; it++) { - allFound = checkType(type, inputs[it], true); - } - in_args = allFound; - } - if (in_args === true) { - results_in_args[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - if (returned === true) { - results_returned[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - if (is_module === true) { - results[fullId] = { - id: i, - index: -1, - dontValidate: true, - }; - } - } - } - query.inputs = inputs.map(function(input) { - return input.name; - }); - query.output = output.name; - } else { - query.inputs = [val]; - query.output = val; - query.search = val; - // gather matching search results up to a certain maximum - val = val.replace(/\_/g, ""); - - var valGenerics = extractGenerics(val); - - var paths = valLower.split("::"); - removeEmptyStringsFromArray(paths); - val = paths[paths.length - 1]; - var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1); - - var lev, j; - for (j = 0; j < nSearchWords; ++j) { - ty = searchIndex[j]; - if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) { - continue; - } - var lev_add = 0; - if (paths.length > 1) { - lev = checkPath(contains, paths[paths.length - 1], ty); - if (lev > MAX_LEV_DISTANCE) { - continue; - } else if (lev > 0) { - lev_add = lev / 10; - } - } - - returned = MAX_LEV_DISTANCE + 1; - in_args = MAX_LEV_DISTANCE + 1; - var index = -1; - // we want lev results to go lower than others - lev = MAX_LEV_DISTANCE + 1; - fullId = ty.id; - - if (searchWords[j].indexOf(split[i]) > -1 || - searchWords[j].indexOf(val) > -1 || - ty.normalizedName.indexOf(val) > -1) - { - // filter type: ... queries - if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) { - index = ty.normalizedName.indexOf(val); - } - } - if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) { - if (typePassesFilter(typeFilter, ty.ty) === false) { - lev = MAX_LEV_DISTANCE + 1; - } else { - lev += 1; - } - } - in_args = findArg(ty, valGenerics, false, typeFilter); - returned = checkReturned(ty, valGenerics, false, typeFilter); - - lev += lev_add; - if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) { - if (val.length < 6) { - lev -= 1; - } else { - lev = 0; - } - } - if (in_args <= MAX_LEV_DISTANCE) { - if (results_in_args[fullId] === undefined) { - results_in_args[fullId] = { - id: j, - index: index, - lev: in_args, - }; - } - results_in_args[fullId].lev = - Math.min(results_in_args[fullId].lev, in_args); - } - if (returned <= MAX_LEV_DISTANCE) { - if (results_returned[fullId] === undefined) { - results_returned[fullId] = { - id: j, - index: index, - lev: returned, - }; - } - results_returned[fullId].lev = - Math.min(results_returned[fullId].lev, returned); - } - if (index !== -1 || lev <= MAX_LEV_DISTANCE) { - if (index !== -1 && paths.length < 2) { - lev = 0; - } - if (results[fullId] === undefined) { - results[fullId] = { - id: j, - index: index, - lev: lev, - }; - } - results[fullId].lev = Math.min(results[fullId].lev, lev); - } - } - } - - var ret = { - "in_args": sortResults(results_in_args, true), - "returned": sortResults(results_returned, true), - "others": sortResults(results), - }; - handleAliases(ret, query, filterCrates); - return ret; - } - - /** - * Validate performs the following boolean logic. For example: - * "File::open" will give IF A PARENT EXISTS => ("file" && "open") - * exists in (name || path || parent) OR => ("file" && "open") exists in - * (name || path ) - * - * This could be written functionally, but I wanted to minimise - * functions on stack. - * - * @param {[string]} name [The name of the result] - * @param {[string]} path [The path of the result] - * @param {[string]} keys [The keys to be used (["file", "open"])] - * @param {[object]} parent [The parent of the result] - * @return {[boolean]} [Whether the result is valid or not] - */ - function validateResult(name, path, keys, parent) { - for (var i = 0, len = keys.length; i < len; ++i) { - // each check is for validation so we negate the conditions and invalidate - if (!( - // check for an exact name match - name.indexOf(keys[i]) > -1 || - // then an exact path match - path.indexOf(keys[i]) > -1 || - // next if there is a parent, check for exact parent match - (parent !== undefined && parent.name !== undefined && - parent.name.toLowerCase().indexOf(keys[i]) > -1) || - // lastly check to see if the name was a levenshtein match - levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) { - return false; - } - } - return true; - } - - function getQuery(raw) { - var matches, type, query; - query = raw; - - matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i); - if (matches) { - type = matches[1].replace(/^const$/, "constant"); - query = query.substring(matches[0].length); - } - - return { - raw: raw, - query: query, - type: type, - id: query + type - }; - } - - function initSearchNav() { - var hoverTimeout; - - var click_func = function(e) { - var el = e.target; - // to retrieve the real "owner" of the event. - while (el.tagName !== "TR") { - el = el.parentNode; - } - var dst = e.target.getElementsByTagName("a"); - if (dst.length < 1) { - return; - } - dst = dst[0]; - if (window.location.pathname === dst.pathname) { - hideSearchResults(); - document.location.href = dst.href; - } - }; - var mouseover_func = function(e) { - if (mouseMovedAfterSearch) { - var el = e.target; - // to retrieve the real "owner" of the event. - while (el.tagName !== "TR") { - el = el.parentNode; - } - clearTimeout(hoverTimeout); - hoverTimeout = setTimeout(function() { - onEachLazy(document.getElementsByClassName("search-results"), function(e) { - onEachLazy(e.getElementsByClassName("result"), function(i_e) { - removeClass(i_e, "highlighted"); - }); - }); - addClass(el, "highlighted"); - }, 20); - } - }; - onEachLazy(document.getElementsByClassName("search-results"), function(e) { - onEachLazy(e.getElementsByClassName("result"), function(i_e) { - i_e.onclick = click_func; - i_e.onmouseover = mouseover_func; - }); - }); - - search_input.onkeydown = function(e) { - // "actives" references the currently highlighted item in each search tab. - // Each array in "actives" represents a tab. - var actives = [[], [], []]; - // "current" is used to know which tab we're looking into. - var current = 0; - onEachLazy(document.getElementById("results").childNodes, function(e) { - onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) { - actives[current].push(h_e); - }); - current += 1; - }); - - if (e.which === 38) { // up - if (e.ctrlKey) { // Going through result tabs. - printTab(currentTab > 0 ? currentTab - 1 : 2); - } else { - if (!actives[currentTab].length || - !actives[currentTab][0].previousElementSibling) { - return; - } - addClass(actives[currentTab][0].previousElementSibling, "highlighted"); - removeClass(actives[currentTab][0], "highlighted"); - } - e.preventDefault(); - } else if (e.which === 40) { // down - if (e.ctrlKey) { // Going through result tabs. - printTab(currentTab > 1 ? 0 : currentTab + 1); - } else if (!actives[currentTab].length) { - var results = document.getElementById("results").childNodes; - if (results.length > 0) { - var res = results[currentTab].getElementsByClassName("result"); - if (res.length > 0) { - addClass(res[0], "highlighted"); - } - } - } else if (actives[currentTab][0].nextElementSibling) { - addClass(actives[currentTab][0].nextElementSibling, "highlighted"); - removeClass(actives[currentTab][0], "highlighted"); - } - e.preventDefault(); - } else if (e.which === 13) { // return - if (actives[currentTab].length) { - document.location.href = - actives[currentTab][0].getElementsByTagName("a")[0].href; - } - } else if (e.which === 16) { // shift - // Does nothing, it's just to avoid losing "focus" on the highlighted element. - } else if (actives[currentTab].length > 0) { - removeClass(actives[currentTab][0], "highlighted"); - } - }; - } - - function buildHrefAndPath(item) { - var displayPath; - var href; - var type = itemTypes[item.ty]; - var name = item.name; - var path = item.path; - - if (type === "mod") { - displayPath = path + "::"; - href = window.rootPath + path.replace(/::/g, "/") + "/" + - name + "/index.html"; - } else if (type === "primitive" || type === "keyword") { - displayPath = ""; - href = window.rootPath + path.replace(/::/g, "/") + - "/" + type + "." + name + ".html"; - } else if (type === "externcrate") { - displayPath = ""; - href = window.rootPath + name + "/index.html"; - } else if (item.parent !== undefined) { - var myparent = item.parent; - var anchor = "#" + type + "." + name; - var parentType = itemTypes[myparent.ty]; - var pageType = parentType; - var pageName = myparent.name; - - if (parentType === "primitive") { - displayPath = myparent.name + "::"; - } else if (type === "structfield" && parentType === "variant") { - // Structfields belonging to variants are special: the - // final path element is the enum name. - var enumNameIdx = item.path.lastIndexOf("::"); - var enumName = item.path.substr(enumNameIdx + 2); - path = item.path.substr(0, enumNameIdx); - displayPath = path + "::" + enumName + "::" + myparent.name + "::"; - anchor = "#variant." + myparent.name + ".field." + name; - pageType = "enum"; - pageName = enumName; - } else { - displayPath = path + "::" + myparent.name + "::"; - } - href = window.rootPath + path.replace(/::/g, "/") + - "/" + pageType + - "." + pageName + - ".html" + anchor; - } else { - displayPath = item.path + "::"; - href = window.rootPath + item.path.replace(/::/g, "/") + - "/" + type + "." + name + ".html"; - } - return [displayPath, href]; - } - - function escape(content) { - var h1 = document.createElement("h1"); - h1.textContent = content; - return h1.innerHTML; - } - - function pathSplitter(path) { - var tmp = "<span>" + path.replace(/::/g, "::</span><span>"); - if (tmp.endsWith("<span>")) { - return tmp.slice(0, tmp.length - 6); - } - return tmp; - } - - function addTab(array, query, display) { - var extraStyle = ""; - if (display === false) { - extraStyle = " style=\"display: none;\""; - } - - var output = ""; - var duplicates = {}; - var length = 0; - if (array.length > 0) { - output = "<table class=\"search-results\"" + extraStyle + ">"; - - array.forEach(function(item) { - var name, type; - - name = item.name; - type = itemTypes[item.ty]; - - if (item.is_alias !== true) { - if (duplicates[item.fullPath]) { - return; - } - duplicates[item.fullPath] = true; - } - length += 1; - - output += "<tr class=\"" + type + " result\"><td>" + - "<a href=\"" + item.href + "\">" + - (item.is_alias === true ? - ("<span class=\"alias\"><b>" + item.alias + " </b></span><span " + - "class=\"grey\"><i> - see </i></span>") : "") + - item.displayPath + "<span class=\"" + type + "\">" + - name + "</span></a></td><td>" + - "<a href=\"" + item.href + "\">" + - "<span class=\"desc\">" + item.desc + - " </span></a></td></tr>"; - }); - output += "</table>"; - } else { - output = "<div class=\"search-failed\"" + extraStyle + ">No results :(<br/>" + - "Try on <a href=\"https://duckduckgo.com/?q=" + - encodeURIComponent("rust " + query.query) + - "\">DuckDuckGo</a>?<br/><br/>" + - "Or try looking in one of these:<ul><li>The <a " + - "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " + - " for technical details about the language.</li><li><a " + - "href=\"https://doc.rust-lang.org/rust-by-example/index.html\">Rust By " + - "Example</a> for expository code examples.</a></li><li>The <a " + - "href=\"https://doc.rust-lang.org/book/index.html\">Rust Book</a> for " + - "introductions to language features and the language itself.</li><li><a " + - "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" + - " <a href=\"https://crates.io/\">crates.io</a>.</li></ul></div>"; - } - return [output, length]; - } - - function makeTabHeader(tabNb, text, nbElems) { - if (currentTab === tabNb) { - return "<button class=\"selected\">" + text + - " <div class=\"count\">(" + nbElems + ")</div></button>"; - } - return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>"; - } - - function showResults(results) { - var search = getSearchElement(); - if (results.others.length === 1 - && getSettingValue("go-to-only-result") === "true" - // By default, the search DOM element is "empty" (meaning it has no children not - // text content). Once a search has been run, it won't be empty, even if you press - // ESC or empty the search input (which also "cancels" the search). - && (!search.firstChild || search.firstChild.innerText !== getSearchLoadingText())) - { - var elem = document.createElement("a"); - elem.href = results.others[0].href; - elem.style.display = "none"; - // For firefox, we need the element to be in the DOM so it can be clicked. - document.body.appendChild(elem); - elem.click(); - return; - } - var query = getQuery(search_input.value); - - currentResults = query.id; - - var ret_others = addTab(results.others, query); - var ret_in_args = addTab(results.in_args, query, false); - var ret_returned = addTab(results.returned, query, false); - - // Navigate to the relevant tab if the current tab is empty, like in case users search - // for "-> String". If they had selected another tab previously, they have to click on - // it again. - if ((currentTab === 0 && ret_others[1] === 0) || - (currentTab === 1 && ret_in_args[1] === 0) || - (currentTab === 2 && ret_returned[1] === 0)) { - if (ret_others[1] !== 0) { - currentTab = 0; - } else if (ret_in_args[1] !== 0) { - currentTab = 1; - } else if (ret_returned[1] !== 0) { - currentTab = 2; - } - } - - var output = "<h1>Results for " + escape(query.query) + - (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" + - "<div id=\"titles\">" + - makeTabHeader(0, "In Names", ret_others[1]) + - makeTabHeader(1, "In Parameters", ret_in_args[1]) + - makeTabHeader(2, "In Return Types", ret_returned[1]) + - "</div><div id=\"results\">" + - ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>"; - - search.innerHTML = output; - showSearchResults(search); - initSearchNav(); - var elems = document.getElementById("titles").childNodes; - elems[0].onclick = function() { printTab(0); }; - elems[1].onclick = function() { printTab(1); }; - elems[2].onclick = function() { printTab(2); }; - printTab(currentTab); - } - - function execSearch(query, searchWords, filterCrates) { - function getSmallest(arrays, positions, notDuplicates) { - var start = null; - - for (var it = 0, len = positions.length; it < len; ++it) { - if (arrays[it].length > positions[it] && - (start === null || start > arrays[it][positions[it]].lev) && - !notDuplicates[arrays[it][positions[it]].fullPath]) { - start = arrays[it][positions[it]].lev; - } - } - return start; - } - - function mergeArrays(arrays) { - var ret = []; - var positions = []; - var notDuplicates = {}; - - for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) { - positions.push(0); - } - while (ret.length < MAX_RESULTS) { - var smallest = getSmallest(arrays, positions, notDuplicates); - - if (smallest === null) { - break; - } - for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) { - if (arrays[x].length > positions[x] && - arrays[x][positions[x]].lev === smallest && - !notDuplicates[arrays[x][positions[x]].fullPath]) { - ret.push(arrays[x][positions[x]]); - notDuplicates[arrays[x][positions[x]].fullPath] = true; - positions[x] += 1; - } - } - } - return ret; - } - - var queries = query.raw.split(","); - var results = { - "in_args": [], - "returned": [], - "others": [], - }; - - for (var i = 0, len = queries.length; i < len; ++i) { - query = queries[i].trim(); - if (query.length !== 0) { - var tmp = execQuery(getQuery(query), searchWords, filterCrates); - - results.in_args.push(tmp.in_args); - results.returned.push(tmp.returned); - results.others.push(tmp.others); - } - } - if (queries.length > 1) { - return { - "in_args": mergeArrays(results.in_args), - "returned": mergeArrays(results.returned), - "others": mergeArrays(results.others), - }; - } - return { - "in_args": results.in_args[0], - "returned": results.returned[0], - "others": results.others[0], - }; - } - - function getFilterCrates() { - var elem = document.getElementById("crate-search"); - - if (elem && elem.value !== "All crates" && hasOwnProperty(rawSearchIndex, elem.value)) { - return elem.value; - } - return undefined; - } - - function search(e, forced) { - var params = getQueryStringParams(); - var query = getQuery(search_input.value.trim()); - - if (e) { - e.preventDefault(); - } - - if (query.query.length === 0) { - return; - } - if (forced !== true && query.id === currentResults) { - if (query.query.length > 0) { - putBackSearch(search_input); - } - return; - } - - // Update document title to maintain a meaningful browser history - searchTitle = "Results for " + query.query + " - Rust"; - - // Because searching is incremental by character, only the most - // recent search query is added to the browser history. - if (browserSupportsHistoryApi()) { - var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) + - window.location.hash; - if (!history.state && !params.search) { - history.pushState(query, "", newURL); - } else { - history.replaceState(query, "", newURL); - } - } - - var filterCrates = getFilterCrates(); - showResults(execSearch(query, index, filterCrates)); - } - - function buildIndex(rawSearchIndex) { - searchIndex = []; - var searchWords = []; - var i, word; - var currentIndex = 0; - var id = 0; - - for (var crate in rawSearchIndex) { - if (!hasOwnProperty(rawSearchIndex, crate)) { continue; } - - var crateSize = 0; - - searchWords.push(crate); - var normalizedName = crate.indexOf("_") === -1 - ? crate - : crate.replace(/_/g, ""); - // This object should have exactly the same set of fields as the "row" - // object defined below. Your JavaScript runtime will thank you. - // https://mathiasbynens.be/notes/shapes-ics - var crateRow = { - crate: crate, - ty: 1, // == ExternCrate - name: crate, - path: "", - desc: rawSearchIndex[crate].doc, - parent: undefined, - type: null, - id: id, - normalizedName: normalizedName, - }; - id += 1; - searchIndex.push(crateRow); - currentIndex += 1; - - // an array of (Number) item types - var itemTypes = rawSearchIndex[crate].t; - // an array of (String) item names - var itemNames = rawSearchIndex[crate].n; - // an array of (String) full paths (or empty string for previous path) - var itemPaths = rawSearchIndex[crate].q; - // an array of (String) descriptions - var itemDescs = rawSearchIndex[crate].d; - // an array of (Number) the parent path index + 1 to `paths`, or 0 if none - var itemParentIdxs = rawSearchIndex[crate].i; - // an array of (Object | null) the type of the function, if any - var itemFunctionSearchTypes = rawSearchIndex[crate].f; - // an array of [(Number) item type, - // (String) name] - var paths = rawSearchIndex[crate].p; - // a array of [(String) alias name - // [Number] index to items] - var aliases = rawSearchIndex[crate].a; - - // convert `rawPaths` entries into object form - var len = paths.length; - for (i = 0; i < len; ++i) { - paths[i] = {ty: paths[i][0], name: paths[i][1]}; - } - - // convert `item*` into an object form, and construct word indices. - // - // before any analysis is performed lets gather the search terms to - // search against apart from the rest of the data. This is a quick - // operation that is cached for the life of the page state so that - // all other search operations have access to this cached data for - // faster analysis operations - len = itemTypes.length; - var lastPath = ""; - for (i = 0; i < len; ++i) { - // This object should have exactly the same set of fields as the "crateRow" - // object defined above. - if (typeof itemNames[i] === "string") { - word = itemNames[i].toLowerCase(); - searchWords.push(word); - } else { - word = ""; - searchWords.push(""); - } - var normalizedName = word.indexOf("_") === -1 - ? word - : word.replace(/_/g, ""); - var row = { - crate: crate, - ty: itemTypes[i], - name: itemNames[i], - path: itemPaths[i] ? itemPaths[i] : lastPath, - desc: itemDescs[i], - parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, - type: itemFunctionSearchTypes[i], - id: id, - normalizedName: normalizedName, - }; - id += 1; - searchIndex.push(row); - lastPath = row.path; - crateSize += 1; - } - - if (aliases) { - ALIASES[crate] = {}; - var j, local_aliases; - for (var alias_name in aliases) { - if (!aliases.hasOwnProperty(alias_name)) { continue; } - - if (!ALIASES[crate].hasOwnProperty(alias_name)) { - ALIASES[crate][alias_name] = []; - } - local_aliases = aliases[alias_name]; - for (j = 0, len = local_aliases.length; j < len; ++j) { - ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex); - } - } - } - currentIndex += crateSize; - } - return searchWords; - } - - function registerSearchEvents() { - var searchAfter500ms = function() { - clearInputTimeout(); - if (search_input.value.length === 0) { - if (browserSupportsHistoryApi()) { - history.replaceState("", window.currentCrate + " - Rust", - getNakedUrl() + window.location.hash); - } - hideSearchResults(); - } else { - searchTimeout = setTimeout(search, 500); - } - }; - search_input.onkeyup = searchAfter500ms; - search_input.oninput = searchAfter500ms; - document.getElementsByClassName("search-form")[0].onsubmit = function(e) { - e.preventDefault(); - clearInputTimeout(); - search(); - }; - search_input.onchange = function(e) { - if (e.target !== document.activeElement) { - // To prevent doing anything when it's from a blur event. - return; - } - // Do NOT e.preventDefault() here. It will prevent pasting. - clearInputTimeout(); - // zero-timeout necessary here because at the time of event handler execution the - // pasted content is not in the input field yet. Shouldn’t make any difference for - // change, though. - setTimeout(search, 0); - }; - search_input.onpaste = search_input.onchange; - - var selectCrate = document.getElementById("crate-search"); - if (selectCrate) { - selectCrate.onchange = function() { - updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value); - search(undefined, true); - }; - } - - // Push and pop states are used to add search results to the browser - // history. - if (browserSupportsHistoryApi()) { - // Store the previous <title> so we can revert back to it later. - var previousTitle = document.title; - - window.addEventListener("popstate", function(e) { - var params = getQueryStringParams(); - // Revert to the previous title manually since the History - // API ignores the title parameter. - document.title = previousTitle; - // When browsing forward to search results the previous - // search will be repeated, so the currentResults are - // cleared to ensure the search is successful. - currentResults = null; - // Synchronize search bar with query string state and - // perform the search. This will empty the bar if there's - // nothing there, which lets you really go back to a - // previous state with nothing in the bar. - if (params.search && params.search.length > 0) { - search_input.value = params.search; - // Some browsers fire "onpopstate" for every page load - // (Chrome), while others fire the event only when actually - // popping a state (Firefox), which is why search() is - // called both here and at the end of the startSearch() - // function. - search(e); - } else { - search_input.value = ""; - // When browsing back from search results the main page - // visibility must be reset. - hideSearchResults(); - } - }); - } - - // This is required in firefox to avoid this problem: Navigating to a search result - // with the keyboard, hitting enter, and then hitting back would take you back to - // the doc page, rather than the search that should overlay it. - // This was an interaction between the back-forward cache and our handlers - // that try to sync state between the URL and the search input. To work around it, - // do a small amount of re-init on page show. - window.onpageshow = function(){ - var qSearch = getQueryStringParams().search; - if (search_input.value === "" && qSearch) { - search_input.value = qSearch; - } - search(); - }; - } - - index = buildIndex(rawSearchIndex); - registerSearchEvents(); - // If there's a search term in the URL, execute the search now. - if (getQueryStringParams().search) { - search(); - } - }; - function addSidebarCrates(crates) { // Draw a convenient sidebar of known crates if we have a listing if (window.rootPath === "../" || window.rootPath === "./") { @@ -2216,6 +804,9 @@ function hideThemeButtonState() { block("foreigntype", "Foreign Types"); block("keyword", "Keywords"); block("traitalias", "Trait Aliases"); + + // `crates{version}.js` should always be loaded before this script, so we can use it safely. + addSidebarCrates(window.ALL_CRATES); }; window.register_implementors = function(imp) { @@ -2316,6 +907,9 @@ function hideThemeButtonState() { if (hasClass(innerToggle, "will-expand")) { updateLocalStorage("rustdoc-collapse", "false"); removeClass(innerToggle, "will-expand"); + onEachLazy(document.getElementsByTagName("details"), function(e) { + e.open = true; + }); onEveryMatchingChild(innerToggle, "inner", function(e) { e.innerHTML = labelForToggleButton(false); }); @@ -2328,6 +922,9 @@ function hideThemeButtonState() { } else { updateLocalStorage("rustdoc-collapse", "true"); addClass(innerToggle, "will-expand"); + onEachLazy(document.getElementsByTagName("details"), function(e) { + e.open = false; + }); onEveryMatchingChild(innerToggle, "inner", function(e) { var parent = e.parentNode; var superParent = null; @@ -2569,6 +1166,7 @@ function hideThemeButtonState() { var toggle = createSimpleToggle(false); var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true"; var hideImplementors = getSettingValue("auto-collapse-implementors") !== "false"; + var hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false"; var func = function(e) { var next = e.nextElementSibling; @@ -2615,6 +1213,14 @@ function hideThemeButtonState() { onEachLazy(document.getElementsByClassName("associatedconstant"), func); onEachLazy(document.getElementsByClassName("impl"), funcImpl); var impl_call = function() {}; + // Large items are hidden by default in the HTML. If the setting overrides that, show 'em. + if (!hideLargeItemContents) { + onEachLazy(document.getElementsByTagName("details"), function (e) { + if (hasClass(e, "type-contents-toggle")) { + e.open = true; + } + }); + } if (hideMethodDocs === true) { impl_call = function(e, newToggle) { if (e.id.match(/^impl(?:-\d+)?$/) === null) { @@ -2683,18 +1289,7 @@ function hideThemeButtonState() { }); } } - var showItemDeclarations = getSettingValue("auto-hide-" + className); - if (showItemDeclarations === null) { - if (className === "enum" || className === "macro") { - showItemDeclarations = "false"; - } else if (className === "struct" || className === "union" || className === "trait") { - showItemDeclarations = "true"; - } else { - // In case we found an unknown type, we just use the "parent" value. - showItemDeclarations = getSettingValue("auto-hide-declarations"); - } - } - showItemDeclarations = showItemDeclarations === "false"; + function buildToggleWrapper(e) { if (hasClass(e, "autohide")) { var wrap = e.previousElementSibling; @@ -2721,11 +1316,8 @@ function hideThemeButtonState() { var extraClass; if (hasClass(e, "type-decl")) { - fontSize = "20px"; - otherMessage = " Show declaration"; - if (showItemDeclarations === false) { - extraClass = "collapsed"; - } + // We do something special for these + return; } else if (hasClass(e, "sub-variant")) { otherMessage = " Show fields"; } else if (hasClass(e, "non-exhaustive")) { @@ -2750,11 +1342,8 @@ function hideThemeButtonState() { otherMessage, fontSize, extraClass, - hasClass(e, "type-decl") === false || showItemDeclarations === true), + true), e); - if (hasClass(e, "type-decl") === true && showItemDeclarations === true) { - collapseDocs(e.previousSibling.childNodes[0], "toggle"); - } if (hasClass(e, "non-exhaustive") === true) { collapseDocs(e.previousSibling.childNodes[0], "toggle"); } @@ -2772,38 +1361,6 @@ function hideThemeButtonState() { } }()); - function createToggleWrapper(tog) { - var span = document.createElement("span"); - span.className = "toggle-label"; - span.style.display = "none"; - span.innerHTML = " Expand attributes"; - tog.appendChild(span); - - var wrapper = document.createElement("div"); - wrapper.className = "toggle-wrapper toggle-attributes"; - wrapper.appendChild(tog); - return wrapper; - } - - (function() { - // To avoid checking on "rustdoc-item-attributes" value on every loop... - var itemAttributesFunc = function() {}; - if (getSettingValue("auto-hide-attributes") !== "false") { - itemAttributesFunc = function(x) { - collapseDocs(x.previousSibling.childNodes[0], "toggle"); - }; - } - var attributesToggle = createToggleWrapper(createSimpleToggle(false)); - onEachLazy(main.getElementsByClassName("attributes"), function(i_e) { - var attr_tog = attributesToggle.cloneNode(true); - if (hasClass(i_e, "top-attr") === true) { - addClass(attr_tog, "top-attr"); - } - i_e.parentNode.insertBefore(attr_tog, i_e); - itemAttributesFunc(i_e); - }); - }()); - (function() { // To avoid checking on "rustdoc-line-numbers" value on every loop... var lineNumbersFunc = function() {}; @@ -2847,60 +1404,6 @@ function hideThemeButtonState() { }; }); - // In the search display, allows to switch between tabs. - function printTab(nb) { - if (nb === 0 || nb === 1 || nb === 2) { - currentTab = nb; - } - var nb_copy = nb; - onEachLazy(document.getElementById("titles").childNodes, function(elem) { - if (nb_copy === 0) { - addClass(elem, "selected"); - } else { - removeClass(elem, "selected"); - } - nb_copy -= 1; - }); - onEachLazy(document.getElementById("results").childNodes, function(elem) { - if (nb === 0) { - elem.style.display = ""; - } else { - elem.style.display = "none"; - } - nb -= 1; - }); - } - - function putBackSearch(search_input) { - var search = getSearchElement(); - if (search_input.value !== "" && hasClass(search, "hidden")) { - showSearchResults(search); - if (browserSupportsHistoryApi()) { - var extra = "?search=" + encodeURIComponent(search_input.value); - history.replaceState(search_input.value, "", - getNakedUrl() + extra + window.location.hash); - } - document.title = searchTitle; - } - } - - function getSearchLoadingText() { - return "Loading search results..."; - } - - if (search_input) { - search_input.onfocus = function() { - putBackSearch(this); - }; - } - - var params = getQueryStringParams(); - if (params && params.search) { - var search = getSearchElement(); - search.innerHTML = "<h3 style=\"text-align: center;\">" + getSearchLoadingText() + "</h3>"; - showSearchResults(search); - } - var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0]; if (sidebar_menu) { sidebar_menu.onclick = function() { @@ -2933,30 +1436,6 @@ function hideThemeButtonState() { }); } - function addSearchOptions(crates) { - var elem = document.getElementById("crate-search"); - - if (!elem) { - return; - } - var savedCrate = getSettingValue("saved-filter-crate"); - for (var i = 0, len = crates.length; i < len; ++i) { - var option = document.createElement("option"); - option.value = crates[i]; - option.innerText = crates[i]; - elem.appendChild(option); - // Set the crate filter from saved storage, if the current page has the saved crate - // filter. - // - // If not, ignore the crate filter -- we want to support filtering for crates on sites - // like doc.rust-lang.org where the crates may differ from page to page while on the - // same domain. - if (crates[i] === savedCrate) { - elem.value = savedCrate; - } - } - }; - function buildHelperPopup() { var popup = document.createElement("aside"); addClass(popup, "hidden"); @@ -3014,55 +1493,14 @@ function hideThemeButtonState() { container.appendChild(div_infos); popup.appendChild(container); - insertAfter(popup, getSearchElement()); + insertAfter(popup, searchState.outputElement()); // So that it's only built once and then it'll do nothing when called! buildHelperPopup = function() {}; } - function loadScript(url) { - var script = document.createElement('script'); - script.src = url; - document.head.append(script); - } - - function setupSearchLoader() { - var searchLoaded = false; - function loadSearch() { - if (!searchLoaded) { - searchLoaded = true; - loadScript(window.searchJS); - } - } - - // `crates{version}.js` should always be loaded before this script, so we can use it safely. - addSearchOptions(window.ALL_CRATES); - addSidebarCrates(window.ALL_CRATES); - - search_input.addEventListener("focus", function() { - search_input.origPlaceholder = search_input.placeholder; - search_input.placeholder = "Type your search here."; - loadSearch(); - }); - search_input.addEventListener("blur", function() { - search_input.placeholder = search_input.origPlaceholder; - }); - search_input.removeAttribute('disabled'); - - var crateSearchDropDown = document.getElementById("crate-search"); - // `crateSearchDropDown` can be null in case there is only crate because in that case, the - // crate filter dropdown is removed. - if (crateSearchDropDown) { - crateSearchDropDown.addEventListener("focus", loadSearch); - } - var params = getQueryStringParams(); - if (params.search !== undefined) { - loadSearch(); - } - } - onHashChange(null); window.onhashchange = onHashChange; - setupSearchLoader(); + searchState.setup(); }()); function copy_path(but) { diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 585b7459bd7..427564cd779 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -133,7 +133,8 @@ h3.impl, h3.method, h4.method, h3.type, h4.type, h4.associatedconstant { margin-bottom: 10px; position: relative; } -h3.impl, h3.method, h3.type { +h3.impl, h3.method, h4.method.trait-impl, h3.type, +h4.type.trait-impl, h4.associatedconstant.trait-impl { padding-left: 15px; } @@ -655,7 +656,8 @@ a { display: initial; } -.in-band:hover > .anchor, .impl:hover > .anchor { +.in-band:hover > .anchor, .impl:hover > .anchor, .method.trait-impl:hover > .anchor, +.type.trait-impl:hover > .anchor, .associatedconstant.trait-impl:hover > .anchor { display: inline-block; position: absolute; } @@ -1054,12 +1056,6 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { margin-top: 3px; } -.enum > .toggle-wrapper + .docblock, .struct > .toggle-wrapper + .docblock { - margin-left: 30px; - margin-bottom: 20px; - margin-top: 5px; -} - .docblock > .section-header:first-child { margin-left: 15px; margin-top: 0; @@ -1069,30 +1065,10 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { left: -10px; } -.enum > .collapsed, .struct > .collapsed { - margin-bottom: 25px; -} - #main > .variant, #main > .structfield { display: block; } -.attributes { - display: block; - margin-top: 0px !important; - margin-right: 0px; - margin-bottom: 0px !important; - margin-left: 30px; -} -.toggle-attributes.collapsed { - margin-bottom: 0; -} -.impl-items > .toggle-attributes { - margin-left: 20px; -} -.impl-items .attributes { - font-weight: 500; -} :target > code { opacity: 1; @@ -1779,16 +1755,54 @@ div.name.expand::before { top: 2px; } -/* This part is to fix the "Expand attributes" part in the type declaration. */ -.type-decl > pre > .toggle-wrapper.toggle-attributes.top-attr { - margin-left: 0 !important; +/* The hideme class is used on summary tags that contain a span with + placeholder text shown only when the toggle is closed. For instance, + "Expand description" or "Show methods". */ +details.rustdoc-toggle > summary.hideme { + cursor: pointer; +} + +details.rustdoc-toggle > summary::-webkit-details-marker { + display: none; +} + +details.rustdoc-toggle > summary.hideme > span { + margin-left: 9px; } -.type-decl > pre > .docblock.attributes.top-attr { - margin-left: 1.8em !important; + +details.rustdoc-toggle > summary::before { + content: "[+]"; + font-weight: 300; + font-size: 0.8em; + letter-spacing: 1px; +} + +details.rustdoc-toggle > summary.hideme::before { + position: relative; } -.type-decl > pre > .toggle-attributes { - margin-left: 2.2em; + +details.rustdoc-toggle > summary:not(.hideme)::before { + float: left; } -.type-decl > pre > .docblock.attributes { - margin-left: 4em; + +/* When a "hideme" summary is open and the "Expand description" or "Show + methods" text is hidden, we want the [-] toggle that remains to not + affect the layout of the items to its right. To do that, we use + absolute positioning. Note that we also set position: relative + on the parent <details> to make this work properly. */ +details.rustdoc-toggle[open] > summary.hideme { + position: absolute; +} + +details.rustdoc-toggle[open] { + position: relative; +} + +details.rustdoc-toggle[open] > summary.hideme > span { + display: none; +} + +details.rustdoc-toggle[open] > summary::before { + content: "[−]"; + display: inline; } diff --git a/src/librustdoc/html/static/search.js b/src/librustdoc/html/static/search.js new file mode 100644 index 00000000000..538c811c710 --- /dev/null +++ b/src/librustdoc/html/static/search.js @@ -0,0 +1,1512 @@ +(function() { +// This mapping table should match the discriminants of +// `rustdoc::html::item_type::ItemType` type in Rust. +var itemTypes = ["mod", + "externcrate", + "import", + "struct", + "enum", + "fn", + "type", + "static", + "trait", + "impl", + "tymethod", + "method", + "structfield", + "variant", + "macro", + "primitive", + "associatedtype", + "constant", + "associatedconstant", + "union", + "foreigntype", + "keyword", + "existential", + "attr", + "derive", + "traitalias"]; + +// used for special search precedence +var TY_PRIMITIVE = itemTypes.indexOf("primitive"); +var TY_KEYWORD = itemTypes.indexOf("keyword"); + +// In the search display, allows to switch between tabs. +function printTab(nb) { + if (nb === 0 || nb === 1 || nb === 2) { + searchState.currentTab = nb; + } + var nb_copy = nb; + onEachLazy(document.getElementById("titles").childNodes, function(elem) { + if (nb_copy === 0) { + addClass(elem, "selected"); + } else { + removeClass(elem, "selected"); + } + nb_copy -= 1; + }); + onEachLazy(document.getElementById("results").childNodes, function(elem) { + if (nb === 0) { + elem.style.display = ""; + } else { + elem.style.display = "none"; + } + nb -= 1; + }); +} + +function removeEmptyStringsFromArray(x) { + for (var i = 0, len = x.length; i < len; ++i) { + if (x[i] === "") { + x.splice(i, 1); + i -= 1; + } + } +} + +/** + * A function to compute the Levenshtein distance between two strings + * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported + * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode + * This code is an unmodified version of the code written by Marco de Wit + * and was found at http://stackoverflow.com/a/18514751/745719 + */ +var levenshtein_row2 = []; +function levenshtein(s1, s2) { + if (s1 === s2) { + return 0; + } + var s1_len = s1.length, s2_len = s2.length; + if (s1_len && s2_len) { + var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2; + while (i1 < s1_len) { + row[i1] = ++i1; + } + while (i2 < s2_len) { + c2 = s2.charCodeAt(i2); + a = i2; + ++i2; + b = i2; + for (i1 = 0; i1 < s1_len; ++i1) { + c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0); + a = row[i1]; + b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c); + row[i1] = b; + } + } + return b; + } + return s1_len + s2_len; +} + +window.initSearch = function(rawSearchIndex) { + var MAX_LEV_DISTANCE = 3; + var MAX_RESULTS = 200; + var GENERICS_DATA = 1; + var NAME = 0; + var INPUTS_DATA = 0; + var OUTPUT_DATA = 1; + var NO_TYPE_FILTER = -1; + var currentResults, index, searchIndex; + var ALIASES = {}; + var params = searchState.getQueryStringParams(); + + // Populate search bar with query string search term when provided, + // but only if the input bar is empty. This avoid the obnoxious issue + // where you start trying to do a search, and the index loads, and + // suddenly your search is gone! + if (searchState.input.value === "") { + searchState.input.value = params.search || ""; + } + + /** + * Executes the query and builds an index of results + * @param {[Object]} query [The user query] + * @param {[type]} searchWords [The list of search words to query + * against] + * @param {[type]} filterCrates [Crate to search in if defined] + * @return {[type]} [A search index of results] + */ + function execQuery(query, searchWords, filterCrates) { + function itemTypeFromName(typename) { + for (var i = 0, len = itemTypes.length; i < len; ++i) { + if (itemTypes[i] === typename) { + return i; + } + } + return NO_TYPE_FILTER; + } + + var valLower = query.query.toLowerCase(), + val = valLower, + typeFilter = itemTypeFromName(query.type), + results = {}, results_in_args = {}, results_returned = {}, + split = valLower.split("::"); + + removeEmptyStringsFromArray(split); + + function transformResults(results, isType) { + var out = []; + for (var i = 0, len = results.length; i < len; ++i) { + if (results[i].id > -1) { + var obj = searchIndex[results[i].id]; + obj.lev = results[i].lev; + if (isType !== true || obj.type) { + var res = buildHrefAndPath(obj); + obj.displayPath = pathSplitter(res[0]); + obj.fullPath = obj.displayPath + obj.name; + // To be sure than it some items aren't considered as duplicate. + obj.fullPath += "|" + obj.ty; + obj.href = res[1]; + out.push(obj); + if (out.length >= MAX_RESULTS) { + break; + } + } + } + } + return out; + } + + function sortResults(results, isType) { + var ar = []; + for (var entry in results) { + if (hasOwnProperty(results, entry)) { + ar.push(results[entry]); + } + } + results = ar; + var i, len, result; + for (i = 0, len = results.length; i < len; ++i) { + result = results[i]; + result.word = searchWords[result.id]; + result.item = searchIndex[result.id] || {}; + } + // if there are no results then return to default and fail + if (results.length === 0) { + return []; + } + + results.sort(function(aaa, bbb) { + var a, b; + + // sort by exact match with regard to the last word (mismatch goes later) + a = (aaa.word !== val); + b = (bbb.word !== val); + if (a !== b) { return a - b; } + + // Sort by non levenshtein results and then levenshtein results by the distance + // (less changes required to match means higher rankings) + a = (aaa.lev); + b = (bbb.lev); + if (a !== b) { return a - b; } + + // sort by crate (non-current crate goes later) + a = (aaa.item.crate !== window.currentCrate); + b = (bbb.item.crate !== window.currentCrate); + if (a !== b) { return a - b; } + + // sort by item name length (longer goes later) + a = aaa.word.length; + b = bbb.word.length; + if (a !== b) { return a - b; } + + // sort by item name (lexicographically larger goes later) + a = aaa.word; + b = bbb.word; + if (a !== b) { return (a > b ? +1 : -1); } + + // sort by index of keyword in item name (no literal occurrence goes later) + a = (aaa.index < 0); + b = (bbb.index < 0); + if (a !== b) { return a - b; } + // (later literal occurrence, if any, goes later) + a = aaa.index; + b = bbb.index; + if (a !== b) { return a - b; } + + // special precedence for primitive and keyword pages + if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) || + (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) { + return -1; + } + if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) || + (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) { + return 1; + } + + // sort by description (no description goes later) + a = (aaa.item.desc === ""); + b = (bbb.item.desc === ""); + if (a !== b) { return a - b; } + + // sort by type (later occurrence in `itemTypes` goes later) + a = aaa.item.ty; + b = bbb.item.ty; + if (a !== b) { return a - b; } + + // sort by path (lexicographically larger goes later) + a = aaa.item.path; + b = bbb.item.path; + if (a !== b) { return (a > b ? +1 : -1); } + + // que sera, sera + return 0; + }); + + for (i = 0, len = results.length; i < len; ++i) { + var result = results[i]; + + // this validation does not make sense when searching by types + if (result.dontValidate) { + continue; + } + var name = result.item.name.toLowerCase(), + path = result.item.path.toLowerCase(), + parent = result.item.parent; + + if (isType !== true && + validateResult(name, path, split, parent) === false) + { + result.id = -1; + } + } + return transformResults(results); + } + + function extractGenerics(val) { + val = val.toLowerCase(); + if (val.indexOf("<") !== -1) { + var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">")); + return { + name: val.substring(0, val.indexOf("<")), + generics: values.split(/\s*,\s*/), + }; + } + return { + name: val, + generics: [], + }; + } + + function getObjectNameFromId(id) { + if (typeof id === "number") { + return searchIndex[id].name; + } + return id; + } + + function checkGenerics(obj, val) { + // The names match, but we need to be sure that all generics kinda + // match as well. + var tmp_lev, elem_name; + if (val.generics.length > 0) { + if (obj.length > GENERICS_DATA && + obj[GENERICS_DATA].length >= val.generics.length) { + var elems = Object.create(null); + var elength = object[GENERICS_DATA].length; + for (var x = 0; x < elength; ++x) { + elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1; + } + var total = 0; + var done = 0; + // We need to find the type that matches the most to remove it in order + // to move forward. + var vlength = val.generics.length; + for (x = 0; x < vlength; ++x) { + var lev = MAX_LEV_DISTANCE + 1; + var firstGeneric = getObjectNameFromId(val.generics[x]); + var match = null; + if (elems[firstGeneric]) { + match = firstGeneric; + lev = 0; + } else { + for (elem_name in elems) { + tmp_lev = levenshtein(elem_name, firstGeneric); + if (tmp_lev < lev) { + lev = tmp_lev; + match = elem_name; + } + } + } + if (match !== null) { + elems[match] -= 1; + if (elems[match] == 0) { + delete elems[match]; + } + total += lev; + done += 1; + } else { + return MAX_LEV_DISTANCE + 1; + } + } + return Math.ceil(total / done); + } + } + return MAX_LEV_DISTANCE + 1; + } + + // Check for type name and type generics (if any). + function checkType(obj, val, literalSearch) { + var lev_distance = MAX_LEV_DISTANCE + 1; + var len, x, firstGeneric; + if (obj[NAME] === val.name) { + if (literalSearch === true) { + if (val.generics && val.generics.length !== 0) { + if (obj.length > GENERICS_DATA && + obj[GENERICS_DATA].length >= val.generics.length) { + var elems = Object.create(null); + len = obj[GENERICS_DATA].length; + for (x = 0; x < len; ++x) { + elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1; + } + + var allFound = true; + len = val.generics.length; + for (x = 0; x < len; ++x) { + firstGeneric = getObjectNameFromId(val.generics[x]); + if (elems[firstGeneric]) { + elems[firstGeneric] -= 1; + } else { + allFound = false; + break; + } + } + if (allFound === true) { + return true; + } + } else { + return false; + } + } + return true; + } + // If the type has generics but don't match, then it won't return at this point. + // Otherwise, `checkGenerics` will return 0 and it'll return. + if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) { + var tmp_lev = checkGenerics(obj, val); + if (tmp_lev <= MAX_LEV_DISTANCE) { + return tmp_lev; + } + } else { + return 0; + } + } + // Names didn't match so let's check if one of the generic types could. + if (literalSearch === true) { + if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) { + return obj[GENERICS_DATA].some( + function(name) { + return name === val.name; + }); + } + return false; + } + lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance); + if (lev_distance <= MAX_LEV_DISTANCE) { + // The generics didn't match but the name kinda did so we give it + // a levenshtein distance value that isn't *this* good so it goes + // into the search results but not too high. + lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2); + } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) { + // We can check if the type we're looking for is inside the generics! + var olength = obj[GENERICS_DATA].length; + for (x = 0; x < olength; ++x) { + lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name), + lev_distance); + } + } + // Now whatever happens, the returned distance is "less good" so we should mark it + // as such, and so we add 1 to the distance to make it "less good". + return lev_distance + 1; + } + + function findArg(obj, val, literalSearch, typeFilter) { + var lev_distance = MAX_LEV_DISTANCE + 1; + + if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) { + var length = obj.type[INPUTS_DATA].length; + for (var i = 0; i < length; i++) { + var tmp = obj.type[INPUTS_DATA][i]; + if (typePassesFilter(typeFilter, tmp[1]) === false) { + continue; + } + tmp = checkType(tmp, val, literalSearch); + if (literalSearch === true) { + if (tmp === true) { + return true; + } + continue; + } + lev_distance = Math.min(tmp, lev_distance); + if (lev_distance === 0) { + return 0; + } + } + } + return literalSearch === true ? false : lev_distance; + } + + function checkReturned(obj, val, literalSearch, typeFilter) { + var lev_distance = MAX_LEV_DISTANCE + 1; + + if (obj && obj.type && obj.type.length > OUTPUT_DATA) { + var ret = obj.type[OUTPUT_DATA]; + if (typeof ret[0] === "string") { + ret = [ret]; + } + for (var x = 0, len = ret.length; x < len; ++x) { + var tmp = ret[x]; + if (typePassesFilter(typeFilter, tmp[1]) === false) { + continue; + } + tmp = checkType(tmp, val, literalSearch); + if (literalSearch === true) { + if (tmp === true) { + return true; + } + continue; + } + lev_distance = Math.min(tmp, lev_distance); + if (lev_distance === 0) { + return 0; + } + } + } + return literalSearch === true ? false : lev_distance; + } + + function checkPath(contains, lastElem, ty) { + if (contains.length === 0) { + return 0; + } + var ret_lev = MAX_LEV_DISTANCE + 1; + var path = ty.path.split("::"); + + if (ty.parent && ty.parent.name) { + path.push(ty.parent.name.toLowerCase()); + } + + var length = path.length; + var clength = contains.length; + if (clength > length) { + return MAX_LEV_DISTANCE + 1; + } + for (var i = 0; i < length; ++i) { + if (i + clength > length) { + break; + } + var lev_total = 0; + var aborted = false; + for (var x = 0; x < clength; ++x) { + var lev = levenshtein(path[i + x], contains[x]); + if (lev > MAX_LEV_DISTANCE) { + aborted = true; + break; + } + lev_total += lev; + } + if (aborted === false) { + ret_lev = Math.min(ret_lev, Math.round(lev_total / clength)); + } + } + return ret_lev; + } + + function typePassesFilter(filter, type) { + // No filter + if (filter <= NO_TYPE_FILTER) return true; + + // Exact match + if (filter === type) return true; + + // Match related items + var name = itemTypes[type]; + switch (itemTypes[filter]) { + case "constant": + return name === "associatedconstant"; + case "fn": + return name === "method" || name === "tymethod"; + case "type": + return name === "primitive" || name === "associatedtype"; + case "trait": + return name === "traitalias"; + } + + // No match + return false; + } + + function createAliasFromItem(item) { + return { + crate: item.crate, + name: item.name, + path: item.path, + desc: item.desc, + ty: item.ty, + parent: item.parent, + type: item.type, + is_alias: true, + }; + } + + function handleAliases(ret, query, filterCrates) { + // We separate aliases and crate aliases because we want to have current crate + // aliases to be before the others in the displayed results. + var aliases = []; + var crateAliases = []; + if (filterCrates !== undefined) { + if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) { + var query_aliases = ALIASES[filterCrates][query.search]; + var len = query_aliases.length; + for (var i = 0; i < len; ++i) { + aliases.push(createAliasFromItem(searchIndex[query_aliases[i]])); + } + } + } else { + Object.keys(ALIASES).forEach(function(crate) { + if (ALIASES[crate][query.search]) { + var pushTo = crate === window.currentCrate ? crateAliases : aliases; + var query_aliases = ALIASES[crate][query.search]; + var len = query_aliases.length; + for (var i = 0; i < len; ++i) { + pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]])); + } + } + }); + } + + var sortFunc = function(aaa, bbb) { + if (aaa.path < bbb.path) { + return 1; + } else if (aaa.path === bbb.path) { + return 0; + } + return -1; + }; + crateAliases.sort(sortFunc); + aliases.sort(sortFunc); + + var pushFunc = function(alias) { + alias.alias = query.raw; + var res = buildHrefAndPath(alias); + alias.displayPath = pathSplitter(res[0]); + alias.fullPath = alias.displayPath + alias.name; + alias.href = res[1]; + + ret.others.unshift(alias); + if (ret.others.length > MAX_RESULTS) { + ret.others.pop(); + } + }; + onEach(aliases, pushFunc); + onEach(crateAliases, pushFunc); + } + + // quoted values mean literal search + var nSearchWords = searchWords.length; + var i, it; + var ty; + var fullId; + var returned; + var in_args; + var len; + if ((val.charAt(0) === "\"" || val.charAt(0) === "'") && + val.charAt(val.length - 1) === val.charAt(0)) + { + val = extractGenerics(val.substr(1, val.length - 2)); + for (i = 0; i < nSearchWords; ++i) { + if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) { + continue; + } + in_args = findArg(searchIndex[i], val, true, typeFilter); + returned = checkReturned(searchIndex[i], val, true, typeFilter); + ty = searchIndex[i]; + fullId = ty.id; + + if (searchWords[i] === val.name + && typePassesFilter(typeFilter, searchIndex[i].ty) + && results[fullId] === undefined) { + results[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + if (in_args === true && results_in_args[fullId] === undefined) { + results_in_args[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + if (returned === true && results_returned[fullId] === undefined) { + results_returned[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + } + query.inputs = [val]; + query.output = val; + query.search = val; + // searching by type + } else if (val.search("->") > -1) { + var trimmer = function(s) { return s.trim(); }; + var parts = val.split("->").map(trimmer); + var input = parts[0]; + // sort inputs so that order does not matter + var inputs = input.split(",").map(trimmer).sort(); + for (i = 0, len = inputs.length; i < len; ++i) { + inputs[i] = extractGenerics(inputs[i]); + } + var output = extractGenerics(parts[1]); + + for (i = 0; i < nSearchWords; ++i) { + if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) { + continue; + } + var type = searchIndex[i].type; + ty = searchIndex[i]; + if (!type) { + continue; + } + fullId = ty.id; + + returned = checkReturned(ty, output, true, NO_TYPE_FILTER); + if (output.name === "*" || returned === true) { + in_args = false; + var is_module = false; + + if (input === "*") { + is_module = true; + } else { + var allFound = true; + for (it = 0, len = inputs.length; allFound === true && it < len; it++) { + allFound = checkType(type, inputs[it], true); + } + in_args = allFound; + } + if (in_args === true) { + results_in_args[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + if (returned === true) { + results_returned[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + if (is_module === true) { + results[fullId] = { + id: i, + index: -1, + dontValidate: true, + }; + } + } + } + query.inputs = inputs.map(function(input) { + return input.name; + }); + query.output = output.name; + } else { + query.inputs = [val]; + query.output = val; + query.search = val; + // gather matching search results up to a certain maximum + val = val.replace(/\_/g, ""); + + var valGenerics = extractGenerics(val); + + var paths = valLower.split("::"); + removeEmptyStringsFromArray(paths); + val = paths[paths.length - 1]; + var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1); + + var lev, j; + for (j = 0; j < nSearchWords; ++j) { + ty = searchIndex[j]; + if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) { + continue; + } + var lev_add = 0; + if (paths.length > 1) { + lev = checkPath(contains, paths[paths.length - 1], ty); + if (lev > MAX_LEV_DISTANCE) { + continue; + } else if (lev > 0) { + lev_add = lev / 10; + } + } + + returned = MAX_LEV_DISTANCE + 1; + in_args = MAX_LEV_DISTANCE + 1; + var index = -1; + // we want lev results to go lower than others + lev = MAX_LEV_DISTANCE + 1; + fullId = ty.id; + + if (searchWords[j].indexOf(split[i]) > -1 || + searchWords[j].indexOf(val) > -1 || + ty.normalizedName.indexOf(val) > -1) + { + // filter type: ... queries + if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) { + index = ty.normalizedName.indexOf(val); + } + } + if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) { + if (typePassesFilter(typeFilter, ty.ty) === false) { + lev = MAX_LEV_DISTANCE + 1; + } else { + lev += 1; + } + } + in_args = findArg(ty, valGenerics, false, typeFilter); + returned = checkReturned(ty, valGenerics, false, typeFilter); + + lev += lev_add; + if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) { + if (val.length < 6) { + lev -= 1; + } else { + lev = 0; + } + } + if (in_args <= MAX_LEV_DISTANCE) { + if (results_in_args[fullId] === undefined) { + results_in_args[fullId] = { + id: j, + index: index, + lev: in_args, + }; + } + results_in_args[fullId].lev = + Math.min(results_in_args[fullId].lev, in_args); + } + if (returned <= MAX_LEV_DISTANCE) { + if (results_returned[fullId] === undefined) { + results_returned[fullId] = { + id: j, + index: index, + lev: returned, + }; + } + results_returned[fullId].lev = + Math.min(results_returned[fullId].lev, returned); + } + if (index !== -1 || lev <= MAX_LEV_DISTANCE) { + if (index !== -1 && paths.length < 2) { + lev = 0; + } + if (results[fullId] === undefined) { + results[fullId] = { + id: j, + index: index, + lev: lev, + }; + } + results[fullId].lev = Math.min(results[fullId].lev, lev); + } + } + } + + var ret = { + "in_args": sortResults(results_in_args, true), + "returned": sortResults(results_returned, true), + "others": sortResults(results), + }; + handleAliases(ret, query, filterCrates); + return ret; + } + + /** + * Validate performs the following boolean logic. For example: + * "File::open" will give IF A PARENT EXISTS => ("file" && "open") + * exists in (name || path || parent) OR => ("file" && "open") exists in + * (name || path ) + * + * This could be written functionally, but I wanted to minimise + * functions on stack. + * + * @param {[string]} name [The name of the result] + * @param {[string]} path [The path of the result] + * @param {[string]} keys [The keys to be used (["file", "open"])] + * @param {[object]} parent [The parent of the result] + * @return {[boolean]} [Whether the result is valid or not] + */ + function validateResult(name, path, keys, parent) { + for (var i = 0, len = keys.length; i < len; ++i) { + // each check is for validation so we negate the conditions and invalidate + if (!( + // check for an exact name match + name.indexOf(keys[i]) > -1 || + // then an exact path match + path.indexOf(keys[i]) > -1 || + // next if there is a parent, check for exact parent match + (parent !== undefined && parent.name !== undefined && + parent.name.toLowerCase().indexOf(keys[i]) > -1) || + // lastly check to see if the name was a levenshtein match + levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) { + return false; + } + } + return true; + } + + function getQuery(raw) { + var matches, type, query; + query = raw; + + matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i); + if (matches) { + type = matches[1].replace(/^const$/, "constant"); + query = query.substring(matches[0].length); + } + + return { + raw: raw, + query: query, + type: type, + id: query + type + }; + } + + function initSearchNav() { + var hoverTimeout; + + var click_func = function(e) { + var el = e.target; + // to retrieve the real "owner" of the event. + while (el.tagName !== "TR") { + el = el.parentNode; + } + var dst = e.target.getElementsByTagName("a"); + if (dst.length < 1) { + return; + } + dst = dst[0]; + if (window.location.pathname === dst.pathname) { + searchState.hideResults(); + document.location.href = dst.href; + } + }; + var mouseover_func = function(e) { + if (searchState.mouseMovedAfterSearch) { + var el = e.target; + // to retrieve the real "owner" of the event. + while (el.tagName !== "TR") { + el = el.parentNode; + } + clearTimeout(hoverTimeout); + hoverTimeout = setTimeout(function() { + onEachLazy(document.getElementsByClassName("search-results"), function(e) { + onEachLazy(e.getElementsByClassName("result"), function(i_e) { + removeClass(i_e, "highlighted"); + }); + }); + addClass(el, "highlighted"); + }, 20); + } + }; + onEachLazy(document.getElementsByClassName("search-results"), function(e) { + onEachLazy(e.getElementsByClassName("result"), function(i_e) { + i_e.onclick = click_func; + i_e.onmouseover = mouseover_func; + }); + }); + + searchState.input.onkeydown = function(e) { + // "actives" references the currently highlighted item in each search tab. + // Each array in "actives" represents a tab. + var actives = [[], [], []]; + // "current" is used to know which tab we're looking into. + var current = 0; + onEachLazy(document.getElementById("results").childNodes, function(e) { + onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) { + actives[current].push(h_e); + }); + current += 1; + }); + + var currentTab = searchState.currentTab; + if (e.which === 38) { // up + if (e.ctrlKey) { // Going through result tabs. + printTab(currentTab > 0 ? currentTab - 1 : 2); + } else { + if (!actives[currentTab].length || + !actives[currentTab][0].previousElementSibling) { + return; + } + addClass(actives[currentTab][0].previousElementSibling, "highlighted"); + removeClass(actives[currentTab][0], "highlighted"); + } + e.preventDefault(); + } else if (e.which === 40) { // down + if (e.ctrlKey) { // Going through result tabs. + printTab(currentTab > 1 ? 0 : currentTab + 1); + } else if (!actives[currentTab].length) { + var results = document.getElementById("results").childNodes; + if (results.length > 0) { + var res = results[currentTab].getElementsByClassName("result"); + if (res.length > 0) { + addClass(res[0], "highlighted"); + } + } + } else if (actives[currentTab][0].nextElementSibling) { + addClass(actives[currentTab][0].nextElementSibling, "highlighted"); + removeClass(actives[currentTab][0], "highlighted"); + } + e.preventDefault(); + } else if (e.which === 13) { // return + if (actives[currentTab].length) { + document.location.href = + actives[currentTab][0].getElementsByTagName("a")[0].href; + } + } else if (e.which === 16) { // shift + // Does nothing, it's just to avoid losing "focus" on the highlighted element. + } else if (actives[currentTab].length > 0) { + removeClass(actives[currentTab][0], "highlighted"); + } + }; + } + + function buildHrefAndPath(item) { + var displayPath; + var href; + var type = itemTypes[item.ty]; + var name = item.name; + var path = item.path; + + if (type === "mod") { + displayPath = path + "::"; + href = window.rootPath + path.replace(/::/g, "/") + "/" + + name + "/index.html"; + } else if (type === "primitive" || type === "keyword") { + displayPath = ""; + href = window.rootPath + path.replace(/::/g, "/") + + "/" + type + "." + name + ".html"; + } else if (type === "externcrate") { + displayPath = ""; + href = window.rootPath + name + "/index.html"; + } else if (item.parent !== undefined) { + var myparent = item.parent; + var anchor = "#" + type + "." + name; + var parentType = itemTypes[myparent.ty]; + var pageType = parentType; + var pageName = myparent.name; + + if (parentType === "primitive") { + displayPath = myparent.name + "::"; + } else if (type === "structfield" && parentType === "variant") { + // Structfields belonging to variants are special: the + // final path element is the enum name. + var enumNameIdx = item.path.lastIndexOf("::"); + var enumName = item.path.substr(enumNameIdx + 2); + path = item.path.substr(0, enumNameIdx); + displayPath = path + "::" + enumName + "::" + myparent.name + "::"; + anchor = "#variant." + myparent.name + ".field." + name; + pageType = "enum"; + pageName = enumName; + } else { + displayPath = path + "::" + myparent.name + "::"; + } + href = window.rootPath + path.replace(/::/g, "/") + + "/" + pageType + + "." + pageName + + ".html" + anchor; + } else { + displayPath = item.path + "::"; + href = window.rootPath + item.path.replace(/::/g, "/") + + "/" + type + "." + name + ".html"; + } + return [displayPath, href]; + } + + function escape(content) { + var h1 = document.createElement("h1"); + h1.textContent = content; + return h1.innerHTML; + } + + function pathSplitter(path) { + var tmp = "<span>" + path.replace(/::/g, "::</span><span>"); + if (tmp.endsWith("<span>")) { + return tmp.slice(0, tmp.length - 6); + } + return tmp; + } + + function addTab(array, query, display) { + var extraStyle = ""; + if (display === false) { + extraStyle = " style=\"display: none;\""; + } + + var output = ""; + var duplicates = {}; + var length = 0; + if (array.length > 0) { + output = "<table class=\"search-results\"" + extraStyle + ">"; + + array.forEach(function(item) { + var name, type; + + name = item.name; + type = itemTypes[item.ty]; + + if (item.is_alias !== true) { + if (duplicates[item.fullPath]) { + return; + } + duplicates[item.fullPath] = true; + } + length += 1; + + output += "<tr class=\"" + type + " result\"><td>" + + "<a href=\"" + item.href + "\">" + + (item.is_alias === true ? + ("<span class=\"alias\"><b>" + item.alias + " </b></span><span " + + "class=\"grey\"><i> - see </i></span>") : "") + + item.displayPath + "<span class=\"" + type + "\">" + + name + "</span></a></td><td>" + + "<a href=\"" + item.href + "\">" + + "<span class=\"desc\">" + item.desc + + " </span></a></td></tr>"; + }); + output += "</table>"; + } else { + output = "<div class=\"search-failed\"" + extraStyle + ">No results :(<br/>" + + "Try on <a href=\"https://duckduckgo.com/?q=" + + encodeURIComponent("rust " + query.query) + + "\">DuckDuckGo</a>?<br/><br/>" + + "Or try looking in one of these:<ul><li>The <a " + + "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " + + " for technical details about the language.</li><li><a " + + "href=\"https://doc.rust-lang.org/rust-by-example/index.html\">Rust By " + + "Example</a> for expository code examples.</a></li><li>The <a " + + "href=\"https://doc.rust-lang.org/book/index.html\">Rust Book</a> for " + + "introductions to language features and the language itself.</li><li><a " + + "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" + + " <a href=\"https://crates.io/\">crates.io</a>.</li></ul></div>"; + } + return [output, length]; + } + + function makeTabHeader(tabNb, text, nbElems) { + if (searchState.currentTab === tabNb) { + return "<button class=\"selected\">" + text + + " <div class=\"count\">(" + nbElems + ")</div></button>"; + } + return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>"; + } + + function showResults(results) { + var search = searchState.outputElement(); + if (results.others.length === 1 + && getSettingValue("go-to-only-result") === "true" + // By default, the search DOM element is "empty" (meaning it has no children not + // text content). Once a search has been run, it won't be empty, even if you press + // ESC or empty the search input (which also "cancels" the search). + && (!search.firstChild || search.firstChild.innerText !== searchState.loadingText)) + { + var elem = document.createElement("a"); + elem.href = results.others[0].href; + elem.style.display = "none"; + // For firefox, we need the element to be in the DOM so it can be clicked. + document.body.appendChild(elem); + elem.click(); + return; + } + var query = getQuery(searchState.input.value); + + currentResults = query.id; + + var ret_others = addTab(results.others, query); + var ret_in_args = addTab(results.in_args, query, false); + var ret_returned = addTab(results.returned, query, false); + + // Navigate to the relevant tab if the current tab is empty, like in case users search + // for "-> String". If they had selected another tab previously, they have to click on + // it again. + var currentTab = searchState.currentTab; + if ((currentTab === 0 && ret_others[1] === 0) || + (currentTab === 1 && ret_in_args[1] === 0) || + (currentTab === 2 && ret_returned[1] === 0)) { + if (ret_others[1] !== 0) { + currentTab = 0; + } else if (ret_in_args[1] !== 0) { + currentTab = 1; + } else if (ret_returned[1] !== 0) { + currentTab = 2; + } + } + + var output = "<h1>Results for " + escape(query.query) + + (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" + + "<div id=\"titles\">" + + makeTabHeader(0, "In Names", ret_others[1]) + + makeTabHeader(1, "In Parameters", ret_in_args[1]) + + makeTabHeader(2, "In Return Types", ret_returned[1]) + + "</div><div id=\"results\">" + + ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>"; + + search.innerHTML = output; + searchState.showResults(search); + initSearchNav(); + var elems = document.getElementById("titles").childNodes; + elems[0].onclick = function() { printTab(0); }; + elems[1].onclick = function() { printTab(1); }; + elems[2].onclick = function() { printTab(2); }; + printTab(currentTab); + } + + function execSearch(query, searchWords, filterCrates) { + function getSmallest(arrays, positions, notDuplicates) { + var start = null; + + for (var it = 0, len = positions.length; it < len; ++it) { + if (arrays[it].length > positions[it] && + (start === null || start > arrays[it][positions[it]].lev) && + !notDuplicates[arrays[it][positions[it]].fullPath]) { + start = arrays[it][positions[it]].lev; + } + } + return start; + } + + function mergeArrays(arrays) { + var ret = []; + var positions = []; + var notDuplicates = {}; + + for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) { + positions.push(0); + } + while (ret.length < MAX_RESULTS) { + var smallest = getSmallest(arrays, positions, notDuplicates); + + if (smallest === null) { + break; + } + for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) { + if (arrays[x].length > positions[x] && + arrays[x][positions[x]].lev === smallest && + !notDuplicates[arrays[x][positions[x]].fullPath]) { + ret.push(arrays[x][positions[x]]); + notDuplicates[arrays[x][positions[x]].fullPath] = true; + positions[x] += 1; + } + } + } + return ret; + } + + var queries = query.raw.split(","); + var results = { + "in_args": [], + "returned": [], + "others": [], + }; + + for (var i = 0, len = queries.length; i < len; ++i) { + query = queries[i].trim(); + if (query.length !== 0) { + var tmp = execQuery(getQuery(query), searchWords, filterCrates); + + results.in_args.push(tmp.in_args); + results.returned.push(tmp.returned); + results.others.push(tmp.others); + } + } + if (queries.length > 1) { + return { + "in_args": mergeArrays(results.in_args), + "returned": mergeArrays(results.returned), + "others": mergeArrays(results.others), + }; + } + return { + "in_args": results.in_args[0], + "returned": results.returned[0], + "others": results.others[0], + }; + } + + function getFilterCrates() { + var elem = document.getElementById("crate-search"); + + if (elem && elem.value !== "All crates" && hasOwnProperty(rawSearchIndex, elem.value)) { + return elem.value; + } + return undefined; + } + + function search(e, forced) { + var params = searchState.getQueryStringParams(); + var query = getQuery(searchState.input.value.trim()); + + if (e) { + e.preventDefault(); + } + + if (query.query.length === 0) { + return; + } + if (forced !== true && query.id === currentResults) { + if (query.query.length > 0) { + searchState.putBackSearch(searchState.input); + } + return; + } + + // Update document title to maintain a meaningful browser history + searchState.title = "Results for " + query.query + " - Rust"; + + // Because searching is incremental by character, only the most + // recent search query is added to the browser history. + if (searchState.browserSupportsHistoryApi()) { + var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) + + window.location.hash; + if (!history.state && !params.search) { + history.pushState(query, "", newURL); + } else { + history.replaceState(query, "", newURL); + } + } + + var filterCrates = getFilterCrates(); + showResults(execSearch(query, index, filterCrates)); + } + + function buildIndex(rawSearchIndex) { + searchIndex = []; + var searchWords = []; + var i, word; + var currentIndex = 0; + var id = 0; + + for (var crate in rawSearchIndex) { + if (!hasOwnProperty(rawSearchIndex, crate)) { continue; } + + var crateSize = 0; + + searchWords.push(crate); + var normalizedName = crate.indexOf("_") === -1 + ? crate + : crate.replace(/_/g, ""); + // This object should have exactly the same set of fields as the "row" + // object defined below. Your JavaScript runtime will thank you. + // https://mathiasbynens.be/notes/shapes-ics + var crateRow = { + crate: crate, + ty: 1, // == ExternCrate + name: crate, + path: "", + desc: rawSearchIndex[crate].doc, + parent: undefined, + type: null, + id: id, + normalizedName: normalizedName, + }; + id += 1; + searchIndex.push(crateRow); + currentIndex += 1; + + // an array of (Number) item types + var itemTypes = rawSearchIndex[crate].t; + // an array of (String) item names + var itemNames = rawSearchIndex[crate].n; + // an array of (String) full paths (or empty string for previous path) + var itemPaths = rawSearchIndex[crate].q; + // an array of (String) descriptions + var itemDescs = rawSearchIndex[crate].d; + // an array of (Number) the parent path index + 1 to `paths`, or 0 if none + var itemParentIdxs = rawSearchIndex[crate].i; + // an array of (Object | null) the type of the function, if any + var itemFunctionSearchTypes = rawSearchIndex[crate].f; + // an array of [(Number) item type, + // (String) name] + var paths = rawSearchIndex[crate].p; + // a array of [(String) alias name + // [Number] index to items] + var aliases = rawSearchIndex[crate].a; + + // convert `rawPaths` entries into object form + var len = paths.length; + for (i = 0; i < len; ++i) { + paths[i] = {ty: paths[i][0], name: paths[i][1]}; + } + + // convert `item*` into an object form, and construct word indices. + // + // before any analysis is performed lets gather the search terms to + // search against apart from the rest of the data. This is a quick + // operation that is cached for the life of the page state so that + // all other search operations have access to this cached data for + // faster analysis operations + len = itemTypes.length; + var lastPath = ""; + for (i = 0; i < len; ++i) { + // This object should have exactly the same set of fields as the "crateRow" + // object defined above. + if (typeof itemNames[i] === "string") { + word = itemNames[i].toLowerCase(); + searchWords.push(word); + } else { + word = ""; + searchWords.push(""); + } + var normalizedName = word.indexOf("_") === -1 + ? word + : word.replace(/_/g, ""); + var row = { + crate: crate, + ty: itemTypes[i], + name: itemNames[i], + path: itemPaths[i] ? itemPaths[i] : lastPath, + desc: itemDescs[i], + parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined, + type: itemFunctionSearchTypes[i], + id: id, + normalizedName: normalizedName, + }; + id += 1; + searchIndex.push(row); + lastPath = row.path; + crateSize += 1; + } + + if (aliases) { + ALIASES[crate] = {}; + var j, local_aliases; + for (var alias_name in aliases) { + if (!aliases.hasOwnProperty(alias_name)) { continue; } + + if (!ALIASES[crate].hasOwnProperty(alias_name)) { + ALIASES[crate][alias_name] = []; + } + local_aliases = aliases[alias_name]; + for (j = 0, len = local_aliases.length; j < len; ++j) { + ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex); + } + } + } + currentIndex += crateSize; + } + return searchWords; + } + + function registerSearchEvents() { + var searchAfter500ms = function() { + searchState.clearInputTimeout(); + if (searchState.input.value.length === 0) { + if (searchState.browserSupportsHistoryApi()) { + history.replaceState("", window.currentCrate + " - Rust", + getNakedUrl() + window.location.hash); + } + searchState.hideResults(); + } else { + searchState.timeout = setTimeout(search, 500); + } + }; + searchState.input.onkeyup = searchAfter500ms; + searchState.input.oninput = searchAfter500ms; + document.getElementsByClassName("search-form")[0].onsubmit = function(e) { + e.preventDefault(); + searchState.clearInputTimeout(); + search(); + }; + searchState.input.onchange = function(e) { + if (e.target !== document.activeElement) { + // To prevent doing anything when it's from a blur event. + return; + } + // Do NOT e.preventDefault() here. It will prevent pasting. + searchState.clearInputTimeout(); + // zero-timeout necessary here because at the time of event handler execution the + // pasted content is not in the input field yet. Shouldn’t make any difference for + // change, though. + setTimeout(search, 0); + }; + searchState.input.onpaste = searchState.input.onchange; + + var selectCrate = document.getElementById("crate-search"); + if (selectCrate) { + selectCrate.onchange = function() { + updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value); + search(undefined, true); + }; + } + + // Push and pop states are used to add search results to the browser + // history. + if (searchState.browserSupportsHistoryApi()) { + // Store the previous <title> so we can revert back to it later. + var previousTitle = document.title; + + window.addEventListener("popstate", function(e) { + var params = searchState.getQueryStringParams(); + // Revert to the previous title manually since the History + // API ignores the title parameter. + document.title = previousTitle; + // When browsing forward to search results the previous + // search will be repeated, so the currentResults are + // cleared to ensure the search is successful. + currentResults = null; + // Synchronize search bar with query string state and + // perform the search. This will empty the bar if there's + // nothing there, which lets you really go back to a + // previous state with nothing in the bar. + if (params.search && params.search.length > 0) { + searchState.input.value = params.search; + // Some browsers fire "onpopstate" for every page load + // (Chrome), while others fire the event only when actually + // popping a state (Firefox), which is why search() is + // called both here and at the end of the startSearch() + // function. + search(e); + } else { + searchState.input.value = ""; + // When browsing back from search results the main page + // visibility must be reset. + searchState.hideResults(); + } + }); + } + + // This is required in firefox to avoid this problem: Navigating to a search result + // with the keyboard, hitting enter, and then hitting back would take you back to + // the doc page, rather than the search that should overlay it. + // This was an interaction between the back-forward cache and our handlers + // that try to sync state between the URL and the search input. To work around it, + // do a small amount of re-init on page show. + window.onpageshow = function(){ + var qSearch = searchState.getQueryStringParams().search; + if (searchState.input.value === "" && qSearch) { + searchState.input.value = qSearch; + } + search(); + }; + } + + index = buildIndex(rawSearchIndex); + registerSearchEvents(); + // If there's a search term in the URL, execute the search now. + if (searchState.getQueryStringParams().search) { + search(); + } +}; + +if (window.searchIndex !== undefined) { + initSearch(window.searchIndex); +} + +})(); diff --git a/src/librustdoc/html/static/storage.js b/src/librustdoc/html/static/storage.js index c68128516d2..2ed87fdedae 100644 --- a/src/librustdoc/html/static/storage.js +++ b/src/librustdoc/html/static/storage.js @@ -1,6 +1,5 @@ // From rust: /* global resourcesSuffix */ - var darkThemes = ["dark", "ayu"]; window.currentTheme = document.getElementById("themeStyle"); window.mainTheme = document.getElementById("mainThemeStyle"); diff --git a/src/librustdoc/html/static/themes/ayu.css b/src/librustdoc/html/static/themes/ayu.css index b24f4035ca8..5f6f3d66e57 100644 --- a/src/librustdoc/html/static/themes/ayu.css +++ b/src/librustdoc/html/static/themes/ayu.css @@ -224,7 +224,9 @@ a { color: #39AFD7; } -.collapse-toggle { +.collapse-toggle, +details.rustdoc-toggle > summary.hideme > span, +details.rustdoc-toggle > summary::before { color: #999; } diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css index e863ed03f51..2ce6cf4cc45 100644 --- a/src/librustdoc/html/static/themes/dark.css +++ b/src/librustdoc/html/static/themes/dark.css @@ -186,7 +186,9 @@ a.test-arrow { color: #dedede; } -.collapse-toggle { +.collapse-toggle, +details.rustdoc-toggle > summary.hideme > span, +details.rustdoc-toggle > summary::before { color: #999; } diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css index 9335dd96d29..31b3562cfcb 100644 --- a/src/librustdoc/html/static/themes/light.css +++ b/src/librustdoc/html/static/themes/light.css @@ -184,7 +184,9 @@ a.test-arrow { color: #f5f5f5; } -.collapse-toggle { +.collapse-toggle, +details.rustdoc-toggle > summary.hideme > span, +details.rustdoc-toggle > summary::before { color: #999; } diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index b3ac865d55e..2b73bd5d52e 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -24,6 +24,9 @@ crate static NORMALIZE_CSS: &str = include_str!("static/normalize.css"); /// including search behavior and docblock folding, among others. crate static MAIN_JS: &str = include_str!("static/main.js"); +/// The file contents of `search.js`, which contains the search behavior. +crate static SEARCH_JS: &str = include_str!("static/search.js"); + /// The file contents of `settings.js`, which contains the JavaScript used to handle the settings /// page. crate static SETTINGS_JS: &str = include_str!("static/settings.js"); diff --git a/src/librustdoc/html/tests.rs b/src/librustdoc/html/tests.rs new file mode 100644 index 00000000000..5d537dabd0c --- /dev/null +++ b/src/librustdoc/html/tests.rs @@ -0,0 +1,44 @@ +use crate::html::format::href_relative_parts; + +fn assert_relative_path(expected: &[&str], relative_to_fqp: &[&str], fqp: &[&str]) { + let relative_to_fqp: Vec<String> = relative_to_fqp.iter().copied().map(String::from).collect(); + let fqp: Vec<String> = fqp.iter().copied().map(String::from).collect(); + assert_eq!(expected, href_relative_parts(&fqp, &relative_to_fqp)); +} + +#[test] +fn href_relative_parts_basic() { + let relative_to_fqp = &["std", "vec"]; + let fqp = &["std", "iter"]; + assert_relative_path(&["..", "iter"], relative_to_fqp, fqp); +} +#[test] +fn href_relative_parts_parent_module() { + let relative_to_fqp = &["std", "vec"]; + let fqp = &["std"]; + assert_relative_path(&[".."], relative_to_fqp, fqp); +} +#[test] +fn href_relative_parts_different_crate() { + let relative_to_fqp = &["std", "vec"]; + let fqp = &["core", "iter"]; + assert_relative_path(&["..", "..", "core", "iter"], relative_to_fqp, fqp); +} +#[test] +fn href_relative_parts_same_module() { + let relative_to_fqp = &["std", "vec"]; + let fqp = &["std", "vec"]; + assert_relative_path(&[], relative_to_fqp, fqp); +} +#[test] +fn href_relative_parts_child_module() { + let relative_to_fqp = &["std"]; + let fqp = &["std", "vec"]; + assert_relative_path(&["vec"], relative_to_fqp, fqp); +} +#[test] +fn href_relative_parts_root() { + let relative_to_fqp = &[]; + let fqp = &["std"]; + assert_relative_path(&["std"], relative_to_fqp, fqp); +} diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index b9c4bbdceb2..2a51d78f64a 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -289,7 +289,13 @@ fn opts() -> Vec<RustcOptGroup> { stable("cfg", |o| o.optmulti("", "cfg", "pass a --cfg to rustc", "")), stable("extern", |o| o.optmulti("", "extern", "pass an --extern to rustc", "NAME[=PATH]")), unstable("extern-html-root-url", |o| { - o.optmulti("", "extern-html-root-url", "base URL to use for dependencies", "NAME=URL") + o.optmulti( + "", + "extern-html-root-url", + "base URL to use for dependencies; for example, \ + \"std=/doc\" links std::vec::Vec to /doc/std/vec/struct.Vec.html", + "NAME=URL", + ) }), stable("plugin-path", |o| o.optmulti("", "plugin-path", "removed", "DIR")), stable("C", |o| { diff --git a/src/librustdoc/passes/bare_urls.rs b/src/librustdoc/passes/bare_urls.rs index 3f2e1c68c55..ac0d74cbbb9 100644 --- a/src/librustdoc/passes/bare_urls.rs +++ b/src/librustdoc/passes/bare_urls.rs @@ -16,7 +16,7 @@ crate const CHECK_BARE_URLS: Pass = Pass { description: "detects URLs that are not hyperlinks", }; -const URL_REGEX: SyncLazy<Regex> = SyncLazy::new(|| { +static URL_REGEX: SyncLazy<Regex> = SyncLazy::new(|| { Regex::new(concat!( r"https?://", // url scheme r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains @@ -73,6 +73,7 @@ impl<'a, 'tcx> DocFolder for BareUrlsLinter<'a, 'tcx> { .unwrap_or(item.span.inner()); cx.tcx.struct_span_lint_hir(crate::lint::BARE_URLS, hir_id, sp, |lint| { lint.build(msg) + .note("bare URLs are not automatically turned into clickable links") .span_suggestion( sp, "use an automatic link instead", diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 4bc7544e33d..837cd034b65 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -39,13 +39,16 @@ use crate::passes::Pass; use super::span_of_attrs; +mod early; +crate use early::IntraLinkCrateLoader; + crate const COLLECT_INTRA_DOC_LINKS: Pass = Pass { name: "collect-intra-doc-links", run: collect_intra_doc_links, description: "resolves intra-doc links", }; -crate fn collect_intra_doc_links(krate: Crate, cx: &mut DocContext<'_>) -> Crate { +fn collect_intra_doc_links(krate: Crate, cx: &mut DocContext<'_>) -> Crate { LinkCollector { cx, mod_ids: Vec::new(), @@ -892,6 +895,117 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } } +enum PreprocessingError<'a> { + Anchor(AnchorFailure), + Disambiguator(Range<usize>, String), + Resolution(ResolutionFailure<'a>, String, Option<Disambiguator>), +} + +impl From<AnchorFailure> for PreprocessingError<'_> { + fn from(err: AnchorFailure) -> Self { + Self::Anchor(err) + } +} + +struct PreprocessingInfo { + path_str: String, + disambiguator: Option<Disambiguator>, + extra_fragment: Option<String>, + link_text: String, +} + +/// Returns: +/// - `None` if the link should be ignored. +/// - `Some(Err)` if the link should emit an error +/// - `Some(Ok)` if the link is valid +/// +/// `link_buffer` is needed for lifetime reasons; it will always be overwritten and the contents ignored. +fn preprocess_link<'a>( + ori_link: &'a MarkdownLink, +) -> Option<Result<PreprocessingInfo, PreprocessingError<'a>>> { + // [] is mostly likely not supposed to be a link + if ori_link.link.is_empty() { + return None; + } + + // Bail early for real links. + if ori_link.link.contains('/') { + return None; + } + + let stripped = ori_link.link.replace("`", ""); + let mut parts = stripped.split('#'); + + let link = parts.next().unwrap(); + if link.trim().is_empty() { + // This is an anchor to an element of the current page, nothing to do in here! + return None; + } + let extra_fragment = parts.next(); + if parts.next().is_some() { + // A valid link can't have multiple #'s + return Some(Err(AnchorFailure::MultipleAnchors.into())); + } + + // Parse and strip the disambiguator from the link, if present. + let (path_str, disambiguator) = match Disambiguator::from_str(&link) { + Ok(Some((d, path))) => (path.trim(), Some(d)), + Ok(None) => (link.trim(), None), + Err((err_msg, relative_range)) => { + // Only report error if we would not have ignored this link. See issue #83859. + if !should_ignore_link_with_disambiguators(link) { + let no_backticks_range = range_between_backticks(&ori_link); + let disambiguator_range = (no_backticks_range.start + relative_range.start) + ..(no_backticks_range.start + relative_range.end); + return Some(Err(PreprocessingError::Disambiguator(disambiguator_range, err_msg))); + } else { + return None; + } + } + }; + + if should_ignore_link(path_str) { + return None; + } + + // We stripped `()` and `!` when parsing the disambiguator. + // Add them back to be displayed, but not prefix disambiguators. + let link_text = + disambiguator.map(|d| d.display_for(path_str)).unwrap_or_else(|| path_str.to_owned()); + + // Strip generics from the path. + let path_str = if path_str.contains(['<', '>'].as_slice()) { + match strip_generics_from_path(&path_str) { + Ok(path) => path, + Err(err_kind) => { + debug!("link has malformed generics: {}", path_str); + return Some(Err(PreprocessingError::Resolution( + err_kind, + path_str.to_owned(), + disambiguator, + ))); + } + } + } else { + path_str.to_owned() + }; + + // Sanity check to make sure we don't have any angle brackets after stripping generics. + assert!(!path_str.contains(['<', '>'].as_slice())); + + // The link is not an intra-doc link if it still contains spaces after stripping generics. + if path_str.contains(' ') { + return None; + } + + Some(Ok(PreprocessingInfo { + path_str, + disambiguator, + extra_fragment: extra_fragment.map(String::from), + link_text, + })) +} + impl LinkCollector<'_, '_> { /// This is the entry point for resolving an intra-doc link. /// @@ -907,16 +1021,6 @@ impl LinkCollector<'_, '_> { ) -> Option<ItemLink> { trace!("considering link '{}'", ori_link.link); - // Bail early for real links. - if ori_link.link.contains('/') { - return None; - } - - // [] is mostly likely not supposed to be a link - if ori_link.link.is_empty() { - return None; - } - let diag_info = DiagnosticInfo { item, dox, @@ -924,47 +1028,29 @@ impl LinkCollector<'_, '_> { link_range: ori_link.range.clone(), }; - let link = ori_link.link.replace("`", ""); - let no_backticks_range = range_between_backticks(&ori_link); - let parts = link.split('#').collect::<Vec<_>>(); - let (link, extra_fragment) = if parts.len() > 2 { - // A valid link can't have multiple #'s - anchor_failure(self.cx, diag_info, AnchorFailure::MultipleAnchors); - return None; - } else if parts.len() == 2 { - if parts[0].trim().is_empty() { - // This is an anchor to an element of the current page, nothing to do in here! - return None; - } - (parts[0], Some(parts[1].to_owned())) - } else { - (parts[0], None) - }; - - // Parse and strip the disambiguator from the link, if present. - let (mut path_str, disambiguator) = match Disambiguator::from_str(&link) { - Ok(Some((d, path))) => (path.trim(), Some(d)), - Ok(None) => (link.trim(), None), - Err((err_msg, relative_range)) => { - if !should_ignore_link_with_disambiguators(link) { - // Only report error if we would not have ignored this link. - // See issue #83859. - let disambiguator_range = (no_backticks_range.start + relative_range.start) - ..(no_backticks_range.start + relative_range.end); - disambiguator_error(self.cx, diag_info, disambiguator_range, &err_msg); + let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } = + match preprocess_link(&ori_link)? { + Ok(x) => x, + Err(err) => { + match err { + PreprocessingError::Anchor(err) => anchor_failure(self.cx, diag_info, err), + PreprocessingError::Disambiguator(range, msg) => { + disambiguator_error(self.cx, diag_info, range, &msg) + } + PreprocessingError::Resolution(err, path_str, disambiguator) => { + resolution_failure( + self, + diag_info, + &path_str, + disambiguator, + smallvec![err], + ); + } + } + return None; } - return None; - } - }; - - if should_ignore_link(path_str) { - return None; - } - - // We stripped `()` and `!` when parsing the disambiguator. - // Add them back to be displayed, but not prefix disambiguators. - let link_text = - disambiguator.map(|d| d.display_for(path_str)).unwrap_or_else(|| path_str.to_owned()); + }; + let mut path_str = &*path_str; // In order to correctly resolve intra-doc links we need to // pick a base AST node to work from. If the documentation for @@ -1029,39 +1115,12 @@ impl LinkCollector<'_, '_> { module_id = DefId { krate, index: CRATE_DEF_INDEX }; } - // Strip generics from the path. - let stripped_path_string; - if path_str.contains(['<', '>'].as_slice()) { - stripped_path_string = match strip_generics_from_path(path_str) { - Ok(path) => path, - Err(err_kind) => { - debug!("link has malformed generics: {}", path_str); - resolution_failure( - self, - diag_info, - path_str, - disambiguator, - smallvec![err_kind], - ); - return None; - } - }; - path_str = &stripped_path_string; - } - // Sanity check to make sure we don't have any angle brackets after stripping generics. - assert!(!path_str.contains(['<', '>'].as_slice())); - - // The link is not an intra-doc link if it still contains spaces after stripping generics. - if path_str.contains(' ') { - return None; - } - let (mut res, mut fragment) = self.resolve_with_disambiguator_cached( ResolutionInfo { module_id, dis: disambiguator, path_str: path_str.to_owned(), - extra_fragment, + extra_fragment: extra_fragment.map(String::from), }, diag_info.clone(), // this struct should really be Copy, but Range is not :( matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut), @@ -1438,7 +1497,7 @@ fn should_ignore_link(path_str: &str) -> bool { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] /// Disambiguators for a link. -crate enum Disambiguator { +enum Disambiguator { /// `prim@` /// /// This is buggy, see <https://github.com/rust-lang/rust/pull/77875#discussion_r503583103> @@ -1467,7 +1526,7 @@ impl Disambiguator { /// This returns `Ok(Some(...))` if a disambiguator was found, /// `Ok(None)` if no disambiguator was found, or `Err(...)` /// if there was a problem with the disambiguator. - crate fn from_str(link: &str) -> Result<Option<(Self, &str)>, (String, Range<usize>)> { + fn from_str(link: &str) -> Result<Option<(Self, &str)>, (String, Range<usize>)> { use Disambiguator::{Kind, Namespace as NS, Primitive}; if let Some(idx) = link.find('@') { @@ -1898,20 +1957,32 @@ fn resolution_failure( /// Report an anchor failure. fn anchor_failure(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, failure: AnchorFailure) { - let msg = match failure { + let (msg, anchor_idx) = match failure { AnchorFailure::MultipleAnchors => { - format!("`{}` contains multiple anchors", diag_info.ori_link) + (format!("`{}` contains multiple anchors", diag_info.ori_link), 1) } - AnchorFailure::RustdocAnchorConflict(res) => format!( - "`{}` contains an anchor, but links to {kind}s are already anchored", - diag_info.ori_link, - kind = res.descr(), + AnchorFailure::RustdocAnchorConflict(res) => ( + format!( + "`{}` contains an anchor, but links to {kind}s are already anchored", + diag_info.ori_link, + kind = res.descr(), + ), + 0, ), }; report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, |diag, sp| { - if let Some(sp) = sp { - diag.span_label(sp, "contains invalid anchor"); + if let Some(mut sp) = sp { + if let Some((fragment_offset, _)) = + diag_info.ori_link.char_indices().filter(|(_, x)| *x == '#').nth(anchor_idx) + { + sp = sp.with_lo(sp.lo() + rustc_span::BytePos(fragment_offset as _)); + } + diag.span_label(sp, "invalid anchor"); + } + if let AnchorFailure::RustdocAnchorConflict(Res::Primitive(_)) = failure { + diag.note("this restriction may be lifted in a future release"); + diag.note("see https://github.com/rust-lang/rust/issues/83083 for more information"); } }); } diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs new file mode 100644 index 00000000000..7cba2523d1a --- /dev/null +++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs @@ -0,0 +1,63 @@ +use rustc_ast as ast; +use rustc_hir::def::Namespace::TypeNS; +use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX}; +use rustc_interface::interface; + +use std::cell::RefCell; +use std::mem; +use std::rc::Rc; + +// Letting the resolver escape at the end of the function leads to inconsistencies between the +// crates the TyCtxt sees and the resolver sees (because the resolver could load more crates +// after escaping). Hopefully `IntraLinkCrateLoader` gets all the crates we need ... +crate struct IntraLinkCrateLoader { + current_mod: DefId, + crate resolver: Rc<RefCell<interface::BoxedResolver>>, +} + +impl IntraLinkCrateLoader { + crate fn new(resolver: Rc<RefCell<interface::BoxedResolver>>) -> Self { + let crate_id = LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(); + Self { current_mod: crate_id, resolver } + } +} + +impl ast::visit::Visitor<'_> for IntraLinkCrateLoader { + fn visit_attribute(&mut self, attr: &ast::Attribute) { + use crate::html::markdown::markdown_links; + use crate::passes::collect_intra_doc_links::preprocess_link; + + if let Some(doc) = attr.doc_str() { + for link in markdown_links(&doc.as_str()) { + let path_str = if let Some(Ok(x)) = preprocess_link(&link) { + x.path_str + } else { + continue; + }; + self.resolver.borrow_mut().access(|resolver| { + let _ = resolver.resolve_str_path_error( + attr.span, + &path_str, + TypeNS, + self.current_mod, + ); + }); + } + } + ast::visit::walk_attribute(self, attr); + } + + fn visit_item(&mut self, item: &ast::Item) { + use rustc_ast_lowering::ResolverAstLowering; + + if let ast::ItemKind::Mod(..) = item.kind { + let new_mod = + self.resolver.borrow_mut().access(|resolver| resolver.local_def_id(item.id)); + let old_mod = mem::replace(&mut self.current_mod, new_mod.to_def_id()); + ast::visit::walk_item(self, item); + self.current_mod = old_mod; + } else { + ast::visit::walk_item(self, item); + } + } +} diff --git a/src/llvm-project b/src/llvm-project -Subproject 171991e30966695fd118c90ebbb2eeec5098ccc +Subproject ea6bb2615f04d53db11b6a43a14be5c9d1eaebe diff --git a/src/test/assembly/panic-no-unwind-no-uwtable.rs b/src/test/assembly/panic-no-unwind-no-uwtable.rs new file mode 100644 index 00000000000..499d4e69867 --- /dev/null +++ b/src/test/assembly/panic-no-unwind-no-uwtable.rs @@ -0,0 +1,8 @@ +// assembly-output: emit-asm +// only-x86_64-unknown-linux-gnu +// compile-flags: -C panic=unwind -C force-unwind-tables=n -O + +#![crate_type = "lib"] + +// CHECK-NOT: .cfi_startproc +pub fn foo() {} diff --git a/src/test/assembly/panic-unwind-no-uwtable.rs b/src/test/assembly/panic-unwind-no-uwtable.rs new file mode 100644 index 00000000000..8eed72b2fca --- /dev/null +++ b/src/test/assembly/panic-unwind-no-uwtable.rs @@ -0,0 +1,12 @@ +// assembly-output: emit-asm +// only-x86_64-unknown-linux-gnu +// compile-flags: -C panic=unwind -C force-unwind-tables=n + +#![crate_type = "lib"] + +// CHECK-LABEL: foo: +// CHECK: .cfi_startproc +#[no_mangle] +fn foo() { + panic!(); +} diff --git a/src/test/codegen/force-no-unwind-tables.rs b/src/test/codegen/force-no-unwind-tables.rs index dc77e6cb709..3ee23f05eb2 100644 --- a/src/test/codegen/force-no-unwind-tables.rs +++ b/src/test/codegen/force-no-unwind-tables.rs @@ -3,5 +3,9 @@ #![crate_type="lib"] +// CHECK-LABEL: define{{.*}}void @foo // CHECK-NOT: attributes #{{.*}} uwtable -pub fn foo() {} +#[no_mangle] +fn foo() { + panic!(); +} diff --git a/src/test/codegen/panic-unwind-default-uwtable.rs b/src/test/codegen/panic-unwind-default-uwtable.rs new file mode 100644 index 00000000000..4c85008cf35 --- /dev/null +++ b/src/test/codegen/panic-unwind-default-uwtable.rs @@ -0,0 +1,6 @@ +// compile-flags: -C panic=unwind -C no-prepopulate-passes + +#![crate_type = "lib"] + +// CHECK: attributes #{{.*}} uwtable +pub fn foo() {} diff --git a/src/test/incremental/issue-84252-global-alloc.rs b/src/test/incremental/issue-84252-global-alloc.rs new file mode 100644 index 00000000000..d2438df89ff --- /dev/null +++ b/src/test/incremental/issue-84252-global-alloc.rs @@ -0,0 +1,12 @@ +// revisions: cfail1 cfail2 +// build-pass + +#![crate_type="lib"] +#![crate_type="cdylib"] + +#[allow(unused_imports)] +use std::alloc::System; + +#[cfg(cfail1)] +#[global_allocator] +static ALLOC: System = System; diff --git a/src/test/rustdoc-ui/bare-urls.fixed b/src/test/rustdoc-ui/bare-urls.fixed new file mode 100644 index 00000000000..23aa5c44c21 --- /dev/null +++ b/src/test/rustdoc-ui/bare-urls.fixed @@ -0,0 +1,60 @@ +// run-rustfix + +#![deny(rustdoc::bare_urls)] + +/// <https://somewhere.com> +//~^ ERROR this URL is not a hyperlink +/// <https://somewhere.com/a> +//~^ ERROR this URL is not a hyperlink +/// <https://www.somewhere.com> +//~^ ERROR this URL is not a hyperlink +/// <https://www.somewhere.com/a> +//~^ ERROR this URL is not a hyperlink +/// <https://subdomain.example.com> +//~^ ERROR not a hyperlink +/// <https://somewhere.com?> +//~^ ERROR this URL is not a hyperlink +/// <https://somewhere.com/a?> +//~^ ERROR this URL is not a hyperlink +/// <https://somewhere.com?hello=12> +//~^ ERROR this URL is not a hyperlink +/// <https://somewhere.com/a?hello=12> +//~^ ERROR this URL is not a hyperlink +/// <https://example.com?hello=12#xyz> +//~^ ERROR this URL is not a hyperlink +/// <https://example.com/a?hello=12#xyz> +//~^ ERROR this URL is not a hyperlink +/// <https://example.com#xyz> +//~^ ERROR this URL is not a hyperlink +/// <https://example.com/a#xyz> +//~^ ERROR this URL is not a hyperlink +/// <https://somewhere.com?hello=12&bye=11> +//~^ ERROR this URL is not a hyperlink +/// <https://somewhere.com/a?hello=12&bye=11> +//~^ ERROR this URL is not a hyperlink +/// <https://somewhere.com?hello=12&bye=11#xyz> +//~^ ERROR this URL is not a hyperlink +/// hey! <https://somewhere.com/a?hello=12&bye=11#xyz> +//~^ ERROR this URL is not a hyperlink +pub fn c() {} + +/// <https://somewhere.com> +/// [a](http://a.com) +/// [b] +/// +/// [b]: http://b.com +/// +/// ``` +/// This link should not be linted: http://example.com +/// +/// Nor this one: <http://example.com> or this one: [x](http://example.com) +/// ``` +/// +/// [should_not.lint](should_not.lint) +pub fn everything_is_fine_here() {} + +#[allow(rustdoc::bare_urls)] +pub mod foo { + /// https://somewhere.com/a?hello=12&bye=11#xyz + pub fn bar() {} +} diff --git a/src/test/rustdoc-ui/url-improvements.rs b/src/test/rustdoc-ui/bare-urls.rs index 43a13b02d0a..592f57343bc 100644 --- a/src/test/rustdoc-ui/url-improvements.rs +++ b/src/test/rustdoc-ui/bare-urls.rs @@ -1,3 +1,5 @@ +// run-rustfix + #![deny(rustdoc::bare_urls)] /// https://somewhere.com diff --git a/src/test/rustdoc-ui/url-improvements.stderr b/src/test/rustdoc-ui/bare-urls.stderr index 3d5ebd8be6b..7097a8ddf3a 100644 --- a/src/test/rustdoc-ui/url-improvements.stderr +++ b/src/test/rustdoc-ui/bare-urls.stderr @@ -1,110 +1,143 @@ error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:3:5 + --> $DIR/bare-urls.rs:5:5 | LL | /// https://somewhere.com | ^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://somewhere.com>` | note: the lint level is defined here - --> $DIR/url-improvements.rs:1:9 + --> $DIR/bare-urls.rs:3:9 | LL | #![deny(rustdoc::bare_urls)] | ^^^^^^^^^^^^^^^^^^ + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:5:5 + --> $DIR/bare-urls.rs:7:5 | LL | /// https://somewhere.com/a | ^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://somewhere.com/a>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:7:5 + --> $DIR/bare-urls.rs:9:5 | LL | /// https://www.somewhere.com | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://www.somewhere.com>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:9:5 + --> $DIR/bare-urls.rs:11:5 | LL | /// https://www.somewhere.com/a | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://www.somewhere.com/a>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:11:5 + --> $DIR/bare-urls.rs:13:5 | LL | /// https://subdomain.example.com | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://subdomain.example.com>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:13:5 + --> $DIR/bare-urls.rs:15:5 | LL | /// https://somewhere.com? | ^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://somewhere.com?>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:15:5 + --> $DIR/bare-urls.rs:17:5 | LL | /// https://somewhere.com/a? | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://somewhere.com/a?>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:17:5 + --> $DIR/bare-urls.rs:19:5 | LL | /// https://somewhere.com?hello=12 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://somewhere.com?hello=12>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:19:5 + --> $DIR/bare-urls.rs:21:5 | LL | /// https://somewhere.com/a?hello=12 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://somewhere.com/a?hello=12>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:21:5 + --> $DIR/bare-urls.rs:23:5 | LL | /// https://example.com?hello=12#xyz | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://example.com?hello=12#xyz>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:23:5 + --> $DIR/bare-urls.rs:25:5 | LL | /// https://example.com/a?hello=12#xyz | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://example.com/a?hello=12#xyz>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:25:5 + --> $DIR/bare-urls.rs:27:5 | LL | /// https://example.com#xyz | ^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://example.com#xyz>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:27:5 + --> $DIR/bare-urls.rs:29:5 | LL | /// https://example.com/a#xyz | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://example.com/a#xyz>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:29:5 + --> $DIR/bare-urls.rs:31:5 | LL | /// https://somewhere.com?hello=12&bye=11 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://somewhere.com?hello=12&bye=11>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:31:5 + --> $DIR/bare-urls.rs:33:5 | LL | /// https://somewhere.com/a?hello=12&bye=11 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://somewhere.com/a?hello=12&bye=11>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:33:5 + --> $DIR/bare-urls.rs:35:5 | LL | /// https://somewhere.com?hello=12&bye=11#xyz | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://somewhere.com?hello=12&bye=11#xyz>` + | + = note: bare URLs are not automatically turned into clickable links error: this URL is not a hyperlink - --> $DIR/url-improvements.rs:35:10 + --> $DIR/bare-urls.rs:37:10 | LL | /// hey! https://somewhere.com/a?hello=12&bye=11#xyz | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://somewhere.com/a?hello=12&bye=11#xyz>` + | + = note: bare URLs are not automatically turned into clickable links error: aborting due to 17 previous errors diff --git a/src/test/rustdoc-ui/intra-doc/anchors.stderr b/src/test/rustdoc-ui/intra-doc/anchors.stderr index 787a68ed969..d63e1ee60b3 100644 --- a/src/test/rustdoc-ui/intra-doc/anchors.stderr +++ b/src/test/rustdoc-ui/intra-doc/anchors.stderr @@ -2,37 +2,52 @@ error: `prim@usize#x` contains an anchor, but links to builtin types are already --> $DIR/anchors.rs:47:6 | LL | /// [prim@usize#x] - | ^^^^^^^^^^^^ contains invalid anchor + | ^^^^^^^^^^-- + | | + | invalid anchor | note: the lint level is defined here --> $DIR/anchors.rs:1:9 | LL | #![deny(rustdoc::broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this restriction may be lifted in a future release + = note: see https://github.com/rust-lang/rust/issues/83083 for more information error: `Foo::f#hola` contains an anchor, but links to fields are already anchored --> $DIR/anchors.rs:25:15 | LL | /// Or maybe [Foo::f#hola]. - | ^^^^^^^^^^^ contains invalid anchor + | ^^^^^^----- + | | + | invalid anchor error: `hello#people#!` contains multiple anchors --> $DIR/anchors.rs:31:28 | LL | /// Another anchor error: [hello#people#!]. - | ^^^^^^^^^^^^^^ contains invalid anchor + | ^^^^^^^^^^^^-- + | | + | invalid anchor error: `Enum::A#whatever` contains an anchor, but links to variants are already anchored --> $DIR/anchors.rs:37:28 | LL | /// Damn enum's variants: [Enum::A#whatever]. - | ^^^^^^^^^^^^^^^^ contains invalid anchor + | ^^^^^^^--------- + | | + | invalid anchor error: `u32#hello` contains an anchor, but links to builtin types are already anchored --> $DIR/anchors.rs:43:6 | LL | /// [u32#hello] - | ^^^^^^^^^ contains invalid anchor + | ^^^------ + | | + | invalid anchor + | + = note: this restriction may be lifted in a future release + = note: see https://github.com/rust-lang/rust/issues/83083 for more information error: aborting due to 5 previous errors diff --git a/src/test/rustdoc-ui/intra-doc/double-anchor.stderr b/src/test/rustdoc-ui/intra-doc/double-anchor.stderr index c0241b98b78..6addb010e07 100644 --- a/src/test/rustdoc-ui/intra-doc/double-anchor.stderr +++ b/src/test/rustdoc-ui/intra-doc/double-anchor.stderr @@ -2,7 +2,9 @@ warning: `with#anchor#error` contains multiple anchors --> $DIR/double-anchor.rs:5:18 | LL | /// docs [label][with#anchor#error] - | ^^^^^^^^^^^^^^^^^ contains invalid anchor + | ^^^^^^^^^^^------ + | | + | invalid anchor | = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default diff --git a/src/test/rustdoc-ui/renamed-lint-still-applies.stderr b/src/test/rustdoc-ui/renamed-lint-still-applies.stderr index 19c253b366b..3040cad532c 100644 --- a/src/test/rustdoc-ui/renamed-lint-still-applies.stderr +++ b/src/test/rustdoc-ui/renamed-lint-still-applies.stderr @@ -31,6 +31,7 @@ note: the lint level is defined here | LL | #![deny(rustdoc::non_autolinks)] | ^^^^^^^^^^^^^^^^^^^^^^ + = note: bare URLs are not automatically turned into clickable links error: aborting due to 2 previous errors; 1 warning emitted diff --git a/src/test/rustdoc/assoc-types.rs b/src/test/rustdoc/assoc-types.rs index 82fa7cf9e60..8fda171002b 100644 --- a/src/test/rustdoc/assoc-types.rs +++ b/src/test/rustdoc/assoc-types.rs @@ -6,14 +6,14 @@ pub trait Index<I: ?Sized> { type Output: ?Sized; // @has - '//*[@id="tymethod.index"]//code' \ // "fn index<'a>(&'a self, index: I) -> &'a Self::Output" - // @has - '//*[@id="tymethod.index"]//code//a[@href="../assoc_types/trait.Index.html#associatedtype.Output"]' \ + // @has - '//*[@id="tymethod.index"]//code//a[@href="trait.Index.html#associatedtype.Output"]' \ // "Output" fn index<'a>(&'a self, index: I) -> &'a Self::Output; } // @has assoc_types/fn.use_output.html // @has - '//*[@class="rust fn"]' '-> &T::Output' -// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Index.html#associatedtype.Output"]' 'Output' +// @has - '//*[@class="rust fn"]//a[@href="trait.Index.html#associatedtype.Output"]' 'Output' pub fn use_output<T: Index<usize>>(obj: &T, index: usize) -> &T::Output { obj.index(index) } @@ -24,12 +24,12 @@ pub trait Feed { // @has assoc_types/fn.use_input.html // @has - '//*[@class="rust fn"]' 'T::Input' -// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Feed.html#associatedtype.Input"]' 'Input' +// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input' pub fn use_input<T: Feed>(_feed: &T, _element: T::Input) { } // @has assoc_types/fn.cmp_input.html // @has - '//*[@class="rust fn"]' 'where T::Input: PartialEq<U::Input>' -// @has - '//*[@class="rust fn"]//a[@href="../assoc_types/trait.Feed.html#associatedtype.Input"]' 'Input' +// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input' pub fn cmp_input<T: Feed, U: Feed>(a: &T::Input, b: &U::Input) -> bool where T::Input: PartialEq<U::Input> { diff --git a/src/test/rustdoc/attributes.rs b/src/test/rustdoc/attributes.rs index 54c5939f908..51cd4a6cbfd 100644 --- a/src/test/rustdoc/attributes.rs +++ b/src/test/rustdoc/attributes.rs @@ -1,21 +1,21 @@ #![crate_name = "foo"] -// @has foo/fn.f.html '//*[@class="docblock attributes"]' '#[no_mangle]' +// @has foo/fn.f.html '//*[@class="rust fn"]' '#[no_mangle]' #[no_mangle] pub extern "C" fn f() {} -// @has foo/fn.g.html '//*[@class="docblock attributes"]' '#[export_name = "bar"]' +// @has foo/fn.g.html '//*[@class="rust fn"]' '#[export_name = "bar"]' #[export_name = "bar"] pub extern "C" fn g() {} -// @matches foo/enum.Foo.html '//*[@class="docblock attributes top-attr"]' \ -// '(?m)\A#\[repr\(i64\)\]\n#\[must_use\]\Z' +// @matches foo/enum.Foo.html '//*[@class="rust enum"]' \ +// '#\[repr\(i64\)\]\n#\[must_use\]' #[repr(i64)] #[must_use] pub enum Foo { Bar, } -// @has foo/struct.Repr.html '//*[@class="docblock attributes top-attr"]' '#[repr(C, align(8))]' +// @has foo/struct.Repr.html '//*[@class="docblock type-decl"]' '#[repr(C, align(8))]' #[repr(C, align(8))] pub struct Repr; diff --git a/src/test/rustdoc/auxiliary/primitive-doc.rs b/src/test/rustdoc/auxiliary/primitive-doc.rs new file mode 100644 index 00000000000..a5b69740dd4 --- /dev/null +++ b/src/test/rustdoc/auxiliary/primitive-doc.rs @@ -0,0 +1,6 @@ +// compile-flags: --crate-type lib --edition 2018 + +#[doc(primitive = "usize")] +/// This is the built-in type `usize`. +mod usize { +} diff --git a/src/test/rustdoc/check-styled-link.rs b/src/test/rustdoc/check-styled-link.rs index b479820dfb2..ed4a5ea2137 100644 --- a/src/test/rustdoc/check-styled-link.rs +++ b/src/test/rustdoc/check-styled-link.rs @@ -2,7 +2,7 @@ pub struct Foo; -// @has foo/struct.Bar.html '//a[@href="../foo/struct.Foo.html"]' 'Foo' +// @has foo/struct.Bar.html '//a[@href="struct.Foo.html"]' 'Foo' /// Code-styled reference to [`Foo`]. pub struct Bar; diff --git a/src/test/rustdoc/cross-crate-primitive-doc.rs b/src/test/rustdoc/cross-crate-primitive-doc.rs new file mode 100644 index 00000000000..05376e4680e --- /dev/null +++ b/src/test/rustdoc/cross-crate-primitive-doc.rs @@ -0,0 +1,9 @@ +// aux-build:primitive-doc.rs +// compile-flags: --extern-html-root-url=primitive_doc=../ -Z unstable-options + +#![no_std] + +extern crate primitive_doc; + +// @has 'cross_crate_primitive_doc/fn.foo.html' '//a[@href="../primitive_doc/primitive.usize.html"]' 'usize' +pub fn foo() -> usize { 0 } diff --git a/src/test/rustdoc/decl_macro.rs b/src/test/rustdoc/decl_macro.rs index e48a56f906c..ede3f455a20 100644 --- a/src/test/rustdoc/decl_macro.rs +++ b/src/test/rustdoc/decl_macro.rs @@ -1,3 +1,5 @@ +// compile-flags: --document-private-items + #![feature(decl_macro)] // @has decl_macro/macro.my_macro.html //pre 'pub macro my_macro() {' @@ -37,3 +39,18 @@ pub macro my_macro_multi { pub macro by_example_single { ($foo:expr) => {} } + +mod a { + mod b { + // @has decl_macro/a/b/macro.by_example_vis.html //pre 'pub(super) macro by_example_vis($foo:expr) {' + pub(in super) macro by_example_vis { + ($foo:expr) => {} + } + mod c { + // @has decl_macro/a/b/c/macro.by_example_vis_named.html //pre 'pub(in a) macro by_example_vis_named($foo:expr) {' + pub(in a) macro by_example_vis_named { + ($foo:expr) => {} + } + } + } +} diff --git a/src/test/rustdoc/default-trait-method-link.rs b/src/test/rustdoc/default-trait-method-link.rs index e4f0bdab162..7bcd2a3c149 100644 --- a/src/test/rustdoc/default-trait-method-link.rs +++ b/src/test/rustdoc/default-trait-method-link.rs @@ -1,7 +1,7 @@ #![crate_name = "foo"] -// @has foo/trait.Foo.html '//a[@href="../foo/trait.Foo.html#tymethod.req"]' 'req' -// @has foo/trait.Foo.html '//a[@href="../foo/trait.Foo.html#method.prov"]' 'prov' +// @has foo/trait.Foo.html '//a[@href="trait.Foo.html#tymethod.req"]' 'req' +// @has foo/trait.Foo.html '//a[@href="trait.Foo.html#method.prov"]' 'prov' /// Always make sure to implement [`req`], but you don't have to implement [`prov`]. /// diff --git a/src/test/rustdoc/intra-doc-crate/self.rs b/src/test/rustdoc/intra-doc-crate/self.rs index 4db63b12b6b..8c36a7fa002 100644 --- a/src/test/rustdoc/intra-doc-crate/self.rs +++ b/src/test/rustdoc/intra-doc-crate/self.rs @@ -3,7 +3,7 @@ extern crate cross_crate_self; -// @has self/struct.S.html '//a[@href="../self/struct.S.html#method.f"]' "Self::f" -// @has self/struct.S.html '//a[@href="../self/struct.S.html"]' "Self" +// @has self/struct.S.html '//a[@href="struct.S.html#method.f"]' "Self::f" +// @has self/struct.S.html '//a[@href="struct.S.html"]' "Self" // @has self/struct.S.html '//a[@href="../cross_crate_self/index.html"]' "crate" pub use cross_crate_self::S; diff --git a/src/test/rustdoc/intra-doc/anchors.rs b/src/test/rustdoc/intra-doc/anchors.rs index e4f0c737bdd..8ec1a7b4f90 100644 --- a/src/test/rustdoc/intra-doc/anchors.rs +++ b/src/test/rustdoc/intra-doc/anchors.rs @@ -4,7 +4,7 @@ pub struct Something; // @has anchors/struct.SomeOtherType.html -// @has - '//a/@href' '../anchors/struct.Something.html#Anchor!' +// @has - '//a/@href' 'struct.Something.html#Anchor!' /// I want... /// diff --git a/src/test/rustdoc/intra-doc/associated-defaults.rs b/src/test/rustdoc/intra-doc/associated-defaults.rs index 28dc7073a3e..68647127fe8 100644 --- a/src/test/rustdoc/intra-doc/associated-defaults.rs +++ b/src/test/rustdoc/intra-doc/associated-defaults.rs @@ -9,14 +9,14 @@ pub trait TraitWithDefault { } /// Link to [UsesDefaults::T] and [UsesDefaults::f] -// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="../associated_defaults/struct.UsesDefaults.html#associatedtype.T"]' 'UsesDefaults::T' -// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="../associated_defaults/struct.UsesDefaults.html#method.f"]' 'UsesDefaults::f' +// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="struct.UsesDefaults.html#associatedtype.T"]' 'UsesDefaults::T' +// @has 'associated_defaults/struct.UsesDefaults.html' '//a[@href="struct.UsesDefaults.html#method.f"]' 'UsesDefaults::f' pub struct UsesDefaults; impl TraitWithDefault for UsesDefaults {} /// Link to [OverridesDefaults::T] and [OverridesDefaults::f] -// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="../associated_defaults/struct.OverridesDefaults.html#associatedtype.T"]' 'OverridesDefaults::T' -// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="../associated_defaults/struct.OverridesDefaults.html#method.f"]' 'OverridesDefaults::f' +// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="struct.OverridesDefaults.html#associatedtype.T"]' 'OverridesDefaults::T' +// @has 'associated_defaults/struct.OverridesDefaults.html' '//a[@href="struct.OverridesDefaults.html#method.f"]' 'OverridesDefaults::f' pub struct OverridesDefaults; impl TraitWithDefault for OverridesDefaults { type T = bool; diff --git a/src/test/rustdoc/intra-doc/associated-items.rs b/src/test/rustdoc/intra-doc/associated-items.rs index 43a43a79738..2757418bc64 100644 --- a/src/test/rustdoc/intra-doc/associated-items.rs +++ b/src/test/rustdoc/intra-doc/associated-items.rs @@ -9,10 +9,10 @@ pub fn foo() {} /// Link to [MyStruct], [link from struct][MyStruct::method], [MyStruct::clone], [MyStruct::Input] -// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html"]' 'MyStruct' -// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.method"]' 'link from struct' -// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.clone"]' 'MyStruct::clone' -// @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input' +// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html"]' 'MyStruct' +// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from struct' +// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.clone"]' 'MyStruct::clone' +// @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#associatedtype.Input"]' 'MyStruct::Input' pub struct MyStruct { foo: () } impl Clone for MyStruct { @@ -30,7 +30,7 @@ impl T for MyStruct { type Input = usize; /// [link from method][MyStruct::method] on method - // @has 'associated_items/struct.MyStruct.html' '//a[@href="../associated_items/struct.MyStruct.html#method.method"]' 'link from method' + // @has 'associated_items/struct.MyStruct.html' '//a[@href="struct.MyStruct.html#method.method"]' 'link from method' fn method(i: usize) { } } diff --git a/src/test/rustdoc/intra-doc/auxiliary/empty.rs b/src/test/rustdoc/intra-doc/auxiliary/empty.rs new file mode 100644 index 00000000000..d11c69f812a --- /dev/null +++ b/src/test/rustdoc/intra-doc/auxiliary/empty.rs @@ -0,0 +1 @@ +// intentionally empty diff --git a/src/test/rustdoc/intra-doc/auxiliary/empty2.rs b/src/test/rustdoc/intra-doc/auxiliary/empty2.rs new file mode 100644 index 00000000000..d11c69f812a --- /dev/null +++ b/src/test/rustdoc/intra-doc/auxiliary/empty2.rs @@ -0,0 +1 @@ +// intentionally empty diff --git a/src/test/rustdoc/intra-doc/basic.rs b/src/test/rustdoc/intra-doc/basic.rs index 7760546e1fa..39f5c298bc4 100644 --- a/src/test/rustdoc/intra-doc/basic.rs +++ b/src/test/rustdoc/intra-doc/basic.rs @@ -1,21 +1,21 @@ // @has basic/index.html -// @has - '//a/@href' '../basic/struct.ThisType.html' -// @has - '//a/@href' '../basic/struct.ThisType.html#method.this_method' -// @has - '//a/@href' '../basic/enum.ThisEnum.html' -// @has - '//a/@href' '../basic/enum.ThisEnum.html#variant.ThisVariant' -// @has - '//a/@href' '../basic/trait.ThisTrait.html' -// @has - '//a/@href' '../basic/trait.ThisTrait.html#tymethod.this_associated_method' -// @has - '//a/@href' '../basic/trait.ThisTrait.html#associatedtype.ThisAssociatedType' -// @has - '//a/@href' '../basic/trait.ThisTrait.html#associatedconstant.THIS_ASSOCIATED_CONST' -// @has - '//a/@href' '../basic/trait.ThisTrait.html' -// @has - '//a/@href' '../basic/type.ThisAlias.html' -// @has - '//a/@href' '../basic/union.ThisUnion.html' -// @has - '//a/@href' '../basic/fn.this_function.html' -// @has - '//a/@href' '../basic/constant.THIS_CONST.html' -// @has - '//a/@href' '../basic/static.THIS_STATIC.html' -// @has - '//a/@href' '../basic/macro.this_macro.html' -// @has - '//a/@href' '../basic/trait.SoAmbiguous.html' -// @has - '//a/@href' '../basic/fn.SoAmbiguous.html' +// @has - '//a/@href' 'struct.ThisType.html' +// @has - '//a/@href' 'struct.ThisType.html#method.this_method' +// @has - '//a/@href' 'enum.ThisEnum.html' +// @has - '//a/@href' 'enum.ThisEnum.html#variant.ThisVariant' +// @has - '//a/@href' 'trait.ThisTrait.html' +// @has - '//a/@href' 'trait.ThisTrait.html#tymethod.this_associated_method' +// @has - '//a/@href' 'trait.ThisTrait.html#associatedtype.ThisAssociatedType' +// @has - '//a/@href' 'trait.ThisTrait.html#associatedconstant.THIS_ASSOCIATED_CONST' +// @has - '//a/@href' 'trait.ThisTrait.html' +// @has - '//a/@href' 'type.ThisAlias.html' +// @has - '//a/@href' 'union.ThisUnion.html' +// @has - '//a/@href' 'fn.this_function.html' +// @has - '//a/@href' 'constant.THIS_CONST.html' +// @has - '//a/@href' 'static.THIS_STATIC.html' +// @has - '//a/@href' 'macro.this_macro.html' +// @has - '//a/@href' 'trait.SoAmbiguous.html' +// @has - '//a/@href' 'fn.SoAmbiguous.html' //! In this crate we would like to link to: //! //! * [`ThisType`](ThisType) @@ -46,7 +46,7 @@ macro_rules! this_macro { () => {}; } -// @has basic/struct.ThisType.html '//a/@href' '../basic/macro.this_macro.html' +// @has basic/struct.ThisType.html '//a/@href' 'macro.this_macro.html' /// another link to [`this_macro!()`] pub struct ThisType; @@ -72,10 +72,10 @@ pub trait SoAmbiguous {} pub fn SoAmbiguous() {} -// @has basic/struct.SomeOtherType.html '//a/@href' '../basic/struct.ThisType.html' -// @has - '//a/@href' '../basic/struct.ThisType.html#method.this_method' -// @has - '//a/@href' '../basic/enum.ThisEnum.html' -// @has - '//a/@href' '../basic/enum.ThisEnum.html#variant.ThisVariant' +// @has basic/struct.SomeOtherType.html '//a/@href' 'struct.ThisType.html' +// @has - '//a/@href' 'struct.ThisType.html#method.this_method' +// @has - '//a/@href' 'enum.ThisEnum.html' +// @has - '//a/@href' 'enum.ThisEnum.html#variant.ThisVariant' /// Shortcut links for: /// * [`ThisType`] /// * [`ThisType::this_method`] diff --git a/src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs b/src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs index 837390b3c71..85c5866ca7e 100644 --- a/src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs +++ b/src/test/rustdoc/intra-doc/cross-crate/additional_doc.rs @@ -4,7 +4,7 @@ extern crate my_rand; -// @has 'additional_doc/trait.Rng.html' '//a[@href="../additional_doc/trait.Rng.html"]' 'Rng' +// @has 'additional_doc/trait.Rng.html' '//a[@href="trait.Rng.html"]' 'Rng' // @has 'additional_doc/trait.Rng.html' '//a[@href="../my_rand/trait.RngCore.html"]' 'RngCore' /// This is an [`Rng`]. pub use my_rand::Rng; diff --git a/src/test/rustdoc/intra-doc/cross-crate/hidden.rs b/src/test/rustdoc/intra-doc/cross-crate/hidden.rs index 9c9d4c64945..31337f20f18 100644 --- a/src/test/rustdoc/intra-doc/cross-crate/hidden.rs +++ b/src/test/rustdoc/intra-doc/cross-crate/hidden.rs @@ -6,5 +6,5 @@ extern crate hidden_dep; -// @has 'hidden/struct.Ready.html' '//a/@href' '../hidden/fn.ready.html' +// @has 'hidden/struct.Ready.html' '//a/@href' 'fn.ready.html' pub use hidden_dep::future::{ready, Ready}; diff --git a/src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs b/src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs index 45f561328f2..db7952b5ace 100644 --- a/src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs +++ b/src/test/rustdoc/intra-doc/cross-crate/submodule-outer.rs @@ -11,6 +11,6 @@ pub mod bar { // NOTE: we re-exported both `Foo` and `Bar` here, // NOTE: so they are inlined and therefore we link to the current module. -// @has 'submodule_outer/trait.Foo.html' '//a[@href="../submodule_outer/bar/trait.Bar.html"]' 'Bar' -// @has 'submodule_outer/trait.Foo.html' '//a[@href="../submodule_outer/trait.Baz.html"]' 'Baz' +// @has 'submodule_outer/trait.Foo.html' '//a[@href="bar/trait.Bar.html"]' 'Bar' +// @has 'submodule_outer/trait.Foo.html' '//a[@href="trait.Baz.html"]' 'Baz' pub use ::bar_::{Foo, Baz}; diff --git a/src/test/rustdoc/intra-doc/disambiguators-removed.rs b/src/test/rustdoc/intra-doc/disambiguators-removed.rs index 12c3cee29c3..d782c5cf5dc 100644 --- a/src/test/rustdoc/intra-doc/disambiguators-removed.rs +++ b/src/test/rustdoc/intra-doc/disambiguators-removed.rs @@ -2,26 +2,26 @@ // first try backticks /// Trait: [`trait@Name`], fn: [`fn@Name`], [`Name`][`macro@Name`] // @has disambiguators_removed/struct.AtDisambiguator.html -// @has - '//a[@href="../disambiguators_removed/trait.Name.html"][code]' "Name" -// @has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name" -// @has - '//a[@href="../disambiguators_removed/macro.Name.html"][code]' "Name" +// @has - '//a[@href="trait.Name.html"][code]' "Name" +// @has - '//a[@href="fn.Name.html"][code]' "Name" +// @has - '//a[@href="macro.Name.html"][code]' "Name" pub struct AtDisambiguator; /// fn: [`Name()`], macro: [`Name!`] // @has disambiguators_removed/struct.SymbolDisambiguator.html -// @has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name()" -// @has - '//a[@href="../disambiguators_removed/macro.Name.html"][code]' "Name!" +// @has - '//a[@href="fn.Name.html"][code]' "Name()" +// @has - '//a[@href="macro.Name.html"][code]' "Name!" pub struct SymbolDisambiguator; // Now make sure that backticks aren't added if they weren't already there /// [fn@Name] // @has disambiguators_removed/trait.Name.html -// @has - '//a[@href="../disambiguators_removed/fn.Name.html"]' "Name" -// @!has - '//a[@href="../disambiguators_removed/fn.Name.html"][code]' "Name" +// @has - '//a[@href="fn.Name.html"]' "Name" +// @!has - '//a[@href="fn.Name.html"][code]' "Name" // FIXME: this will turn !() into ! alone /// [Name!()] -// @has - '//a[@href="../disambiguators_removed/macro.Name.html"]' "Name!" +// @has - '//a[@href="macro.Name.html"]' "Name!" pub trait Name {} #[allow(non_snake_case)] @@ -29,22 +29,22 @@ pub trait Name {} // Try collapsed reference links /// [macro@Name][] // @has disambiguators_removed/fn.Name.html -// @has - '//a[@href="../disambiguators_removed/macro.Name.html"]' "Name" +// @has - '//a[@href="macro.Name.html"]' "Name" // Try links that have the same text as a generated URL -/// Weird URL aligned [../disambiguators_removed/macro.Name.html][trait@Name] -// @has - '//a[@href="../disambiguators_removed/trait.Name.html"]' "../disambiguators_removed/macro.Name.html" +/// Weird URL aligned [macro.Name.html][trait@Name] +// @has - '//a[@href="trait.Name.html"]' "macro.Name.html" pub fn Name() {} #[macro_export] // Rustdoc doesn't currently handle links that have weird interspersing of inline code blocks. /// [fn@Na`m`e] // @has disambiguators_removed/macro.Name.html -// @has - '//a[@href="../disambiguators_removed/fn.Name.html"]' "fn@Name" +// @has - '//a[@href="fn.Name.html"]' "fn@Name" // It also doesn't handle any case where the code block isn't the whole link text: /// [trait@`Name`] -// @has - '//a[@href="../disambiguators_removed/trait.Name.html"]' "trait@Name" +// @has - '//a[@href="trait.Name.html"]' "trait@Name" macro_rules! Name { () => () } diff --git a/src/test/rustdoc/intra-doc/enum-struct-field.rs b/src/test/rustdoc/intra-doc/enum-struct-field.rs index 70bf343a9a5..2270a1fafa1 100644 --- a/src/test/rustdoc/intra-doc/enum-struct-field.rs +++ b/src/test/rustdoc/intra-doc/enum-struct-field.rs @@ -11,4 +11,4 @@ pub enum Foo { /// I want [Foo::X::y]. pub fn foo() {} -// @has foo/fn.foo.html '//a/@href' '../foo/enum.Foo.html#variant.X.field.y' +// @has foo/fn.foo.html '//a/@href' 'enum.Foo.html#variant.X.field.y' diff --git a/src/test/rustdoc/intra-doc/extern-crate-only-used-in-link.rs b/src/test/rustdoc/intra-doc/extern-crate-only-used-in-link.rs index 0964c79de06..5d8dcf8bc1d 100644 --- a/src/test/rustdoc/intra-doc/extern-crate-only-used-in-link.rs +++ b/src/test/rustdoc/intra-doc/extern-crate-only-used-in-link.rs @@ -1,8 +1,19 @@ +// This test is just a little cursed. // aux-build:issue-66159-1.rs // aux-crate:priv:issue_66159_1=issue-66159-1.rs +// aux-build:empty.rs +// aux-crate:priv:empty=empty.rs +// aux-build:empty2.rs +// aux-crate:priv:empty2=empty2.rs // build-aux-docs -// compile-flags:-Z unstable-options +// compile-flags:-Z unstable-options --edition 2018 // @has extern_crate_only_used_in_link/index.html // @has - '//a[@href="../issue_66159_1/struct.Something.html"]' 'issue_66159_1::Something' //! [issue_66159_1::Something] + +// @has - '//a[@href="../empty/index.html"]' 'empty' +//! [`empty`] + +// @has - '//a[@href="../empty2/index.html"]' 'empty2' +//! [empty2<x>] diff --git a/src/test/rustdoc/intra-doc/extern-type.rs b/src/test/rustdoc/intra-doc/extern-type.rs index e1934698d1f..f37ae62dde1 100644 --- a/src/test/rustdoc/intra-doc/extern-type.rs +++ b/src/test/rustdoc/intra-doc/extern-type.rs @@ -12,6 +12,6 @@ impl ExternType { // @has 'extern_type/foreigntype.ExternType.html' // @has 'extern_type/fn.links_to_extern_type.html' \ -// 'href="../extern_type/foreigntype.ExternType.html#method.f"' +// 'href="foreigntype.ExternType.html#method.f"' /// See also [ExternType::f] pub fn links_to_extern_type() {} diff --git a/src/test/rustdoc/intra-doc/issue-82209.rs b/src/test/rustdoc/intra-doc/issue-82209.rs index 76618cdce4c..68a5672a8d2 100644 --- a/src/test/rustdoc/intra-doc/issue-82209.rs +++ b/src/test/rustdoc/intra-doc/issue-82209.rs @@ -8,4 +8,4 @@ pub enum Foo { }, } -// @has foo/enum.Foo.html '//a/@href' '../foo/enum.Foo.html#variant.Bar.field.abc' +// @has foo/enum.Foo.html '//a/@href' 'enum.Foo.html#variant.Bar.field.abc' diff --git a/src/test/rustdoc/intra-doc/mod-ambiguity.rs b/src/test/rustdoc/intra-doc/mod-ambiguity.rs index feb013b22be..24b9dc30a9e 100644 --- a/src/test/rustdoc/intra-doc/mod-ambiguity.rs +++ b/src/test/rustdoc/intra-doc/mod-ambiguity.rs @@ -6,11 +6,11 @@ pub fn foo() { } pub mod foo {} -// @has mod_ambiguity/struct.A.html '//a/@href' '../mod_ambiguity/foo/index.html' +// @has mod_ambiguity/struct.A.html '//a/@href' 'foo/index.html' /// Module is [`module@foo`] pub struct A; -// @has mod_ambiguity/struct.B.html '//a/@href' '../mod_ambiguity/fn.foo.html' +// @has mod_ambiguity/struct.B.html '//a/@href' 'fn.foo.html' /// Function is [`fn@foo`] pub struct B; diff --git a/src/test/rustdoc/intra-doc/prim-precedence.rs b/src/test/rustdoc/intra-doc/prim-precedence.rs index ab6e3da17f4..478b40b0b51 100644 --- a/src/test/rustdoc/intra-doc/prim-precedence.rs +++ b/src/test/rustdoc/intra-doc/prim-precedence.rs @@ -11,6 +11,6 @@ pub mod char { pub struct MyString; /// See also [crate::char] and [mod@char] -// @has prim_precedence/struct.MyString2.html '//*[@href="../prim_precedence/char/index.html"]' 'crate::char' -// @has - '//*[@href="../prim_precedence/char/index.html"]' 'mod@char' +// @has prim_precedence/struct.MyString2.html '//*[@href="char/index.html"]' 'crate::char' +// @has - '//*[@href="char/index.html"]' 'mod@char' pub struct MyString2; diff --git a/src/test/rustdoc/intra-doc/private.rs b/src/test/rustdoc/intra-doc/private.rs index 337102d6ab3..2756a7998e8 100644 --- a/src/test/rustdoc/intra-doc/private.rs +++ b/src/test/rustdoc/intra-doc/private.rs @@ -4,9 +4,9 @@ // make sure to update `rustdoc-ui/intra-doc/private.rs` if you update this file /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x] -// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html"]' 'DontDocMe' -// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html#method.f"]' 'DontDocMe::f' -// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html#structfield.x"]' 'DontDocMe::x' +// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html"]' 'DontDocMe' +// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html#method.f"]' 'DontDocMe::f' +// @has private/struct.DocMe.html '//*a[@href="struct.DontDocMe.html#structfield.x"]' 'DontDocMe::x' pub struct DocMe; struct DontDocMe { x: usize, diff --git a/src/test/rustdoc/intra-doc/proc-macro.rs b/src/test/rustdoc/intra-doc/proc-macro.rs index ab4626ccfc3..fce10a130be 100644 --- a/src/test/rustdoc/intra-doc/proc-macro.rs +++ b/src/test/rustdoc/intra-doc/proc-macro.rs @@ -9,17 +9,17 @@ pub use proc_macro_macro::{DeriveA, attr_a}; use proc_macro_macro::{DeriveB, attr_b}; // @has proc_macro/struct.Foo.html -// @has - '//a/@href' '../proc_macro/derive.DeriveA.html' -// @has - '//a/@href' '../proc_macro/attr.attr_a.html' -// @has - '//a/@href' '../proc_macro/trait.DeriveTrait.html' +// @has - '//a/@href' 'derive.DeriveA.html' +// @has - '//a/@href' 'attr.attr_a.html' +// @has - '//a/@href' 'trait.DeriveTrait.html' // @has - '//a/@href' '../proc_macro_macro/derive.DeriveB.html' // @has - '//a/@href' '../proc_macro_macro/attr.attr_b.html' /// Link to [DeriveA], [attr_a], [DeriveB], [attr_b], [DeriveTrait] pub struct Foo; // @has proc_macro/struct.Bar.html -// @has - '//a/@href' '../proc_macro/derive.DeriveA.html' -// @has - '//a/@href' '../proc_macro/attr.attr_a.html' +// @has - '//a/@href' 'derive.DeriveA.html' +// @has - '//a/@href' 'attr.attr_a.html' /// Link to [deriveA](derive@DeriveA) [attr](macro@attr_a) pub struct Bar; diff --git a/src/test/rustdoc/intra-doc/pub-use.rs b/src/test/rustdoc/intra-doc/pub-use.rs index dd52249abc6..579fa68cee8 100644 --- a/src/test/rustdoc/intra-doc/pub-use.rs +++ b/src/test/rustdoc/intra-doc/pub-use.rs @@ -13,7 +13,7 @@ extern crate inner; // @has outer/index.html // @ has - '//a[@href="https://doc.rust-lang.org/nightly/std/env/fn.var.html"]' "std::env" -// @ has - '//a[@href="../outer/fn.f.html"]' "g" +// @ has - '//a[@href="fn.f.html"]' "g" pub use f as g; // FIXME: same as above diff --git a/src/test/rustdoc/intra-doc/raw-ident-self.rs b/src/test/rustdoc/intra-doc/raw-ident-self.rs index d289797f146..177c3016fb1 100644 --- a/src/test/rustdoc/intra-doc/raw-ident-self.rs +++ b/src/test/rustdoc/intra-doc/raw-ident-self.rs @@ -5,7 +5,7 @@ pub mod r#impl { impl S { /// See [Self::b]. // @has raw_ident_self/impl/struct.S.html - // @has - '//a[@href="../../raw_ident_self/impl/struct.S.html#method.b"]' 'Self::b' + // @has - '//a[@href="struct.S.html#method.b"]' 'Self::b' pub fn a() {} pub fn b() {} diff --git a/src/test/rustdoc/intra-doc/reexport-additional-docs.rs b/src/test/rustdoc/intra-doc/reexport-additional-docs.rs index 96f3580f305..64683bacd65 100644 --- a/src/test/rustdoc/intra-doc/reexport-additional-docs.rs +++ b/src/test/rustdoc/intra-doc/reexport-additional-docs.rs @@ -3,13 +3,13 @@ #![crate_name = "foo"] extern crate inner; -// @has foo/struct.Inner.html '//a[@href="../foo/fn.with_code.html"]' 'crate::with_code' +// @has foo/struct.Inner.html '//a[@href="fn.with_code.html"]' 'crate::with_code' /// [crate::with_code] -// @has - '//a[@href="../foo/fn.with_code.html"]' 'different text' +// @has - '//a[@href="fn.with_code.html"]' 'different text' /// [different text][with_code] -// @has - '//a[@href="../foo/fn.me_too.html"]' 'me_too' +// @has - '//a[@href="fn.me_too.html"]' 'me_too' #[doc = "[me_too]"] -// @has - '//a[@href="../foo/fn.me_three.html"]' 'reference link' +// @has - '//a[@href="fn.me_three.html"]' 'reference link' /// This [reference link] #[doc = "has an attr in the way"] /// diff --git a/src/test/rustdoc/intra-doc/self.rs b/src/test/rustdoc/intra-doc/self.rs index b2b75127b31..0ba7df8a78a 100644 --- a/src/test/rustdoc/intra-doc/self.rs +++ b/src/test/rustdoc/intra-doc/self.rs @@ -1,8 +1,8 @@ #![crate_name = "foo"] -// @has foo/index.html '//a/@href' '../foo/struct.Foo.html#method.new' -// @has foo/struct.Foo.html '//a/@href' '../foo/struct.Foo.html#method.new' +// @has foo/index.html '//a/@href' 'struct.Foo.html#method.new' +// @has foo/struct.Foo.html '//a/@href' 'struct.Foo.html#method.new' /// Use [`new`] to create a new instance. /// @@ -15,8 +15,8 @@ impl Foo { } } -// @has foo/index.html '//a/@href' '../foo/struct.Bar.html#method.new2' -// @has foo/struct.Bar.html '//a/@href' '../foo/struct.Bar.html#method.new2' +// @has foo/index.html '//a/@href' 'struct.Bar.html#method.new2' +// @has foo/struct.Bar.html '//a/@href' 'struct.Bar.html#method.new2' /// Use [`new2`] to create a new instance. /// @@ -30,7 +30,7 @@ impl Bar { } pub struct MyStruct { - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#structfield.struct_field' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#structfield.struct_field' /// [`struct_field`] /// @@ -39,7 +39,7 @@ pub struct MyStruct { } pub enum MyEnum { - // @has foo/enum.MyEnum.html '//a/@href' '../foo/enum.MyEnum.html#variant.EnumVariant' + // @has foo/enum.MyEnum.html '//a/@href' 'enum.MyEnum.html#variant.EnumVariant' /// [`EnumVariant`] /// @@ -48,7 +48,7 @@ pub enum MyEnum { } pub union MyUnion { - // @has foo/union.MyUnion.html '//a/@href' '../foo/union.MyUnion.html#structfield.union_field' + // @has foo/union.MyUnion.html '//a/@href' 'union.MyUnion.html#structfield.union_field' /// [`union_field`] /// @@ -57,21 +57,21 @@ pub union MyUnion { } pub trait MyTrait { - // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedtype.AssoType' + // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#associatedtype.AssoType' /// [`AssoType`] /// /// [`AssoType`]: Self::AssoType type AssoType; - // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#associatedconstant.ASSO_CONST' + // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#associatedconstant.ASSO_CONST' /// [`ASSO_CONST`] /// /// [`ASSO_CONST`]: Self::ASSO_CONST const ASSO_CONST: i32 = 1; - // @has foo/trait.MyTrait.html '//a/@href' '../foo/trait.MyTrait.html#method.asso_fn' + // @has foo/trait.MyTrait.html '//a/@href' 'trait.MyTrait.html#method.asso_fn' /// [`asso_fn`] /// @@ -80,7 +80,7 @@ pub trait MyTrait { } impl MyStruct { - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.for_impl' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.for_impl' /// [`for_impl`] /// @@ -91,21 +91,21 @@ impl MyStruct { } impl MyTrait for MyStruct { - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedtype.AssoType' /// [`AssoType`] /// /// [`AssoType`]: Self::AssoType type AssoType = u32; - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedconstant.ASSO_CONST' /// [`ASSO_CONST`] /// /// [`ASSO_CONST`]: Self::ASSO_CONST const ASSO_CONST: i32 = 10; - // @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.asso_fn' + // @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.asso_fn' /// [`asso_fn`] /// diff --git a/src/test/rustdoc/intra-doc/trait-impl.rs b/src/test/rustdoc/intra-doc/trait-impl.rs index ef1987a829a..cf60dc1dbd5 100644 --- a/src/test/rustdoc/intra-doc/trait-impl.rs +++ b/src/test/rustdoc/intra-doc/trait-impl.rs @@ -5,21 +5,21 @@ pub struct MyStruct; impl MyTrait for MyStruct { -// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedtype.AssoType' +// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedtype.AssoType' /// [`AssoType`] /// /// [`AssoType`]: MyStruct::AssoType type AssoType = u32; -// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#associatedconstant.ASSO_CONST' +// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#associatedconstant.ASSO_CONST' /// [`ASSO_CONST`] /// /// [`ASSO_CONST`]: MyStruct::ASSO_CONST const ASSO_CONST: i32 = 10; -// @has foo/struct.MyStruct.html '//a/@href' '../foo/struct.MyStruct.html#method.trait_fn' +// @has foo/struct.MyStruct.html '//a/@href' 'struct.MyStruct.html#method.trait_fn' /// [`trait_fn`] /// diff --git a/src/test/rustdoc/intra-doc/trait-item.rs b/src/test/rustdoc/intra-doc/trait-item.rs index affd2aaec2d..7602aced564 100644 --- a/src/test/rustdoc/intra-doc/trait-item.rs +++ b/src/test/rustdoc/intra-doc/trait-item.rs @@ -2,7 +2,7 @@ /// Link to [S::assoc_fn()] /// Link to [Default::default()] -// @has trait_item/struct.S.html '//*[@href="../trait_item/struct.S.html#method.assoc_fn"]' 'S::assoc_fn()' +// @has trait_item/struct.S.html '//*[@href="struct.S.html#method.assoc_fn"]' 'S::assoc_fn()' // @has - '//*[@href="https://doc.rust-lang.org/nightly/core/default/trait.Default.html#tymethod.default"]' 'Default::default()' pub struct S; diff --git a/src/test/rustdoc/intra-link-self-cache.rs b/src/test/rustdoc/intra-link-self-cache.rs index add1530a5a6..63bf7fa5768 100644 --- a/src/test/rustdoc/intra-link-self-cache.rs +++ b/src/test/rustdoc/intra-link-self-cache.rs @@ -1,12 +1,12 @@ #![crate_name = "foo"] -// @has foo/enum.E1.html '//a/@href' '../foo/enum.E1.html#variant.A' +// @has foo/enum.E1.html '//a/@href' 'enum.E1.html#variant.A' /// [Self::A::b] pub enum E1 { A { b: usize } } -// @has foo/enum.E2.html '//a/@href' '../foo/enum.E2.html#variant.A' +// @has foo/enum.E2.html '//a/@href' 'enum.E2.html#variant.A' /// [Self::A::b] pub enum E2 { diff --git a/src/test/rustdoc/issue-28478.rs b/src/test/rustdoc/issue-28478.rs index 4cc40560254..497276e6826 100644 --- a/src/test/rustdoc/issue-28478.rs +++ b/src/test/rustdoc/issue-28478.rs @@ -23,9 +23,9 @@ impl Foo { } impl Bar for Foo { - // @has - '//*[@href="../issue_28478/trait.Bar.html#associatedtype.Bar"]' 'Bar' - // @has - '//*[@href="../issue_28478/trait.Bar.html#associatedconstant.Baz"]' 'Baz' - // @has - '//*[@href="../issue_28478/trait.Bar.html#tymethod.bar"]' 'bar' + // @has - '//*[@href="trait.Bar.html#associatedtype.Bar"]' 'Bar' + // @has - '//*[@href="trait.Bar.html#associatedconstant.Baz"]' 'Baz' + // @has - '//*[@href="trait.Bar.html#tymethod.bar"]' 'bar' fn bar() {} - // @has - '//*[@href="../issue_28478/trait.Bar.html#method.baz"]' 'baz' + // @has - '//*[@href="trait.Bar.html#method.baz"]' 'baz' } diff --git a/src/test/rustdoc/issue-55364.rs b/src/test/rustdoc/issue-55364.rs index 4aa553f7793..f156d225bd7 100644 --- a/src/test/rustdoc/issue-55364.rs +++ b/src/test/rustdoc/issue-55364.rs @@ -2,19 +2,19 @@ // @has issue_55364/subone/index.html // These foo/bar links in the module's documentation should refer inside `subone` -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.foo.html"]' 'foo' -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.bar.html"]' 'bar' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar' pub mod subone { //! See either [foo] or [bar]. // This should refer to subone's `bar` // @has issue_55364/subone/fn.foo.html - // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.bar.html"]' 'bar' + // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar' /// See [bar] pub fn foo() {} // This should refer to subone's `foo` // @has issue_55364/subone/fn.bar.html - // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subone/fn.foo.html"]' 'foo' + // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo' /// See [foo] pub fn bar() {} } @@ -23,11 +23,11 @@ pub mod subone { // @has issue_55364/subtwo/index.html // These foo/bar links in the module's documentation should not reference inside `subtwo` -// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.foo.html"]' 'foo' -// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.bar.html"]' 'bar' +// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo' +// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar' // Instead it should be referencing the top level functions -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.foo.html"]' 'foo' -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.bar.html"]' 'bar' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar' // Though there should be such links later // @has - '//section[@id="main"]/table//tr[@class="module-item"]/td/a[@class="fn"][@href="fn.foo.html"]' 'foo' // @has - '//section[@id="main"]/table//tr[@class="module-item"]/td/a[@class="fn"][@href="fn.bar.html"]' 'bar' @@ -37,13 +37,13 @@ pub mod subtwo { // Despite the module's docs referring to the top level foo/bar, // this should refer to subtwo's `bar` // @has issue_55364/subtwo/fn.foo.html - // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.bar.html"]' 'bar' + // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar' /// See [bar] pub fn foo() {} // Despite the module's docs referring to the top level foo/bar, // this should refer to subtwo's `foo` // @has issue_55364/subtwo/fn.bar.html - // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/subtwo/fn.foo.html"]' 'foo' + // @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo' /// See [foo] pub fn bar() {} } @@ -59,8 +59,8 @@ pub fn bar() {} // @has issue_55364/subthree/index.html // This module should also refer to the top level foo/bar -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.foo.html"]' 'foo' -// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../../issue_55364/fn.bar.html"]' 'bar' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo' +// @has - '//section[@id="main"]/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar' pub mod subthree { //! See either [foo][super::foo] or [bar][super::bar] } @@ -68,8 +68,8 @@ pub mod subthree { // Next we go *deeper* - In order to ensure it's not just "this or parent" // we test `crate::` and a `super::super::...` chain // @has issue_55364/subfour/subfive/subsix/subseven/subeight/index.html -// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../../issue_55364/subone/fn.foo.html"]' 'other foo' -// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../../issue_55364/subtwo/fn.bar.html"]' 'other bar' +// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo' +// @has - '//section[@id="main"]/table//tr[@class="module-item"]/td[@class="docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar' pub mod subfour { pub mod subfive { pub mod subsix { diff --git a/src/test/rustdoc/issue-72340.rs b/src/test/rustdoc/issue-72340.rs index 6ed3bfbe3e5..64044cfe947 100644 --- a/src/test/rustdoc/issue-72340.rs +++ b/src/test/rustdoc/issue-72340.rs @@ -10,7 +10,7 @@ impl Body { } impl Default for Body { - // @has foo/struct.Body.html '//a/@href' '../foo/struct.Body.html#method.empty' + // @has foo/struct.Body.html '//a/@href' 'struct.Body.html#method.empty' /// Returns [`Body::empty()`](Body::empty). fn default() -> Body { diff --git a/src/test/rustdoc/item-hide-threshold.rs b/src/test/rustdoc/item-hide-threshold.rs new file mode 100644 index 00000000000..616eef95662 --- /dev/null +++ b/src/test/rustdoc/item-hide-threshold.rs @@ -0,0 +1,155 @@ +#![allow(unused)] + +// @has 'item_hide_threshold/struct.PubStruct.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0 +pub struct PubStruct { + pub a: usize, + pub b: usize, +} + +// @has 'item_hide_threshold/struct.BigPubStruct.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show fields' +pub struct BigPubStruct { + pub a: usize, + pub b: usize, + pub c: usize, + pub d: usize, + pub e: usize, + pub f: usize, + pub g: usize, + pub h: usize, + pub i: usize, + pub j: usize, + pub k: usize, + pub l: usize, + pub m: usize, +} + +// @has 'item_hide_threshold/union.BigUnion.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show fields' +pub union BigUnion { + pub a: usize, + pub b: usize, + pub c: usize, + pub d: usize, + pub e: usize, + pub f: usize, + pub g: usize, + pub h: usize, + pub i: usize, + pub j: usize, + pub k: usize, + pub l: usize, + pub m: usize, +} + +// @has 'item_hide_threshold/union.Union.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0 +pub union Union { + pub a: usize, + pub b: usize, + pub c: usize, +} + +// @has 'item_hide_threshold/struct.PrivStruct.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0 +// @has - '//div[@class="docblock type-decl"]' 'fields omitted' +pub struct PrivStruct { + a: usize, + b: usize, +} + +// @has 'item_hide_threshold/enum.Enum.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0 +pub enum Enum { + A, B, C, + D { + a: u8, + b: u8 + } +} + +// @has 'item_hide_threshold/enum.LargeEnum.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show variants' +pub enum LargeEnum { + A, B, C, D, E, F(u8), G, H, I, J, K, L, M +} + +// @has 'item_hide_threshold/trait.Trait.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0 +pub trait Trait { + type A; + #[must_use] + fn foo(); + fn bar(); +} + +// @has 'item_hide_threshold/trait.GinormousTrait.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show associated items' +pub trait GinormousTrait { + type A; + type B; + type C; + type D; + type E; + type F; + type G; + type H; + type I; + type J; + type K; + type L; + type M; + const N: usize = 1; + #[must_use] + fn foo(); + fn bar(); +} + +// @has 'item_hide_threshold/trait.HugeTrait.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show associated constants and methods' +pub trait HugeTrait { + type A; + const M: usize = 1; + const N: usize = 1; + const O: usize = 1; + const P: usize = 1; + const Q: usize = 1; + const R: usize = 1; + const S: usize = 1; + const T: usize = 1; + const U: usize = 1; + const V: usize = 1; + const W: usize = 1; + const X: usize = 1; + #[must_use] + fn foo(); + fn bar(); +} + +// @has 'item_hide_threshold/trait.BigTrait.html' +// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1 +// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show methods' +pub trait BigTrait { + type A; + #[must_use] + fn foo(); + fn bar(); + fn baz(); + fn quux(); + fn frob(); + fn greeble(); + fn blap(); + fn whoop(); + fn pow(); + fn bang(); + fn oomph(); + fn argh(); + fn wap(); + fn ouch(); +} diff --git a/src/test/rustdoc/link-assoc-const.rs b/src/test/rustdoc/link-assoc-const.rs index f9eb2b722d6..75a2531a308 100644 --- a/src/test/rustdoc/link-assoc-const.rs +++ b/src/test/rustdoc/link-assoc-const.rs @@ -1,7 +1,7 @@ #![crate_name = "foo"] -// @has foo/index.html '//a[@href="../foo/foo/constant.FIRSTCONST.html"]' 'foo::FIRSTCONST' -// @has foo/index.html '//a[@href="../foo/struct.Bar.html#associatedconstant.CONST"]' 'Bar::CONST' +// @has foo/index.html '//a[@href="foo/constant.FIRSTCONST.html"]' 'foo::FIRSTCONST' +// @has foo/index.html '//a[@href="struct.Bar.html#associatedconstant.CONST"]' 'Bar::CONST' //! We have here [`foo::FIRSTCONST`] and [`Bar::CONST`]. diff --git a/src/test/rustdoc/proc-macro.rs b/src/test/rustdoc/proc-macro.rs index 82196e413e9..f6d1f2cf91b 100644 --- a/src/test/rustdoc/proc-macro.rs +++ b/src/test/rustdoc/proc-macro.rs @@ -61,12 +61,12 @@ pub fn some_derive(_item: TokenStream) -> TokenStream { // @has some_macros/foo/index.html mod foo { // @has - '//code' 'pub use some_proc_macro;' - // @has - '//a/@href' '../../some_macros/macro.some_proc_macro.html' + // @has - '//a/@href' '../macro.some_proc_macro.html' pub use some_proc_macro; // @has - '//code' 'pub use some_proc_attr;' - // @has - '//a/@href' '../../some_macros/attr.some_proc_attr.html' + // @has - '//a/@href' '../attr.some_proc_attr.html' pub use some_proc_attr; // @has - '//code' 'pub use some_derive;' - // @has - '//a/@href' '../../some_macros/derive.SomeDerive.html' + // @has - '//a/@href' '../derive.SomeDerive.html' pub use some_derive; } diff --git a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs index 3ecf434c39e..ad190361267 100644 --- a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs +++ b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs @@ -8,13 +8,13 @@ pub mod internal { /// /// [name]: mod /// [other name]: crate::internal::mod - // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' - // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="struct.mod.html"]' 'name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="struct.mod.html"]' 'other name' pub struct B; } /// See [name]. /// /// [name]: internal::mod -// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' +// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="internal/struct.mod.html"]' 'name' pub struct A; diff --git a/src/test/rustdoc/struct-field.rs b/src/test/rustdoc/struct-field.rs index 974b863bb16..998683bdde7 100644 --- a/src/test/rustdoc/struct-field.rs +++ b/src/test/rustdoc/struct-field.rs @@ -1,9 +1,9 @@ #![crate_name = "foo"] -// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/struct.Foo.html#structfield.bar"]' 'Foo::bar' -// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/union.Bar.html#structfield.foo"]' 'Bar::foo' -// @has foo/index.html '//*[@class="docblock"]/p/a[@href="../foo/enum.Uniooon.html#variant.X"]' 'Uniooon::X' +// @has foo/index.html '//*[@class="docblock"]/p/a[@href="struct.Foo.html#structfield.bar"]' 'Foo::bar' +// @has foo/index.html '//*[@class="docblock"]/p/a[@href="union.Bar.html#structfield.foo"]' 'Bar::foo' +// @has foo/index.html '//*[@class="docblock"]/p/a[@href="enum.Uniooon.html#variant.X"]' 'Uniooon::X' //! Test with [Foo::bar], [Bar::foo], [Uniooon::X] diff --git a/src/test/rustdoc/trait-attributes.rs b/src/test/rustdoc/trait-attributes.rs index 2a103509ae1..2bb24a82193 100644 --- a/src/test/rustdoc/trait-attributes.rs +++ b/src/test/rustdoc/trait-attributes.rs @@ -2,7 +2,7 @@ pub trait Foo { - // @has foo/trait.Foo.html '//h3[@id="tymethod.foo"]//span[@class="docblock attributes"]' '#[must_use]' + // @has foo/trait.Foo.html '//h3[@id="tymethod.foo"]//div[@class="code-attribute"]' '#[must_use]' #[must_use] fn foo(); } @@ -11,11 +11,11 @@ pub trait Foo { pub struct Bar; impl Bar { - // @has foo/struct.Bar.html '//h4[@id="method.bar"]//span[@class="docblock attributes"]' '#[must_use]' + // @has foo/struct.Bar.html '//h4[@id="method.bar"]//div[@class="code-attribute"]' '#[must_use]' #[must_use] pub fn bar() {} - // @has foo/struct.Bar.html '//h4[@id="method.bar2"]//span[@class="docblock attributes"]' '#[must_use]' + // @has foo/struct.Bar.html '//h4[@id="method.bar2"]//div[@class="code-attribute"]' '#[must_use]' #[must_use] pub fn bar2() {} } diff --git a/src/test/rustdoc/trait-impl-items-links-and-anchors.rs b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs new file mode 100644 index 00000000000..c6a9313e821 --- /dev/null +++ b/src/test/rustdoc/trait-impl-items-links-and-anchors.rs @@ -0,0 +1,65 @@ +pub trait MyTrait { + type Assoc; + const VALUE: u32; + fn trait_function(&self); + fn defaulted(&self) {} + fn defaulted_override(&self) {} +} + + +impl MyTrait for String { + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-1"]//a[@class="type"]/@href' #associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-1"]//a[@class="anchor"]/@href' #associatedtype.Assoc-1 + type Assoc = (); + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-1"]//a[@class="constant"]/@href' #associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-1"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-1 + const VALUE: u32 = 5; + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function + fn trait_function(&self) {} + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-1"]//a[@class="fnname"]/@href' #method.defaulted_override + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-1"]//a[@class="anchor"]/@href' #method.defaulted_override-1 + fn defaulted_override(&self) {} +} + +impl MyTrait for Vec<u8> { + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-2"]//a[@class="type"]/@href' #associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-2"]//a[@class="anchor"]/@href' #associatedtype.Assoc-2 + type Assoc = (); + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-2"]//a[@class="constant"]/@href' #associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-2"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-2 + const VALUE: u32 = 5; + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-1"]//a[@class="anchor"]/@href' #method.trait_function-1 + fn trait_function(&self) {} + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-2"]//a[@class="fnname"]/@href' #method.defaulted_override + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-2"]//a[@class="anchor"]/@href' #method.defaulted_override-2 + fn defaulted_override(&self) {} +} + +impl MyTrait for MyStruct { + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-3"]//a[@class="type"]/@href' #associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedtype.Assoc-3"]//a[@class="anchor"]/@href' #associatedtype.Assoc-3 + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="type"]/@href' trait.MyTrait.html#associatedtype.Assoc + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedtype.Assoc"]//a[@class="anchor"]/@href' #associatedtype.Assoc + type Assoc = bool; + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-3"]//a[@class="constant"]/@href' #associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="associatedconstant.VALUE-3"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-3 + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' trait.MyTrait.html#associatedconstant.VALUE + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="associatedconstant.VALUE"]//a[@class="anchor"]/@href' #associatedconstant.VALUE + const VALUE: u32 = 20; + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-2"]//a[@class="fnname"]/@href' #tymethod.trait_function + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.trait_function-2"]//a[@class="anchor"]/@href' #method.trait_function-2 + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="fnname"]/@href' trait.MyTrait.html#tymethod.trait_function + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function + fn trait_function(&self) {} + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-3"]//a[@class="fnname"]/@href' #method.defaulted_override + // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//h4[@id="method.defaulted_override-3"]//a[@class="anchor"]/@href' #method.defaulted_override-3 + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted_override + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted_override"]//a[@class="anchor"]/@href' #method.defaulted_override + fn defaulted_override(&self) {} + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted + // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//h4[@id="method.defaulted"]//a[@class="anchor"]/@href' #method.defaulted +} + +pub struct MyStruct; diff --git a/src/test/rustdoc/trait-self-link.rs b/src/test/rustdoc/trait-self-link.rs index bac28b44012..e311dadff0e 100644 --- a/src/test/rustdoc/trait-self-link.rs +++ b/src/test/rustdoc/trait-self-link.rs @@ -1,4 +1,4 @@ -// @has trait_self_link/trait.Foo.html //a/@href ../trait_self_link/trait.Foo.html +// @has trait_self_link/trait.Foo.html //a/@href trait.Foo.html pub trait Foo {} pub struct Bar; diff --git a/src/test/ui/anon-params/anon-params-edition-hygiene.rs b/src/test/ui/anon-params/anon-params-edition-hygiene.rs index 14e11c5696f..6936205f8b9 100644 --- a/src/test/ui/anon-params/anon-params-edition-hygiene.rs +++ b/src/test/ui/anon-params/anon-params-edition-hygiene.rs @@ -2,6 +2,9 @@ // edition:2018 // aux-build:anon-params-edition-hygiene.rs +// This warning is still surfaced +#![allow(anonymous_parameters)] + #[macro_use] extern crate anon_params_edition_hygiene; diff --git a/src/test/ui/asm/inline-syntax.arm.stderr b/src/test/ui/asm/inline-syntax.arm.stderr index 56e6572fc67..bcae1d565f1 100644 --- a/src/test/ui/asm/inline-syntax.arm.stderr +++ b/src/test/ui/asm/inline-syntax.arm.stderr @@ -1,5 +1,5 @@ error: unknown directive - --> $DIR/inline-syntax.rs:22:15 + --> $DIR/inline-syntax.rs:25:15 | LL | asm!(".intel_syntax noprefix", "nop"); | ^ @@ -11,7 +11,7 @@ LL | .intel_syntax noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:25:15 + --> $DIR/inline-syntax.rs:28:15 | LL | asm!(".intel_syntax aaa noprefix", "nop"); | ^ @@ -23,7 +23,7 @@ LL | .intel_syntax aaa noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:28:15 + --> $DIR/inline-syntax.rs:31:15 | LL | asm!(".att_syntax noprefix", "nop"); | ^ @@ -35,7 +35,7 @@ LL | .att_syntax noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:31:15 + --> $DIR/inline-syntax.rs:34:15 | LL | asm!(".att_syntax bbb noprefix", "nop"); | ^ @@ -47,7 +47,7 @@ LL | .att_syntax bbb noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:34:15 + --> $DIR/inline-syntax.rs:37:15 | LL | asm!(".intel_syntax noprefix; nop"); | ^ @@ -59,7 +59,7 @@ LL | .intel_syntax noprefix; nop | ^ error: unknown directive - --> $DIR/inline-syntax.rs:40:13 + --> $DIR/inline-syntax.rs:43:13 | LL | .intel_syntax noprefix | ^ diff --git a/src/test/ui/asm/inline-syntax.rs b/src/test/ui/asm/inline-syntax.rs index 78dde5a58e1..13ded19cfea 100644 --- a/src/test/ui/asm/inline-syntax.rs +++ b/src/test/ui/asm/inline-syntax.rs @@ -2,12 +2,15 @@ // revisions: x86_64 arm //[x86_64] compile-flags: --target x86_64-unknown-linux-gnu //[x86_64] check-pass +//[x86_64_allowed] compile-flags: --target x86_64-unknown-linux-gnu +//[x86_64_allowed] check-pass //[arm] compile-flags: --target armv7-unknown-linux-gnueabihf //[arm] build-fail #![feature(no_core, lang_items, rustc_attrs)] #![crate_type = "rlib"] #![no_core] +#![cfg_attr(x86_64_allowed, allow(bad_asm_style))] #[rustc_builtin_macro] macro_rules! asm { diff --git a/src/test/ui/asm/inline-syntax.x86_64.stderr b/src/test/ui/asm/inline-syntax.x86_64.stderr index 5c03d3a002c..02b29b09013 100644 --- a/src/test/ui/asm/inline-syntax.x86_64.stderr +++ b/src/test/ui/asm/inline-syntax.x86_64.stderr @@ -1,5 +1,5 @@ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:22:15 + --> $DIR/inline-syntax.rs:25:15 | LL | asm!(".intel_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -7,31 +7,31 @@ LL | asm!(".intel_syntax noprefix", "nop"); = note: `#[warn(bad_asm_style)]` on by default warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:25:15 + --> $DIR/inline-syntax.rs:28:15 | LL | asm!(".intel_syntax aaa noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead - --> $DIR/inline-syntax.rs:28:15 + --> $DIR/inline-syntax.rs:31:15 | LL | asm!(".att_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead - --> $DIR/inline-syntax.rs:31:15 + --> $DIR/inline-syntax.rs:34:15 | LL | asm!(".att_syntax bbb noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:34:15 + --> $DIR/inline-syntax.rs:37:15 | LL | asm!(".intel_syntax noprefix; nop"); | ^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:40:13 + --> $DIR/inline-syntax.rs:43:13 | LL | .intel_syntax noprefix | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/asm/naked-invalid-attr.rs b/src/test/ui/asm/naked-invalid-attr.rs index cdb6c17454b..2576d1124c8 100644 --- a/src/test/ui/asm/naked-invalid-attr.rs +++ b/src/test/ui/asm/naked-invalid-attr.rs @@ -1,6 +1,6 @@ // Checks that #[naked] attribute can be placed on function definitions only. // -// ignore-wasm32 asm unsupported +// needs-asm-support #![feature(asm)] #![feature(naked_functions)] #![naked] //~ ERROR should be applied to a function definition diff --git a/src/test/ui/asm/sym.rs b/src/test/ui/asm/sym.rs index 634ef010e6f..18be201d640 100644 --- a/src/test/ui/asm/sym.rs +++ b/src/test/ui/asm/sym.rs @@ -1,4 +1,6 @@ // min-llvm-version: 10.0.1 +// FIXME(#84025): codegen-units=1 leads to linkage errors +// compile-flags: -C codegen-units=2 // only-x86_64 // only-linux // run-pass diff --git a/src/test/ui/assign-imm-local-twice.rs b/src/test/ui/assign-imm-local-twice.rs index c1c9bf62819..b50f6ab5deb 100644 --- a/src/test/ui/assign-imm-local-twice.rs +++ b/src/test/ui/assign-imm-local-twice.rs @@ -1,6 +1,6 @@ fn test() { let v: isize; - //~^ HELP make this binding mutable + //~^ HELP consider making this binding mutable //~| SUGGESTION mut v v = 1; //~ NOTE first assignment println!("v={}", v); diff --git a/src/test/ui/assign-imm-local-twice.stderr b/src/test/ui/assign-imm-local-twice.stderr index df0f4c4d806..bba5d8dffe4 100644 --- a/src/test/ui/assign-imm-local-twice.stderr +++ b/src/test/ui/assign-imm-local-twice.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `v` --> $DIR/assign-imm-local-twice.rs:7:5 | LL | let v: isize; - | - help: make this binding mutable: `mut v` + | - help: consider making this binding mutable: `mut v` ... LL | v = 1; | ----- first assignment to `v` diff --git a/src/test/ui/associated-types/defaults-specialization.stderr b/src/test/ui/associated-types/defaults-specialization.stderr index 920f8322813..3c7dc1fc3c9 100644 --- a/src/test/ui/associated-types/defaults-specialization.stderr +++ b/src/test/ui/associated-types/defaults-specialization.stderr @@ -15,7 +15,10 @@ LL | fn make() -> Self::Ty { | -------- type in trait ... LL | fn make() -> u8 { 0 } - | ^^ expected associated type, found `u8` + | ^^ + | | + | expected associated type, found `u8` + | help: change the output type to match the trait: `<A<T> as Tr>::Ty` | = note: expected fn pointer `fn() -> <A<T> as Tr>::Ty` found fn pointer `fn() -> u8` @@ -30,7 +33,10 @@ LL | default type Ty = bool; | ----------------------- expected this associated type LL | LL | fn make() -> bool { true } - | ^^^^ expected associated type, found `bool` + | ^^^^ + | | + | expected associated type, found `bool` + | help: change the output type to match the trait: `<B<T> as Tr>::Ty` | = note: expected fn pointer `fn() -> <B<T> as Tr>::Ty` found fn pointer `fn() -> bool` diff --git a/src/test/ui/async-await/issue-61452.stderr b/src/test/ui/async-await/issue-61452.stderr index 5eb4b548717..f2dec87baf0 100644 --- a/src/test/ui/async-await/issue-61452.stderr +++ b/src/test/ui/async-await/issue-61452.stderr @@ -13,7 +13,7 @@ LL | pub async fn g(x: usize) { | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/attr-main-2.rs b/src/test/ui/attr-main-2.rs deleted file mode 100644 index 3a51f83ba3b..00000000000 --- a/src/test/ui/attr-main-2.rs +++ /dev/null @@ -1,11 +0,0 @@ -// run-pass - -#![feature(main)] - -pub fn main() { - panic!() -} - -#[main] -fn foo() { -} diff --git a/src/test/ui/attr-main.rs b/src/test/ui/attr-main.rs deleted file mode 100644 index 9c4caaa4a42..00000000000 --- a/src/test/ui/attr-main.rs +++ /dev/null @@ -1,8 +0,0 @@ -// run-pass -// pretty-expanded FIXME #23616 - -#![feature(main)] - -#[main] -fn foo() { -} diff --git a/src/test/ui/attr.rs b/src/test/ui/attr.rs deleted file mode 100644 index 9c4caaa4a42..00000000000 --- a/src/test/ui/attr.rs +++ /dev/null @@ -1,8 +0,0 @@ -// run-pass -// pretty-expanded FIXME #23616 - -#![feature(main)] - -#[main] -fn foo() { -} diff --git a/src/test/ui/borrowck/borrowck-asm.stderr b/src/test/ui/borrowck/borrowck-asm.stderr index 3dccca78415..74cf5a55b70 100644 --- a/src/test/ui/borrowck/borrowck-asm.stderr +++ b/src/test/ui/borrowck/borrowck-asm.stderr @@ -29,7 +29,7 @@ LL | let x = 3; | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | unsafe { LL | llvm_asm!("nop" : "=r"(x)); | ^ cannot assign twice to immutable variable @@ -41,7 +41,7 @@ LL | let x = 3; | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | unsafe { LL | llvm_asm!("nop" : "+r"(x)); | ^ cannot assign twice to immutable variable diff --git a/src/test/ui/borrowck/borrowck-match-binding-is-assignment.stderr b/src/test/ui/borrowck/borrowck-match-binding-is-assignment.stderr index 5661ca52cba..dd22d7e2e2e 100644 --- a/src/test/ui/borrowck/borrowck-match-binding-is-assignment.stderr +++ b/src/test/ui/borrowck/borrowck-match-binding-is-assignment.stderr @@ -5,7 +5,7 @@ LL | x => { | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable @@ -16,7 +16,7 @@ LL | E::Foo(x) => { | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable @@ -27,7 +27,7 @@ LL | S { bar: x } => { | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable @@ -38,7 +38,7 @@ LL | (x,) => { | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable @@ -49,7 +49,7 @@ LL | [x,_,_] => { | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/borrowck/immutable-arg.stderr b/src/test/ui/borrowck/immutable-arg.stderr index 7255ca327e7..bddb0633a0b 100644 --- a/src/test/ui/borrowck/immutable-arg.stderr +++ b/src/test/ui/borrowck/immutable-arg.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign to immutable argument `_x` --> $DIR/immutable-arg.rs:2:5 | LL | fn foo(_x: u32) { - | -- help: make this binding mutable: `mut _x` + | -- help: consider making this binding mutable: `mut _x` LL | _x = 4; | ^^^^^^ cannot assign to immutable argument diff --git a/src/test/ui/borrowck/issue-45199.rs b/src/test/ui/borrowck/issue-45199.rs index cbd45cbb619..ded46e56e34 100644 --- a/src/test/ui/borrowck/issue-45199.rs +++ b/src/test/ui/borrowck/issue-45199.rs @@ -1,6 +1,6 @@ fn test_drop_replace() { let b: Box<isize>; - //~^ HELP make this binding mutable + //~^ HELP consider making this binding mutable //~| SUGGESTION mut b b = Box::new(1); //~ NOTE first assignment b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b` @@ -9,13 +9,13 @@ fn test_drop_replace() { fn test_call() { let b = Box::new(1); //~ NOTE first assignment - //~| HELP make this binding mutable + //~| HELP consider making this binding mutable //~| SUGGESTION mut b b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b` //~| NOTE cannot assign twice to immutable } -fn test_args(b: Box<i32>) { //~ HELP make this binding mutable +fn test_args(b: Box<i32>) { //~ HELP consider making this binding mutable //~| SUGGESTION mut b b = Box::new(2); //~ ERROR cannot assign to immutable argument `b` //~| NOTE cannot assign to immutable argument diff --git a/src/test/ui/borrowck/issue-45199.stderr b/src/test/ui/borrowck/issue-45199.stderr index 83b634051bb..47aa3090827 100644 --- a/src/test/ui/borrowck/issue-45199.stderr +++ b/src/test/ui/borrowck/issue-45199.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `b` --> $DIR/issue-45199.rs:6:5 | LL | let b: Box<isize>; - | - help: make this binding mutable: `mut b` + | - help: consider making this binding mutable: `mut b` ... LL | b = Box::new(1); | - first assignment to `b` @@ -16,7 +16,7 @@ LL | let b = Box::new(1); | - | | | first assignment to `b` - | help: make this binding mutable: `mut b` + | help: consider making this binding mutable: `mut b` ... LL | b = Box::new(2); | ^ cannot assign twice to immutable variable @@ -25,7 +25,7 @@ error[E0384]: cannot assign to immutable argument `b` --> $DIR/issue-45199.rs:20:5 | LL | fn test_args(b: Box<i32>) { - | - help: make this binding mutable: `mut b` + | - help: consider making this binding mutable: `mut b` LL | LL | b = Box::new(2); | ^ cannot assign to immutable argument diff --git a/src/test/ui/cast/issue-84213.fixed b/src/test/ui/cast/issue-84213.fixed new file mode 100644 index 00000000000..e1a60557a20 --- /dev/null +++ b/src/test/ui/cast/issue-84213.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +struct Something { + pub field: u32, +} + +fn main() { + let mut something = Something { field: 1337 }; + + let _pointer_to_something = &something as *const Something; + //~^ ERROR: non-primitive cast + + let _mut_pointer_to_something = &mut something as *mut Something; + //~^ ERROR: non-primitive cast +} diff --git a/src/test/ui/cast/issue-84213.rs b/src/test/ui/cast/issue-84213.rs new file mode 100644 index 00000000000..3df264bdffb --- /dev/null +++ b/src/test/ui/cast/issue-84213.rs @@ -0,0 +1,15 @@ +// run-rustfix + +struct Something { + pub field: u32, +} + +fn main() { + let mut something = Something { field: 1337 }; + + let _pointer_to_something = something as *const Something; + //~^ ERROR: non-primitive cast + + let _mut_pointer_to_something = something as *mut Something; + //~^ ERROR: non-primitive cast +} diff --git a/src/test/ui/cast/issue-84213.stderr b/src/test/ui/cast/issue-84213.stderr new file mode 100644 index 00000000000..a76aac58013 --- /dev/null +++ b/src/test/ui/cast/issue-84213.stderr @@ -0,0 +1,25 @@ +error[E0605]: non-primitive cast: `Something` as `*const Something` + --> $DIR/issue-84213.rs:10:33 + | +LL | let _pointer_to_something = something as *const Something; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast + | +help: borrow the value for the cast to be valid + | +LL | let _pointer_to_something = &something as *const Something; + | ^ + +error[E0605]: non-primitive cast: `Something` as `*mut Something` + --> $DIR/issue-84213.rs:13:37 + | +LL | let _mut_pointer_to_something = something as *mut Something; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast + | +help: borrow the value for the cast to be valid + | +LL | let _mut_pointer_to_something = &mut something as *mut Something; + | ^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0605`. diff --git a/src/test/ui/cleanup-shortcircuit.rs b/src/test/ui/cleanup-shortcircuit.rs index 4f5197a5ba9..fe867ce1fbd 100644 --- a/src/test/ui/cleanup-shortcircuit.rs +++ b/src/test/ui/cleanup-shortcircuit.rs @@ -3,6 +3,9 @@ // pretty-expanded FIXME #23616 +#![allow(deref_nullptr)] + + use std::env; pub fn main() { diff --git a/src/test/ui/closures/issue-84128.rs b/src/test/ui/closures/issue-84128.rs new file mode 100644 index 00000000000..f81d7cfaa65 --- /dev/null +++ b/src/test/ui/closures/issue-84128.rs @@ -0,0 +1,16 @@ +// test for issue 84128 +// missing suggestion for similar ADT type with diffetent generic paramenter +// on closure ReturnNoExpression + +struct Foo<T>(T); + +fn main() { + || { + if false { + return Foo(0); + } + + Foo(()) + //~^ ERROR mismatched types [E0308] + }; +} diff --git a/src/test/ui/closures/issue-84128.stderr b/src/test/ui/closures/issue-84128.stderr new file mode 100644 index 00000000000..70d9273ddf7 --- /dev/null +++ b/src/test/ui/closures/issue-84128.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/issue-84128.rs:13:13 + | +LL | Foo(()) + | ^^ expected integer, found `()` + | +note: return type inferred to be `{integer}` here + --> $DIR/issue-84128.rs:10:20 + | +LL | return Foo(0); + | ^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/command-line-diagnostics.stderr b/src/test/ui/command-line-diagnostics.stderr index b3f8d8a643f..6223ad880d6 100644 --- a/src/test/ui/command-line-diagnostics.stderr +++ b/src/test/ui/command-line-diagnostics.stderr @@ -5,7 +5,7 @@ LL | let x = 42; | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x = 43; | ^^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/compare-method/bad-self-type.rs b/src/test/ui/compare-method/bad-self-type.rs new file mode 100644 index 00000000000..f42a9e49abd --- /dev/null +++ b/src/test/ui/compare-method/bad-self-type.rs @@ -0,0 +1,26 @@ +use std::future::Future; +use std::task::{Context, Poll}; + +fn main() {} + +struct MyFuture {} + +impl Future for MyFuture { + type Output = (); + fn poll(self, _: &mut Context<'_>) -> Poll<()> { + //~^ ERROR method `poll` has an incompatible type for trait + todo!() + } +} + +trait T { + fn foo(self); + fn bar(self) -> Option<()>; +} + +impl T for MyFuture { + fn foo(self: Box<Self>) {} + //~^ ERROR method `foo` has an incompatible type for trait + fn bar(self) {} + //~^ ERROR method `bar` has an incompatible type for trait +} diff --git a/src/test/ui/compare-method/bad-self-type.stderr b/src/test/ui/compare-method/bad-self-type.stderr new file mode 100644 index 00000000000..76f91fbf241 --- /dev/null +++ b/src/test/ui/compare-method/bad-self-type.stderr @@ -0,0 +1,46 @@ +error[E0053]: method `poll` has an incompatible type for trait + --> $DIR/bad-self-type.rs:10:13 + | +LL | fn poll(self, _: &mut Context<'_>) -> Poll<()> { + | ^^^^ + | | + | expected struct `Pin`, found struct `MyFuture` + | help: change the self-receiver type to match the trait: `self: Pin<&mut MyFuture>` + | + = note: expected fn pointer `fn(Pin<&mut MyFuture>, &mut Context<'_>) -> Poll<_>` + found fn pointer `fn(MyFuture, &mut Context<'_>) -> Poll<_>` + +error[E0053]: method `foo` has an incompatible type for trait + --> $DIR/bad-self-type.rs:22:18 + | +LL | fn foo(self); + | ---- type in trait +... +LL | fn foo(self: Box<Self>) {} + | ------^^^^^^^^^ + | | | + | | expected struct `MyFuture`, found struct `Box` + | help: change the self-receiver type to match the trait: `self` + | + = note: expected fn pointer `fn(MyFuture)` + found fn pointer `fn(Box<MyFuture>)` + +error[E0053]: method `bar` has an incompatible type for trait + --> $DIR/bad-self-type.rs:24:18 + | +LL | fn bar(self) -> Option<()>; + | ---------- type in trait +... +LL | fn bar(self) {} + | ^ expected enum `Option`, found `()` + | + = note: expected fn pointer `fn(MyFuture) -> Option<()>` + found fn pointer `fn(MyFuture)` +help: change the output type to match the trait + | +LL | fn bar(self) -> Option<()> {} + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0053`. diff --git a/src/test/ui/compare-method/reordered-type-param.stderr b/src/test/ui/compare-method/reordered-type-param.stderr index f1f8a663f21..d581628ea48 100644 --- a/src/test/ui/compare-method/reordered-type-param.stderr +++ b/src/test/ui/compare-method/reordered-type-param.stderr @@ -5,8 +5,10 @@ LL | fn b<C:Clone,D>(&self, x: C) -> C; | - type in trait ... LL | fn b<F:Clone,G>(&self, _x: G) -> G { panic!() } - | - - ^ expected type parameter `F`, found type parameter `G` - | | | + | - - ^ + | | | | + | | | expected type parameter `F`, found type parameter `G` + | | | help: change the parameter type to match the trait: `F` | | found type parameter | expected type parameter | diff --git a/src/test/ui/const-generics/diagnostics.stderr b/src/test/ui/const-generics/diagnostics.stderr index 7d038ff955d..c8ee6ad61ec 100644 --- a/src/test/ui/const-generics/diagnostics.stderr +++ b/src/test/ui/const-generics/diagnostics.stderr @@ -31,9 +31,12 @@ error[E0747]: type provided when a constant was expected --> $DIR/diagnostics.rs:12:19 | LL | impl<N> Foo for B<N> {} - | - ^ - | | - | help: consider changing this type paramater to a `const`-generic: `const N: u8` + | ^ + | +help: consider changing this type parameter to be a `const` generic + | +LL | impl<const N: u8> Foo for B<N> {} + | ^^^^^^^^^^^ error[E0747]: unresolved item provided when a constant was expected --> $DIR/diagnostics.rs:16:32 diff --git a/src/test/ui/consts/const-int-unchecked.rs b/src/test/ui/consts/const-int-unchecked.rs index 41d8f7a0972..2ccc5d27bbb 100644 --- a/src/test/ui/consts/const-int-unchecked.rs +++ b/src/test/ui/consts/const-int-unchecked.rs @@ -186,4 +186,13 @@ const _: i32 = unsafe { std::intrinsics::unchecked_rem(i32::MIN, -1) }; //~^ ERROR any use of this value will cause an error //~| WARN this was previously accepted by the compiler but is being phased out +// capture fault with zero value + +const _: u32 = unsafe { std::intrinsics::ctlz_nonzero(0) }; +//~^ ERROR any use of this value will cause an error +//~| WARN this was previously accepted by the compiler but is being phased out +const _: u32 = unsafe { std::intrinsics::cttz_nonzero(0) }; +//~^ ERROR any use of this value will cause an error +//~| WARN this was previously accepted by the compiler but is being phased out + fn main() {} diff --git a/src/test/ui/consts/const-int-unchecked.stderr b/src/test/ui/consts/const-int-unchecked.stderr index e5ecbbc71a7..999b26543e2 100644 --- a/src/test/ui/consts/const-int-unchecked.stderr +++ b/src/test/ui/consts/const-int-unchecked.stderr @@ -516,5 +516,27 @@ LL | const _: i32 = unsafe { std::intrinsics::unchecked_rem(i32::MIN, -1) }; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> -error: aborting due to 47 previous errors +error: any use of this value will cause an error + --> $DIR/const-int-unchecked.rs:191:25 + | +LL | const _: u32 = unsafe { std::intrinsics::ctlz_nonzero(0) }; + | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | `ctlz_nonzero` called on 0 + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: any use of this value will cause an error + --> $DIR/const-int-unchecked.rs:194:25 + | +LL | const _: u32 = unsafe { std::intrinsics::cttz_nonzero(0) }; + | ------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | `cttz_nonzero` called on 0 + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800> + +error: aborting due to 49 previous errors diff --git a/src/test/ui/derives/derive-macro-const-default.rs b/src/test/ui/derives/derive-macro-const-default.rs new file mode 100644 index 00000000000..a844f2d2023 --- /dev/null +++ b/src/test/ui/derives/derive-macro-const-default.rs @@ -0,0 +1,14 @@ +// check-pass +#![allow(incomplete_features)] +#![feature(const_generics_defaults)] + +#[derive(Clone, PartialEq, Debug)] +struct Example<T, const N: usize = 1usize>([T; N]); + +fn main() { + let a = Example([(); 16]); + let b = a.clone(); + if a != b { + let _c = format!("{:?}", a); + } +} diff --git a/src/test/ui/error-codes/E0137.rs b/src/test/ui/error-codes/E0137.rs deleted file mode 100644 index b8299c71bdf..00000000000 --- a/src/test/ui/error-codes/E0137.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![feature(main)] - -#[main] -fn foo() {} - -#[main] -fn f() {} -//~^ ERROR E0137 diff --git a/src/test/ui/error-codes/E0137.stderr b/src/test/ui/error-codes/E0137.stderr deleted file mode 100644 index f4d5e10369a..00000000000 --- a/src/test/ui/error-codes/E0137.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0137]: multiple functions with a `#[main]` attribute - --> $DIR/E0137.rs:7:1 - | -LL | fn foo() {} - | ----------- first `#[main]` function -... -LL | fn f() {} - | ^^^^^^^^^ additional `#[main]` function - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0137`. diff --git a/src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs b/src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs index 74c6e501c91..7b731a1d71d 100644 --- a/src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs +++ b/src/test/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs @@ -19,7 +19,9 @@ // revisions: no thin fat //[no]compile-flags: -C lto=no -//[thin]compile-flags: -C lto=thin +// FIXME(#83854) running this revision with 1 CGU triggers llvm assert in register allocator +// when executed in i686-gnu-nopt runner. +//[thin]compile-flags: -C lto=thin -Ccodegen-units=2 //[fat]compile-flags: -C lto=fat #![feature(core_panic)] diff --git a/src/test/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs b/src/test/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs index bc15fcb0e39..32e6d0c90b2 100644 --- a/src/test/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs +++ b/src/test/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs @@ -40,7 +40,9 @@ //[no1]compile-flags: -C opt-level=1 -C lto=no //[no2]compile-flags: -C opt-level=2 -C lto=no //[no3]compile-flags: -C opt-level=3 -C lto=no -//[thin0]compile-flags: -C opt-level=0 -C lto=thin +// FIXME(#83854) running this revision with 1 CGU triggers llvm assert in register allocator +// when executed in dist-i586-gnu-i586-i686-musl runner. +//[thin0]compile-flags: -C opt-level=0 -C lto=thin -Ccodegen-units=2 //[thin1]compile-flags: -C opt-level=1 -C lto=thin //[thin2]compile-flags: -C opt-level=2 -C lto=thin //[thin3]compile-flags: -C opt-level=3 -C lto=thin diff --git a/src/test/ui/feature-gates/feature-gate-main.rs b/src/test/ui/feature-gates/feature-gate-main.rs deleted file mode 100644 index 9c304a1792f..00000000000 --- a/src/test/ui/feature-gates/feature-gate-main.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[main] -fn foo() {} //~ ERROR: declaration of a non-standard `#[main]` function may change over time diff --git a/src/test/ui/feature-gates/feature-gate-main.stderr b/src/test/ui/feature-gates/feature-gate-main.stderr deleted file mode 100644 index f0ef3b38c62..00000000000 --- a/src/test/ui/feature-gates/feature-gate-main.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: declaration of a non-standard `#[main]` function may change over time, for now a top-level `fn main()` is required - --> $DIR/feature-gate-main.rs:2:1 - | -LL | fn foo() {} - | ^^^^^^^^^^^ - | - = note: see issue #29634 <https://github.com/rust-lang/rust/issues/29634> for more information - = help: add `#![feature(main)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-naked_functions.rs b/src/test/ui/feature-gates/feature-gate-naked_functions.rs index 06bddc422cf..71ca5b9373a 100644 --- a/src/test/ui/feature-gates/feature-gate-naked_functions.rs +++ b/src/test/ui/feature-gates/feature-gate-naked_functions.rs @@ -1,3 +1,4 @@ +// needs-asm-support #![feature(asm)] #[naked] diff --git a/src/test/ui/feature-gates/feature-gate-naked_functions.stderr b/src/test/ui/feature-gates/feature-gate-naked_functions.stderr index d95561d2013..653d7b738da 100644 --- a/src/test/ui/feature-gates/feature-gate-naked_functions.stderr +++ b/src/test/ui/feature-gates/feature-gate-naked_functions.stderr @@ -1,5 +1,5 @@ error[E0658]: the `#[naked]` attribute is an experimental feature - --> $DIR/feature-gate-naked_functions.rs:3:1 + --> $DIR/feature-gate-naked_functions.rs:4:1 | LL | #[naked] | ^^^^^^^^ @@ -8,7 +8,7 @@ LL | #[naked] = help: add `#![feature(naked_functions)]` to the crate attributes to enable error[E0658]: the `#[naked]` attribute is an experimental feature - --> $DIR/feature-gate-naked_functions.rs:9:1 + --> $DIR/feature-gate-naked_functions.rs:10:1 | LL | #[naked] | ^^^^^^^^ diff --git a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.rs b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.rs index 8945360b7be..37348e476d4 100644 --- a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.rs +++ b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.rs @@ -12,7 +12,7 @@ trait NonObjectSafe3 { } trait NonObjectSafe4 { - fn foo(&self, &Self); + fn foo(&self, s: &Self); } fn takes_non_object_safe_ref<T>(obj: &dyn NonObjectSafe1) { diff --git a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr index b61d5604455..12195bc1071 100644 --- a/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr +++ b/src/test/ui/feature-gates/feature-gate-object_safe_for_dispatch.stderr @@ -57,12 +57,12 @@ LL | fn return_non_object_safe_rc() -> std::rc::Rc<dyn NonObjectSafe4> { | = help: consider moving `foo` to another trait note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/feature-gate-object_safe_for_dispatch.rs:15:19 + --> $DIR/feature-gate-object_safe_for_dispatch.rs:15:22 | LL | trait NonObjectSafe4 { | -------------- this trait cannot be made into an object... -LL | fn foo(&self, &Self); - | ^^^^^ ...because method `foo` references the `Self` type in this parameter +LL | fn foo(&self, s: &Self); + | ^^^^^ ...because method `foo` references the `Self` type in this parameter error[E0038]: the trait `NonObjectSafe1` cannot be made into an object --> $DIR/feature-gate-object_safe_for_dispatch.rs:38:16 diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs index 07167fa695e..d1fbd38ce07 100644 --- a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs +++ b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs @@ -11,8 +11,8 @@ #![macro_export] //~^ ERROR: `macro_export` attribute cannot be used at crate level -#![main] -//~^ ERROR: `main` attribute cannot be used at crate level +#![rustc_main] //~ ERROR: the `#[rustc_main]` attribute is used internally to specify +//~^ ERROR: `rustc_main` attribute cannot be used at crate level #![start] //~^ ERROR: `start` attribute cannot be used at crate level #![repr()] @@ -106,24 +106,6 @@ mod export_name { //~| NOTE not a function or static } -#[main] -//~^ ERROR: `main` attribute can only be used on functions -mod main { - mod inner { #![main] } - //~^ ERROR: `main` attribute can only be used on functions - - // for `fn f()` case, see feature-gate-main.rs - - #[main] struct S; - //~^ ERROR: `main` attribute can only be used on functions - - #[main] type T = S; - //~^ ERROR: `main` attribute can only be used on functions - - #[main] impl S { } - //~^ ERROR: `main` attribute can only be used on functions -} - #[start] //~^ ERROR: `start` attribute can only be used on functions mod start { diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index 33a5021cde4..ae2c428cc75 100644 --- a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -1,3 +1,11 @@ +error[E0658]: the `#[rustc_main]` attribute is used internally to specify test entry point function + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:14:1 + | +LL | #![rustc_main] + | ^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + error: attribute must be of the form `#[inline]` or `#[inline(always|never)]` --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:40:5 | @@ -8,62 +16,32 @@ LL | #[inline = "2100"] fn f() { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571> -error: `main` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:109:1 - | -LL | #[main] - | ^^^^^^^ - -error: `main` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:112:17 - | -LL | mod inner { #![main] } - | ^^^^^^^^ - -error: `main` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:117:5 - | -LL | #[main] struct S; - | ^^^^^^^ - -error: `main` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:120:5 - | -LL | #[main] type T = S; - | ^^^^^^^ - -error: `main` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:123:5 - | -LL | #[main] impl S { } - | ^^^^^^^ - error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:127:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:109:1 | LL | #[start] | ^^^^^^^^ error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:130:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:112:17 | LL | mod inner { #![start] } | ^^^^^^^^^ error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:135:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:117:5 | LL | #[start] struct S; | ^^^^^^^^ error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:138:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:120:5 | LL | #[start] type T = S; | ^^^^^^^^ error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:141:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:123:5 | LL | #[start] impl S { } | ^^^^^^^^ @@ -137,11 +115,11 @@ error: `macro_export` attribute cannot be used at crate level LL | #![macro_export] | ^^^^^^^^^^^^^^^^ -error: `main` attribute cannot be used at crate level +error: `rustc_main` attribute cannot be used at crate level --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:14:1 | -LL | #![main] - | ^^^^^^^^ +LL | #![rustc_main] + | ^^^^^^^^^^^^^^ error: `start` attribute cannot be used at crate level --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:16:1 @@ -245,6 +223,7 @@ error: attribute should be applied to a function or static LL | #[export_name = "2200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^ ---------- not a function or static -error: aborting due to 36 previous errors +error: aborting due to 32 previous errors -For more information about this error, try `rustc --explain E0518`. +Some errors have detailed explanations: E0518, E0658. +For more information about an error, try `rustc --explain E0518`. diff --git a/src/test/ui/feature-gates/thread-local-const-init.rs b/src/test/ui/feature-gates/thread-local-const-init.rs new file mode 100644 index 00000000000..6584ffa7cf9 --- /dev/null +++ b/src/test/ui/feature-gates/thread-local-const-init.rs @@ -0,0 +1,4 @@ +thread_local!(static X: u32 = const { 0 }); +//~^ ERROR: use of unstable library feature 'thread_local_const_init' + +fn main() {} diff --git a/src/test/ui/feature-gates/thread-local-const-init.stderr b/src/test/ui/feature-gates/thread-local-const-init.stderr new file mode 100644 index 00000000000..a35b18a9c29 --- /dev/null +++ b/src/test/ui/feature-gates/thread-local-const-init.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of unstable library feature 'thread_local_const_init' + --> $DIR/thread-local-const-init.rs:1:1 + | +LL | thread_local!(static X: u32 = const { 0 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #84223 <https://github.com/rust-lang/rust/issues/84223> for more information + = help: add `#![feature(thread_local_const_init)]` to the crate attributes to enable + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr b/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr index 638a0093fb2..d37670db085 100644 --- a/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr +++ b/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr @@ -5,8 +5,10 @@ LL | fn foo<A: Debug>(&self, a: &A, b: &impl Debug); | -- type in trait ... LL | fn foo<B: Debug>(&self, a: &impl Debug, b: &B) { } - | - ^^^^^^^^^^^ expected type parameter `B`, found type parameter `impl Debug` - | | + | - ^^^^^^^^^^^ + | | | + | | expected type parameter `B`, found type parameter `impl Debug` + | | help: change the parameter type to match the trait: `&B` | expected type parameter | = note: expected fn pointer `fn(&(), &B, &impl Debug)` diff --git a/src/test/ui/impl-trait/trait_type.stderr b/src/test/ui/impl-trait/trait_type.stderr index 961bb735118..bea24339837 100644 --- a/src/test/ui/impl-trait/trait_type.stderr +++ b/src/test/ui/impl-trait/trait_type.stderr @@ -1,8 +1,11 @@ error[E0053]: method `fmt` has an incompatible type for trait - --> $DIR/trait_type.rs:7:4 + --> $DIR/trait_type.rs:7:21 | LL | fn fmt(&self, x: &str) -> () { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability + | ^^^^ + | | + | types differ in mutability + | help: change the parameter type to match the trait: `&mut Formatter<'_>` | = note: expected fn pointer `fn(&MyType, &mut Formatter<'_>) -> Result<(), std::fmt::Error>` found fn pointer `fn(&MyType, &str)` diff --git a/src/test/ui/intrinsics/intrinsic-alignment.rs b/src/test/ui/intrinsics/intrinsic-alignment.rs index 2bf40db5ad2..592409ba89f 100644 --- a/src/test/ui/intrinsics/intrinsic-alignment.rs +++ b/src/test/ui/intrinsics/intrinsic-alignment.rs @@ -1,7 +1,7 @@ // run-pass // ignore-wasm32-bare seems not important to test here -#![feature(intrinsics, main)] +#![feature(intrinsics)] mod rusti { extern "rust-intrinsic" { @@ -21,7 +21,6 @@ mod rusti { target_os = "solaris", target_os = "vxworks"))] mod m { - #[main] #[cfg(target_arch = "x86")] pub fn main() { unsafe { @@ -30,7 +29,6 @@ mod m { } } - #[main] #[cfg(not(target_arch = "x86"))] pub fn main() { unsafe { @@ -42,7 +40,6 @@ mod m { #[cfg(target_env = "sgx")] mod m { - #[main] #[cfg(target_arch = "x86_64")] pub fn main() { unsafe { @@ -54,7 +51,6 @@ mod m { #[cfg(target_os = "windows")] mod m { - #[main] pub fn main() { unsafe { assert_eq!(::rusti::pref_align_of::<u64>(), 8); @@ -62,3 +58,7 @@ mod m { } } } + +fn main() { + m::main(); +} diff --git a/src/test/ui/issues/issue-13033.stderr b/src/test/ui/issues/issue-13033.stderr index 57447fa48aa..6c3651ff121 100644 --- a/src/test/ui/issues/issue-13033.stderr +++ b/src/test/ui/issues/issue-13033.stderr @@ -8,7 +8,7 @@ LL | fn bar(&mut self, other: &dyn Foo) {} | ^^^^^^^^ | | | types differ in mutability - | help: consider changing the mutability to match the trait: `&mut dyn Foo` + | help: change the parameter type to match the trait: `&mut dyn Foo` | = note: expected fn pointer `fn(&mut Baz, &mut dyn Foo)` found fn pointer `fn(&mut Baz, &dyn Foo)` diff --git a/src/test/ui/issues/issue-20225.stderr b/src/test/ui/issues/issue-20225.stderr index 3bcc50ded84..6f4813ca623 100644 --- a/src/test/ui/issues/issue-20225.stderr +++ b/src/test/ui/issues/issue-20225.stderr @@ -1,33 +1,42 @@ error[E0053]: method `call` has an incompatible type for trait - --> $DIR/issue-20225.rs:6:3 + --> $DIR/issue-20225.rs:6:43 | LL | impl<'a, T> Fn<(&'a T,)> for Foo { | - this type parameter LL | extern "rust-call" fn call(&self, (_,): (T,)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T` + | ^^^^ + | | + | expected `&T`, found type parameter `T` + | help: change the parameter type to match the trait: `(&'a T,)` | = note: expected fn pointer `extern "rust-call" fn(&Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(&Foo, (T,))` error[E0053]: method `call_mut` has an incompatible type for trait - --> $DIR/issue-20225.rs:11:3 + --> $DIR/issue-20225.rs:11:51 | LL | impl<'a, T> FnMut<(&'a T,)> for Foo { | - this type parameter LL | extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T` + | ^^^^ + | | + | expected `&T`, found type parameter `T` + | help: change the parameter type to match the trait: `(&'a T,)` | = note: expected fn pointer `extern "rust-call" fn(&mut Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(&mut Foo, (T,))` error[E0053]: method `call_once` has an incompatible type for trait - --> $DIR/issue-20225.rs:18:3 + --> $DIR/issue-20225.rs:18:47 | LL | impl<'a, T> FnOnce<(&'a T,)> for Foo { | - this type parameter ... LL | extern "rust-call" fn call_once(self, (_,): (T,)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T` + | ^^^^ + | | + | expected `&T`, found type parameter `T` + | help: change the parameter type to match the trait: `(&'a T,)` | = note: expected fn pointer `extern "rust-call" fn(Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(Foo, (T,))` diff --git a/src/test/ui/issues/issue-21332.stderr b/src/test/ui/issues/issue-21332.stderr index 35863fbebe3..d92966da17c 100644 --- a/src/test/ui/issues/issue-21332.stderr +++ b/src/test/ui/issues/issue-21332.stderr @@ -1,8 +1,11 @@ error[E0053]: method `next` has an incompatible type for trait - --> $DIR/issue-21332.rs:5:5 + --> $DIR/issue-21332.rs:5:27 | LL | fn next(&mut self) -> Result<i32, i32> { Ok(7) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found enum `Result` + | ^^^^^^^^^^^^^^^^ + | | + | expected enum `Option`, found enum `Result` + | help: change the output type to match the trait: `Option<i32>` | = note: expected fn pointer `fn(&mut S) -> Option<i32>` found fn pointer `fn(&mut S) -> Result<i32, i32>` diff --git a/src/test/ui/issues/issue-35869.stderr b/src/test/ui/issues/issue-35869.stderr index f80561bf6be..71b2a9df095 100644 --- a/src/test/ui/issues/issue-35869.stderr +++ b/src/test/ui/issues/issue-35869.stderr @@ -5,7 +5,10 @@ LL | fn foo(_: fn(u8) -> ()); | ------------ type in trait ... LL | fn foo(_: fn(u16) -> ()) {} - | ^^^^^^^^^^^^^ expected `u8`, found `u16` + | ^^^^^^^^^^^^^ + | | + | expected `u8`, found `u16` + | help: change the parameter type to match the trait: `fn(u8)` | = note: expected fn pointer `fn(fn(u8))` found fn pointer `fn(fn(u16))` @@ -17,7 +20,10 @@ LL | fn bar(_: Option<u8>); | ---------- type in trait ... LL | fn bar(_: Option<u16>) {} - | ^^^^^^^^^^^ expected `u8`, found `u16` + | ^^^^^^^^^^^ + | | + | expected `u8`, found `u16` + | help: change the parameter type to match the trait: `Option<u8>` | = note: expected fn pointer `fn(Option<u8>)` found fn pointer `fn(Option<u16>)` @@ -29,7 +35,10 @@ LL | fn baz(_: (u8, u16)); | --------- type in trait ... LL | fn baz(_: (u16, u16)) {} - | ^^^^^^^^^^ expected `u8`, found `u16` + | ^^^^^^^^^^ + | | + | expected `u8`, found `u16` + | help: change the parameter type to match the trait: `(u8, u16)` | = note: expected fn pointer `fn((u8, _))` found fn pointer `fn((u16, _))` @@ -41,7 +50,10 @@ LL | fn qux() -> u8; | -- type in trait ... LL | fn qux() -> u16 { 5u16 } - | ^^^ expected `u8`, found `u16` + | ^^^ + | | + | expected `u8`, found `u16` + | help: change the output type to match the trait: `u8` | = note: expected fn pointer `fn() -> u8` found fn pointer `fn() -> u16` diff --git a/src/test/ui/issues/issue-69225-SCEVAddExpr-wrap-flag.rs b/src/test/ui/issues/issue-69225-SCEVAddExpr-wrap-flag.rs index 6e030f1cc48..e04dec24e44 100644 --- a/src/test/ui/issues/issue-69225-SCEVAddExpr-wrap-flag.rs +++ b/src/test/ui/issues/issue-69225-SCEVAddExpr-wrap-flag.rs @@ -1,5 +1,6 @@ // run-fail // compile-flags: -C opt-level=3 +// min-llvm-version: 11.0 // error-pattern: index out of bounds: the len is 0 but the index is 16777216 // ignore-wasm no panic or subprocess support // ignore-emscripten no panic or subprocess support diff --git a/src/test/ui/issues/issue-78720.rs b/src/test/ui/issues/issue-78720.rs index 57615d1a207..4cdb9f49113 100644 --- a/src/test/ui/issues/issue-78720.rs +++ b/src/test/ui/issues/issue-78720.rs @@ -4,7 +4,7 @@ fn server() -> impl { } trait FilterBase2 { - fn map2<F>(self, F) -> Map2<F> {} + fn map2<F>(self, f: F) -> Map2<F> {} //~^ ERROR mismatched types //~^^ ERROR the size for values of type `Self` cannot be known at compilation time } diff --git a/src/test/ui/issues/issue-78720.stderr b/src/test/ui/issues/issue-78720.stderr index a3a14e34acb..ee15826d189 100644 --- a/src/test/ui/issues/issue-78720.stderr +++ b/src/test/ui/issues/issue-78720.stderr @@ -25,10 +25,10 @@ LL | struct Map2<Segment2, F> { | ^^^ error[E0308]: mismatched types - --> $DIR/issue-78720.rs:7:36 + --> $DIR/issue-78720.rs:7:39 | -LL | fn map2<F>(self, F) -> Map2<F> {} - | ^^ expected struct `Map2`, found `()` +LL | fn map2<F>(self, f: F) -> Map2<F> {} + | ^^ expected struct `Map2`, found `()` | = note: expected struct `Map2<F>` found unit type `()` @@ -36,17 +36,17 @@ LL | fn map2<F>(self, F) -> Map2<F> {} error[E0277]: the size for values of type `Self` cannot be known at compilation time --> $DIR/issue-78720.rs:7:16 | -LL | fn map2<F>(self, F) -> Map2<F> {} +LL | fn map2<F>(self, f: F) -> Map2<F> {} | ^^^^ doesn't have a size known at compile-time | = help: unsized fn params are gated as an unstable feature help: consider further restricting `Self` | -LL | fn map2<F>(self, F) -> Map2<F> where Self: Sized {} - | ^^^^^^^^^^^^^^^^^ +LL | fn map2<F>(self, f: F) -> Map2<F> where Self: Sized {} + | ^^^^^^^^^^^^^^^^^ help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn map2<F>(&self, F) -> Map2<F> {} +LL | fn map2<F>(&self, f: F) -> Map2<F> {} | ^ error: aborting due to 4 previous errors diff --git a/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs b/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs new file mode 100644 index 00000000000..7f0ea730dd3 --- /dev/null +++ b/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.rs @@ -0,0 +1,8 @@ +// check-fail + +struct Foo {} +impl Foo { + fn bar(foo: Foo<Target = usize>) {} + //~^ associated type bindings are not allowed here +} +fn main() {} diff --git a/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr b/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr new file mode 100644 index 00000000000..f7bdee6336e --- /dev/null +++ b/src/test/ui/lifetimes/issue-83753-invalid-associated-type-supertrait-hrtb.stderr @@ -0,0 +1,9 @@ +error[E0229]: associated type bindings are not allowed here + --> $DIR/issue-83753-invalid-associated-type-supertrait-hrtb.rs:5:21 + | +LL | fn bar(foo: Foo<Target = usize>) {} + | ^^^^^^^^^^^^^^ associated type not allowed here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0229`. diff --git a/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.rs b/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.rs new file mode 100644 index 00000000000..604687ce711 --- /dev/null +++ b/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.rs @@ -0,0 +1,7 @@ +// check-fail + +static STATIC_VAR_FIVE: &One(); +//~^ cannot find type +//~| free static item without body + +fn main() {} diff --git a/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.stderr b/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.stderr new file mode 100644 index 00000000000..e57933da558 --- /dev/null +++ b/src/test/ui/lifetimes/issue-83907-invalid-fn-like-path.stderr @@ -0,0 +1,17 @@ +error: free static item without body + --> $DIR/issue-83907-invalid-fn-like-path.rs:3:1 + | +LL | static STATIC_VAR_FIVE: &One(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: provide a definition for the static: `= <expr>;` + +error[E0412]: cannot find type `One` in this scope + --> $DIR/issue-83907-invalid-fn-like-path.rs:3:26 + | +LL | static STATIC_VAR_FIVE: &One(); + | ^^^ not found in this scope + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0412`. diff --git a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr index 5751c319489..bbd62902d9f 100644 --- a/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr +++ b/src/test/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-2.nll.stderr @@ -12,7 +12,7 @@ error[E0384]: cannot assign to immutable argument `y` --> $DIR/ex3-both-anon-regions-one-is-struct-2.rs:4:5 | LL | fn foo(mut x: Ref, y: &u32) { - | - help: make this binding mutable: `mut y` + | - help: consider making this binding mutable: `mut y` LL | y = x.b; | ^^^^^^^ cannot assign to immutable argument diff --git a/src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr b/src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr index c646912d3b6..b47a47d631e 100644 --- a/src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr +++ b/src/test/ui/lifetimes/lifetime-errors/liveness-assign-imm-local-notes.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `x` --> $DIR/liveness-assign-imm-local-notes.rs:10:9 | LL | let x; - | - help: make this binding mutable: `mut x` + | - help: consider making this binding mutable: `mut x` ... LL | x = 2; | ----- first assignment to `x` @@ -13,7 +13,7 @@ error[E0384]: cannot assign twice to immutable variable `x` --> $DIR/liveness-assign-imm-local-notes.rs:21:13 | LL | let x; - | - help: make this binding mutable: `mut x` + | - help: consider making this binding mutable: `mut x` ... LL | x = 2; | ----- first assignment to `x` @@ -24,7 +24,7 @@ error[E0384]: cannot assign twice to immutable variable `x` --> $DIR/liveness-assign-imm-local-notes.rs:30:13 | LL | let x; - | - help: make this binding mutable: `mut x` + | - help: consider making this binding mutable: `mut x` ... LL | x = 1; | ^^^^^ cannot assign twice to immutable variable @@ -33,7 +33,7 @@ error[E0384]: cannot assign twice to immutable variable `x` --> $DIR/liveness-assign-imm-local-notes.rs:32:13 | LL | let x; - | - help: make this binding mutable: `mut x` + | - help: consider making this binding mutable: `mut x` ... LL | x = 1; | ----- first assignment to `x` diff --git a/src/test/ui/linkage-attr/linkage-detect-extern-generated-name-collision.rs b/src/test/ui/linkage-attr/linkage-detect-extern-generated-name-collision.rs index 7b2f5365aca..3a0910658b7 100644 --- a/src/test/ui/linkage-attr/linkage-detect-extern-generated-name-collision.rs +++ b/src/test/ui/linkage-attr/linkage-detect-extern-generated-name-collision.rs @@ -4,6 +4,8 @@ // build-fail // aux-build:def_colliding_external.rs +// FIXME(#83838) codegen-units=1 triggers llvm asserts +// compile-flags: -Ccodegen-units=16 extern crate def_colliding_external as dep1; diff --git a/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.rs b/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.rs index b712f3225ed..c1df9ccef22 100644 --- a/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.rs +++ b/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.rs @@ -1,5 +1,6 @@ // build-fail - +// FIXME(#83838) codegen-units=1 triggers llvm asserts +// compile-flags: -Ccodegen-units=16 #![feature(linkage)] mod dep1 { diff --git a/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.stderr b/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.stderr index d008acc6e7c..7e395e67378 100644 --- a/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.stderr +++ b/src/test/ui/linkage-attr/linkage-detect-local-generated-name-collision.stderr @@ -1,5 +1,5 @@ error: symbol `collision` is already defined - --> $DIR/linkage-detect-local-generated-name-collision.rs:9:9 + --> $DIR/linkage-detect-local-generated-name-collision.rs:10:9 | LL | pub static collision: *const i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/lint/dead-code/lint-dead-code-2.rs b/src/test/ui/lint/dead-code/lint-dead-code-2.rs index 88db4f88c3f..2cfe6e539db 100644 --- a/src/test/ui/lint/dead-code/lint-dead-code-2.rs +++ b/src/test/ui/lint/dead-code/lint-dead-code-2.rs @@ -1,6 +1,6 @@ #![allow(unused_variables)] #![deny(dead_code)] -#![feature(main, start)] +#![feature(rustc_attrs, start)] struct Foo; @@ -21,7 +21,7 @@ fn live_fn() {} fn dead_fn() {} //~ ERROR: function is never used -#[main] +#[rustc_main] fn dead_fn2() {} //~ ERROR: function is never used fn used_fn() {} diff --git a/src/test/ui/lint/lint-deref-nullptr.rs b/src/test/ui/lint/lint-deref-nullptr.rs new file mode 100644 index 00000000000..d052dbd9b64 --- /dev/null +++ b/src/test/ui/lint/lint-deref-nullptr.rs @@ -0,0 +1,38 @@ +// test the deref_nullptr lint + +#![deny(deref_nullptr)] + +use std::ptr; + +struct Struct { + field: u8, +} + +fn f() { + unsafe { + let a = 1; + let ub = *(a as *const i32); + let ub = *(0 as *const i32); + //~^ ERROR dereferencing a null pointer + let ub = *ptr::null::<i32>(); + //~^ ERROR dereferencing a null pointer + let ub = *ptr::null_mut::<i32>(); + //~^ ERROR dereferencing a null pointer + let ub = *(ptr::null::<i16>() as *const i32); + //~^ ERROR dereferencing a null pointer + let ub = *(ptr::null::<i16>() as *mut i32 as *mut usize as *const u8); + //~^ ERROR dereferencing a null pointer + let ub = &*ptr::null::<i32>(); + //~^ ERROR dereferencing a null pointer + let ub = &*ptr::null_mut::<i32>(); + //~^ ERROR dereferencing a null pointer + ptr::addr_of!(*ptr::null::<i32>()); + //~^ ERROR dereferencing a null pointer + ptr::addr_of_mut!(*ptr::null_mut::<i32>()); + //~^ ERROR dereferencing a null pointer + let offset = ptr::addr_of!((*ptr::null::<Struct>()).field); + //~^ ERROR dereferencing a null pointer + } +} + +fn main() {} diff --git a/src/test/ui/lint/lint-deref-nullptr.stderr b/src/test/ui/lint/lint-deref-nullptr.stderr new file mode 100644 index 00000000000..c6f432e4e42 --- /dev/null +++ b/src/test/ui/lint/lint-deref-nullptr.stderr @@ -0,0 +1,68 @@ +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:15:18 + | +LL | let ub = *(0 as *const i32); + | ^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + | +note: the lint level is defined here + --> $DIR/lint-deref-nullptr.rs:3:9 + | +LL | #![deny(deref_nullptr)] + | ^^^^^^^^^^^^^ + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:17:18 + | +LL | let ub = *ptr::null::<i32>(); + | ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:19:18 + | +LL | let ub = *ptr::null_mut::<i32>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:21:18 + | +LL | let ub = *(ptr::null::<i16>() as *const i32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:23:18 + | +LL | let ub = *(ptr::null::<i16>() as *mut i32 as *mut usize as *const u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:25:19 + | +LL | let ub = &*ptr::null::<i32>(); + | ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:27:19 + | +LL | let ub = &*ptr::null_mut::<i32>(); + | ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:29:23 + | +LL | ptr::addr_of!(*ptr::null::<i32>()); + | ^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:31:27 + | +LL | ptr::addr_of_mut!(*ptr::null_mut::<i32>()); + | ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: dereferencing a null pointer + --> $DIR/lint-deref-nullptr.rs:33:36 + | +LL | let offset = ptr::addr_of!((*ptr::null::<Struct>()).field); + | ^^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed + +error: aborting due to 10 previous errors + diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.rs b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.rs index c9e1851b9a9..08911c5bde7 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.rs +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.rs @@ -1,6 +1,6 @@ fn test() { let v: isize; - //~^ HELP make this binding mutable + //~^ HELP consider making this binding mutable //~| SUGGESTION mut v loop { v = 1; //~ ERROR cannot assign twice to immutable variable `v` diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.stderr b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.stderr index 69dff734ee4..66cdce7dacf 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.stderr +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-loop.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `v` --> $DIR/liveness-assign-imm-local-in-loop.rs:6:9 | LL | let v: isize; - | - help: make this binding mutable: `mut v` + | - help: consider making this binding mutable: `mut v` ... LL | v = 1; | ^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.rs b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.rs index f24f7d2bcfc..1752d969086 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.rs +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.rs @@ -1,6 +1,6 @@ fn test() { let v: isize; - //~^ HELP make this binding mutable + //~^ HELP consider making this binding mutable //~| SUGGESTION mut v v = 2; //~ NOTE first assignment v += 1; //~ ERROR cannot assign twice to immutable variable `v` diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.stderr b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.stderr index 182958dd492..5db9539cbf1 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.stderr +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-in-op-eq.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `v` --> $DIR/liveness-assign-imm-local-in-op-eq.rs:6:5 | LL | let v: isize; - | - help: make this binding mutable: `mut v` + | - help: consider making this binding mutable: `mut v` ... LL | v = 2; | ----- first assignment to `v` diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs index 8963e32717e..c9b16e43910 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs @@ -1,6 +1,6 @@ fn test() { let b = Box::new(1); //~ NOTE first assignment - //~| HELP make this binding mutable + //~| HELP consider making this binding mutable //~| SUGGESTION mut b drop(b); b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b` diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.stderr b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.stderr index 7c4af624b27..bb7e7e27a4c 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.stderr +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.stderr @@ -5,7 +5,7 @@ LL | let b = Box::new(1); | - | | | first assignment to `b` - | help: make this binding mutable: `mut b` + | help: consider making this binding mutable: `mut b` ... LL | b = Box::new(2); | ^ cannot assign twice to immutable variable diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.rs b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.rs index 4ab222af8d0..4bb2db27a16 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.rs +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.rs @@ -1,6 +1,6 @@ fn test() { let v: isize = 1; //~ NOTE first assignment - //~| HELP make this binding mutable + //~| HELP consider making this binding mutable //~| SUGGESTION mut v v.clone(); v = 2; //~ ERROR cannot assign twice to immutable variable `v` diff --git a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.stderr b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.stderr index 6f5d5574877..80458a70a01 100644 --- a/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.stderr +++ b/src/test/ui/liveness/liveness-assign/liveness-assign-imm-local-with-init.stderr @@ -5,7 +5,7 @@ LL | let v: isize = 1; | - | | | first assignment to `v` - | help: make this binding mutable: `mut v` + | help: consider making this binding mutable: `mut v` ... LL | v = 2; | ^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr index 9b0aa6be1e9..3e5893f68b6 100644 --- a/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr +++ b/src/test/ui/llvm-asm/llvm-asm-out-assign-imm.stderr @@ -2,7 +2,7 @@ error[E0384]: cannot assign twice to immutable variable `x` --> $DIR/llvm-asm-out-assign-imm.rs:25:39 | LL | let x: isize; - | - help: make this binding mutable: `mut x` + | - help: consider making this binding mutable: `mut x` LL | x = 1; | ----- first assignment to `x` ... diff --git a/src/test/ui/macros/builtin-std-paths-fail.stderr b/src/test/ui/macros/builtin-std-paths-fail.stderr index 4f1a76b0d6e..ba626101190 100644 --- a/src/test/ui/macros/builtin-std-paths-fail.stderr +++ b/src/test/ui/macros/builtin-std-paths-fail.stderr @@ -10,6 +10,18 @@ error[E0433]: failed to resolve: could not find `RustcDecodable` in `core` LL | core::RustcDecodable, | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core` +error[E0433]: failed to resolve: could not find `RustcDecodable` in `core` + --> $DIR/builtin-std-paths-fail.rs:2:11 + | +LL | core::RustcDecodable, + | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core` + +error[E0433]: failed to resolve: could not find `RustcDecodable` in `core` + --> $DIR/builtin-std-paths-fail.rs:4:11 + | +LL | core::RustcDecodable, + | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core` + error[E0433]: failed to resolve: could not find `bench` in `core` --> $DIR/builtin-std-paths-fail.rs:7:9 | @@ -34,17 +46,17 @@ error[E0433]: failed to resolve: could not find `test` in `core` LL | #[core::test] | ^^^^ could not find `test` in `core` -error[E0433]: failed to resolve: could not find `RustcDecodable` in `core` - --> $DIR/builtin-std-paths-fail.rs:4:11 +error[E0433]: failed to resolve: could not find `RustcDecodable` in `std` + --> $DIR/builtin-std-paths-fail.rs:14:10 | -LL | core::RustcDecodable, - | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core` +LL | std::RustcDecodable, + | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `std` -error[E0433]: failed to resolve: could not find `RustcDecodable` in `core` - --> $DIR/builtin-std-paths-fail.rs:2:11 +error[E0433]: failed to resolve: could not find `RustcDecodable` in `std` + --> $DIR/builtin-std-paths-fail.rs:16:10 | -LL | core::RustcDecodable, - | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `core` +LL | std::RustcDecodable, + | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `std` error[E0433]: failed to resolve: could not find `RustcDecodable` in `std` --> $DIR/builtin-std-paths-fail.rs:14:10 @@ -82,18 +94,6 @@ error[E0433]: failed to resolve: could not find `test` in `std` LL | #[std::test] | ^^^^ could not find `test` in `std` -error[E0433]: failed to resolve: could not find `RustcDecodable` in `std` - --> $DIR/builtin-std-paths-fail.rs:16:10 - | -LL | std::RustcDecodable, - | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `std` - -error[E0433]: failed to resolve: could not find `RustcDecodable` in `std` - --> $DIR/builtin-std-paths-fail.rs:14:10 - | -LL | std::RustcDecodable, - | ^^^^^^^^^^^^^^ could not find `RustcDecodable` in `std` - error: aborting due to 16 previous errors For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/ui/macros/issue-84195-lint-anon-const.rs b/src/test/ui/macros/issue-84195-lint-anon-const.rs new file mode 100644 index 00000000000..71c76832015 --- /dev/null +++ b/src/test/ui/macros/issue-84195-lint-anon-const.rs @@ -0,0 +1,14 @@ +// Regression test for issue #84195 +// Checks that we properly fire lints that occur inside +// anon consts. + +#![deny(semicolon_in_expressions_from_macros)] + +macro_rules! len { + () => { 0; }; //~ ERROR trailing semicolon + //~| WARN this was previously accepted +} + +fn main() { + let val: [u8; len!()] = []; +} diff --git a/src/test/ui/macros/issue-84195-lint-anon-const.stderr b/src/test/ui/macros/issue-84195-lint-anon-const.stderr new file mode 100644 index 00000000000..558e0349e2f --- /dev/null +++ b/src/test/ui/macros/issue-84195-lint-anon-const.stderr @@ -0,0 +1,20 @@ +error: trailing semicolon in macro used in expression position + --> $DIR/issue-84195-lint-anon-const.rs:8:14 + | +LL | () => { 0; }; + | ^ +... +LL | let val: [u8; len!()] = []; + | ------ in this macro invocation + | +note: the lint level is defined here + --> $DIR/issue-84195-lint-anon-const.rs:5:9 + | +LL | #![deny(semicolon_in_expressions_from_macros)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813> + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/test/ui/macros/macro-pat-pattern-followed-by-or-in-2021.rs b/src/test/ui/macros/macro-pat-pattern-followed-by-or-in-2021.rs new file mode 100644 index 00000000000..f5a97eca21b --- /dev/null +++ b/src/test/ui/macros/macro-pat-pattern-followed-by-or-in-2021.rs @@ -0,0 +1,20 @@ +// edition:2021 +#![allow(unused_macros)] +macro_rules! foo { ($x:pat | $y:pat) => {} } //~ ERROR `$x:pat` is followed by `|`, which is not allowed for `pat` fragments +macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } //~ ERROR `$x:pat` is followed by `|`, which is not allowed for `pat` fragments +macro_rules! qux { ($x:pat, $y:pat) => {} } // should be ok +macro_rules! match_any { + ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { //~ ERROR `$pat:pat` may be followed by `|`, which is not allowed for `pat` fragments + match $expr { + $( + $( $pat => $expr_arm, )+ + )+ + } + }; +} + +fn main() { + let result: Result<i64, i32> = Err(42); + let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into()); + assert_eq!(int, 42); +} diff --git a/src/test/ui/macros/macro-pat-pattern-followed-by-or-in-2021.stderr b/src/test/ui/macros/macro-pat-pattern-followed-by-or-in-2021.stderr new file mode 100644 index 00000000000..a5987a25551 --- /dev/null +++ b/src/test/ui/macros/macro-pat-pattern-followed-by-or-in-2021.stderr @@ -0,0 +1,26 @@ +error: `$x:pat` is followed by `|`, which is not allowed for `pat` fragments + --> $DIR/macro-pat-pattern-followed-by-or-in-2021.rs:3:28 + | +LL | macro_rules! foo { ($x:pat | $y:pat) => {} } + | ^ not allowed after `pat` fragments + | + = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + +error: `$x:pat` is followed by `|`, which is not allowed for `pat` fragments + --> $DIR/macro-pat-pattern-followed-by-or-in-2021.rs:4:32 + | +LL | macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } + | ^ not allowed after `pat` fragments + | + = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + +error: `$pat:pat` may be followed by `|`, which is not allowed for `pat` fragments + --> $DIR/macro-pat-pattern-followed-by-or-in-2021.rs:7:36 + | +LL | ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { + | ^ not allowed after `pat` fragments + | + = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/macros/macro-pat-pattern-followed-by-or.rs b/src/test/ui/macros/macro-pat-pattern-followed-by-or.rs new file mode 100644 index 00000000000..54bd13d7ebc --- /dev/null +++ b/src/test/ui/macros/macro-pat-pattern-followed-by-or.rs @@ -0,0 +1,20 @@ +// run-pass +#![allow(unused_macros)] +macro_rules! foo { ($x:pat | $y:pat) => {} } // should be ok +macro_rules! bar { ($($x:pat)+ | $($y:pat)+) => {} } // should be ok +macro_rules! qux { ($x:pat, $y:pat) => {} } // should be ok +macro_rules! match_any { + ( $expr:expr , $( $( $pat:pat )|+ => $expr_arm:expr ),+ ) => { // should be ok + match $expr { + $( + $( $pat => $expr_arm, )+ + )+ + } + }; +} + +fn main() { + let result: Result<i64, i32> = Err(42); + let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into()); + assert_eq!(int, 42); +} diff --git a/src/test/ui/macros/macro-pat2021-pattern-followed-by-or.rs b/src/test/ui/macros/macro-pat2021-pattern-followed-by-or.rs new file mode 100644 index 00000000000..edd3f3e7646 --- /dev/null +++ b/src/test/ui/macros/macro-pat2021-pattern-followed-by-or.rs @@ -0,0 +1,21 @@ +#![feature(edition_macro_pats)] +#![allow(unused_macros)] +macro_rules! foo { ($x:pat2021 | $y:pat2021) => {} } //~ ERROR `$x:pat2021` is followed by `|`, which is not allowed for `pat2021` fragments +macro_rules! baz { ($x:pat2015 | $y:pat2015) => {} } // should be ok +macro_rules! qux { ($x:pat2015 | $y:pat2021) => {} } // should be ok +macro_rules! ogg { ($x:pat2021 | $y:pat2015) => {} } //~ ERROR `$x:pat2021` is followed by `|`, which is not allowed for `pat2021` fragments +macro_rules! match_any { + ( $expr:expr , $( $( $pat:pat2021 )|+ => $expr_arm:pat2021 ),+ ) => { //~ ERROR `$pat:pat2021` may be followed by `|`, which is not allowed for `pat2021` fragments + match $expr { + $( + $( $pat => $expr_arm, )+ + )+ + } + }; +} + +fn main() { + let result: Result<i64, i32> = Err(42); + let int: i64 = match_any!(result, Ok(i) | Err(i) => i.into()); + assert_eq!(int, 42); +} diff --git a/src/test/ui/macros/macro-pat2021-pattern-followed-by-or.stderr b/src/test/ui/macros/macro-pat2021-pattern-followed-by-or.stderr new file mode 100644 index 00000000000..fe0b40cd86e --- /dev/null +++ b/src/test/ui/macros/macro-pat2021-pattern-followed-by-or.stderr @@ -0,0 +1,26 @@ +error: `$x:pat2021` is followed by `|`, which is not allowed for `pat2021` fragments + --> $DIR/macro-pat2021-pattern-followed-by-or.rs:3:32 + | +LL | macro_rules! foo { ($x:pat2021 | $y:pat2021) => {} } + | ^ not allowed after `pat2021` fragments + | + = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + +error: `$x:pat2021` is followed by `|`, which is not allowed for `pat2021` fragments + --> $DIR/macro-pat2021-pattern-followed-by-or.rs:6:32 + | +LL | macro_rules! ogg { ($x:pat2021 | $y:pat2015) => {} } + | ^ not allowed after `pat2021` fragments + | + = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + +error: `$pat:pat2021` may be followed by `|`, which is not allowed for `pat2021` fragments + --> $DIR/macro-pat2021-pattern-followed-by-or.rs:8:40 + | +LL | ( $expr:expr , $( $( $pat:pat2021 )|+ => $expr_arm:pat2021 ),+ ) => { + | ^ not allowed after `pat2021` fragments + | + = note: allowed there are: `=>`, `,`, `=`, `if` or `in` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/macros/none-delim-lookahead.rs b/src/test/ui/macros/none-delim-lookahead.rs new file mode 100644 index 00000000000..bf4fddea14b --- /dev/null +++ b/src/test/ui/macros/none-delim-lookahead.rs @@ -0,0 +1,15 @@ +// check-pass + +macro_rules! make_struct { + ($name:ident) => { + #[derive(Debug)] + struct Foo { + #[cfg(not(FALSE))] + field: fn($name: bool) + } + } +} + +make_struct!(param_name); + +fn main() {} diff --git a/src/test/ui/main-wrong-location.rs b/src/test/ui/main-wrong-location.rs index f75d08813cd..f3acd80a7a4 100644 --- a/src/test/ui/main-wrong-location.rs +++ b/src/test/ui/main-wrong-location.rs @@ -1,6 +1,6 @@ mod m { //~^ ERROR `main` function not found - // An inferred main entry point (that doesn't use #[main]) + // An inferred main entry point // must appear at the top of the crate fn main() { } } diff --git a/src/test/ui/main-wrong-location.stderr b/src/test/ui/main-wrong-location.stderr index e301c2ff09a..754ff0f80eb 100644 --- a/src/test/ui/main-wrong-location.stderr +++ b/src/test/ui/main-wrong-location.stderr @@ -3,7 +3,7 @@ error[E0601]: `main` function not found in crate `main_wrong_location` | LL | / mod m { LL | | -LL | | // An inferred main entry point (that doesn't use #[main]) +LL | | // An inferred main entry point LL | | // must appear at the top of the crate LL | | fn main() { } LL | | } @@ -15,7 +15,7 @@ note: here is a function named `main` LL | fn main() { } | ^^^^^^^^^^^^^ = note: you have one or more functions named `main` not defined at the crate level - = help: either move the `main` function definitions or attach the `#[main]` attribute to one of them + = help: consider moving the `main` function definitions error: aborting due to previous error diff --git a/src/test/ui/mismatched_types/E0053.stderr b/src/test/ui/mismatched_types/E0053.stderr index e0a3ce922b9..6ce8126b9f9 100644 --- a/src/test/ui/mismatched_types/E0053.stderr +++ b/src/test/ui/mismatched_types/E0053.stderr @@ -5,7 +5,10 @@ LL | fn foo(x: u16); | --- type in trait ... LL | fn foo(x: i16) { } - | ^^^ expected `u16`, found `i16` + | ^^^ + | | + | expected `u16`, found `i16` + | help: change the parameter type to match the trait: `u16` | = note: expected fn pointer `fn(u16)` found fn pointer `fn(i16)` @@ -20,7 +23,7 @@ LL | fn bar(&mut self) { } | ^^^^^^^^^ | | | types differ in mutability - | help: consider changing the mutability to match the trait: `&self` + | help: change the self-receiver type to match the trait: `self: &Bar` | = note: expected fn pointer `fn(&Bar)` found fn pointer `fn(&mut Bar)` diff --git a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr index 161843473b6..2ac4d1c33a9 100644 --- a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr +++ b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr @@ -5,7 +5,10 @@ LL | fn foo(x: u16); | --- type in trait ... LL | fn foo(x: i16) { } - | ^^^ expected `u16`, found `i16` + | ^^^ + | | + | expected `u16`, found `i16` + | help: change the parameter type to match the trait: `u16` | = note: expected fn pointer `fn(u16)` found fn pointer `fn(i16)` @@ -20,7 +23,7 @@ LL | fn bar(&mut self, bar: &Bar) { } | ^^^^ | | | types differ in mutability - | help: consider changing the mutability to match the trait: `&mut Bar` + | help: change the parameter type to match the trait: `&mut Bar` | = note: expected fn pointer `fn(&mut Bar, &mut Bar)` found fn pointer `fn(&mut Bar, &Bar)` diff --git a/src/test/ui/multiple-main-2.rs b/src/test/ui/multiple-main-2.rs deleted file mode 100644 index e4685b1e004..00000000000 --- a/src/test/ui/multiple-main-2.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![feature(main)] - -#[main] -fn bar() { -} - -#[main] -fn foo() { //~ ERROR multiple functions with a `#[main]` attribute -} diff --git a/src/test/ui/multiple-main-2.stderr b/src/test/ui/multiple-main-2.stderr deleted file mode 100644 index 24bc9a8878b..00000000000 --- a/src/test/ui/multiple-main-2.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0137]: multiple functions with a `#[main]` attribute - --> $DIR/multiple-main-2.rs:8:1 - | -LL | / fn bar() { -LL | | } - | |_- first `#[main]` function -... -LL | / fn foo() { -LL | | } - | |_^ additional `#[main]` function - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0137`. diff --git a/src/test/ui/multiple-main-3.rs b/src/test/ui/multiple-main-3.rs deleted file mode 100644 index d1b5ae9a833..00000000000 --- a/src/test/ui/multiple-main-3.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![feature(main)] - -#[main] -fn main1() { -} - -mod foo { - #[main] - fn main2() { //~ ERROR multiple functions with a `#[main]` attribute - } -} diff --git a/src/test/ui/multiple-main-3.stderr b/src/test/ui/multiple-main-3.stderr deleted file mode 100644 index ec171b76a28..00000000000 --- a/src/test/ui/multiple-main-3.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0137]: multiple functions with a `#[main]` attribute - --> $DIR/multiple-main-3.rs:9:5 - | -LL | / fn main1() { -LL | | } - | |_- first `#[main]` function -... -LL | / fn main2() { -LL | | } - | |_____^ additional `#[main]` function - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0137`. diff --git a/src/test/ui/mut/mut-pattern-internal-mutability.stderr b/src/test/ui/mut/mut-pattern-internal-mutability.stderr index eaa33453a75..6583546aa5c 100644 --- a/src/test/ui/mut/mut-pattern-internal-mutability.stderr +++ b/src/test/ui/mut/mut-pattern-internal-mutability.stderr @@ -5,7 +5,7 @@ LL | let &mut x = foo; | - | | | first assignment to `x` - | help: make this binding mutable: `mut x` + | help: consider making this binding mutable: `mut x` LL | x += 1; | ^^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/panic-runtime/unwind-tables-panic-required.rs b/src/test/ui/panic-runtime/unwind-tables-panic-required.rs deleted file mode 100644 index 79e91879051..00000000000 --- a/src/test/ui/panic-runtime/unwind-tables-panic-required.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Tests that the compiler errors if the user tries to turn off unwind tables -// when they are required. -// -// dont-check-compiler-stderr -// compile-flags: -C panic=unwind -C force-unwind-tables=no -// -// error-pattern: panic=unwind requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no`. - -pub fn main() { -} diff --git a/src/test/ui/parser/issue-63116.stderr b/src/test/ui/parser/issue-63116.stderr index e249a93df92..4766dfafea1 100644 --- a/src/test/ui/parser/issue-63116.stderr +++ b/src/test/ui/parser/issue-63116.stderr @@ -12,11 +12,11 @@ error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `;` LL | impl W <s(f;Y(;] | ^ expected one of 7 possible tokens -error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `->`, `...`, `::`, `:`, `<`, `=`, `>`, `?`, `[`, `_`, `async`, `const`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, lifetime, or path, found `;` - --> $DIR/issue-63116.rs:3:15 +error: mismatched closing delimiter: `]` + --> $DIR/issue-63116.rs:3:16 | LL | impl W <s(f;Y(;] - | -^ help: `)` may belong here + | - ^ mismatched closing delimiter | | | unclosed delimiter diff --git a/src/test/ui/parser/issue-84117.rs b/src/test/ui/parser/issue-84117.rs new file mode 100644 index 00000000000..0f200735915 --- /dev/null +++ b/src/test/ui/parser/issue-84117.rs @@ -0,0 +1,9 @@ +fn main() { + let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + //~^ ERROR expected one of `>`, a const expression + //~| ERROR expected one of `>`, a const expression, lifetime, or type, found `}` + //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` + //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` + //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` +} +//~^ ERROR expected one of `,`, `:`, `=`, or `>`, found `}` diff --git a/src/test/ui/parser/issue-84117.stderr b/src/test/ui/parser/issue-84117.stderr new file mode 100644 index 00000000000..d667a4977d0 --- /dev/null +++ b/src/test/ui/parser/issue-84117.stderr @@ -0,0 +1,49 @@ +error: expected one of `>`, a const expression, lifetime, or type, found `}` + --> $DIR/issue-84117.rs:2:67 + | +LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + | ------------ ^ expected one of `>`, a const expression, lifetime, or type + | | | + | | help: use `=` if you meant to assign + | while parsing the type for `inner_local` + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` + --> $DIR/issue-84117.rs:2:65 + | +LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + | ^ expected one of 7 possible tokens + +error: expected one of `,`, `:`, `=`, or `>`, found `}` + --> $DIR/issue-84117.rs:8:1 + | +LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + | ------------ help: use `=` if you meant to assign - expected one of `,`, `:`, `=`, or `>` + | | + | while parsing the type for `outer_local` +... +LL | } + | ^ unexpected token + +error: expected one of `>`, a const expression, lifetime, or type, found `}` + --> $DIR/issue-84117.rs:2:67 + | +LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + | ------------ ^ expected one of `>`, a const expression, lifetime, or type + | | | + | | help: use `=` if you meant to assign + | while parsing the type for `inner_local` + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` + --> $DIR/issue-84117.rs:2:65 + | +LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + | ^ expected one of 7 possible tokens + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,` + --> $DIR/issue-84117.rs:2:33 + | +LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } + | ^ expected one of 7 possible tokens + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/parser/use-unclosed-brace.rs b/src/test/ui/parser/use-unclosed-brace.rs new file mode 100644 index 00000000000..41742f37f3c --- /dev/null +++ b/src/test/ui/parser/use-unclosed-brace.rs @@ -0,0 +1,12 @@ +// error-pattern: expected one of `,`, `::`, `as`, or `}`, found `;` +// error-pattern: this file contains an unclosed delimiter +// error-pattern: expected item, found `}` +use foo::{bar, baz; + +use std::fmt::Display; + +mod bar { } + +mod baz { } + +fn main() {} diff --git a/src/test/ui/parser/use-unclosed-brace.stderr b/src/test/ui/parser/use-unclosed-brace.stderr new file mode 100644 index 00000000000..d29a68f8214 --- /dev/null +++ b/src/test/ui/parser/use-unclosed-brace.stderr @@ -0,0 +1,27 @@ +error: this file contains an unclosed delimiter + --> $DIR/use-unclosed-brace.rs:12:14 + | +LL | use foo::{bar, baz; + | - unclosed delimiter +... +LL | fn main() {} + | ^ + +error: expected one of `,`, `::`, `as`, or `}`, found `;` + --> $DIR/use-unclosed-brace.rs:4:19 + | +LL | use foo::{bar, baz; + | - ^ + | | | + | | expected one of `,`, `::`, `as`, or `}` + | | help: `}` may belong here + | unclosed delimiter + +error: expected item, found `}` + --> $DIR/use-unclosed-brace.rs:12:14 + | +LL | fn main() {} + | ^ expected item + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/parser/variadic-ffi-semantic-restrictions.rs b/src/test/ui/parser/variadic-ffi-semantic-restrictions.rs index 404f4090755..fe993a6ee13 100644 --- a/src/test/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/src/test/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -1,4 +1,5 @@ #![feature(c_variadic)] +#![allow(anonymous_parameters)] fn main() {} diff --git a/src/test/ui/parser/variadic-ffi-semantic-restrictions.stderr b/src/test/ui/parser/variadic-ffi-semantic-restrictions.stderr index ebfe4979fb6..10fd05c0bef 100644 --- a/src/test/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/src/test/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -1,203 +1,203 @@ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:5:19 + --> $DIR/variadic-ffi-semantic-restrictions.rs:6:19 | LL | fn f1_1(x: isize, ...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:8:9 + --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9 | LL | fn f1_2(...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:8:9 + --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9 | LL | fn f1_2(...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:12:30 + --> $DIR/variadic-ffi-semantic-restrictions.rs:13:30 | LL | extern "C" fn f2_1(x: isize, ...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:15:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:16:20 | LL | extern "C" fn f2_2(...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:15:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:16:20 | LL | extern "C" fn f2_2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:19:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:20:20 | LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:19:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:20:20 | LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:23:30 + --> $DIR/variadic-ffi-semantic-restrictions.rs:24:30 | LL | extern "C" fn f3_1(x: isize, ...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:26:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 | LL | extern "C" fn f3_2(...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:26:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 | LL | extern "C" fn f3_2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:30:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:31:20 | LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:30:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:31:20 | LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:35:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:36:13 | LL | fn e_f1(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:37:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:38:13 | LL | fn e_f2(..., x: isize); | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:44:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:45:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:46:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:47:13 | LL | fn i_f2(...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:46:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:47:13 | LL | fn i_f2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:49:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:50:13 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:49:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:50:13 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:49:28 + --> $DIR/variadic-ffi-semantic-restrictions.rs:50:28 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:53:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:53:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:53:28 + --> $DIR/variadic-ffi-semantic-restrictions.rs:54:28 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:60:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:61:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:63:23 | LL | fn t_f2(x: isize, ...); | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:64:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:13 | LL | fn t_f3(...) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:64:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:13 | LL | fn t_f3(...) {} | ^^^ error: C-variadic function must be declared with at least one named argument - --> $DIR/variadic-ffi-semantic-restrictions.rs:67:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:68:13 | LL | fn t_f4(...); | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:67:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:68:13 | LL | fn t_f4(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:70:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:71:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:70:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:71:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:73:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:74:13 | LL | fn t_f6(..., x: isize); | ^^^ error: only foreign or `unsafe extern "C" functions may be C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:73:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:74:13 | LL | fn t_f6(..., x: isize); | ^^^ diff --git a/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr b/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr index 285c203f382..d0726f05cc3 100644 --- a/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr +++ b/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr @@ -16,7 +16,7 @@ LL | let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr; | --- | | | first assignment to `_x1` - | help: make this binding mutable: `mut _x1` + | help: consider making this binding mutable: `mut _x1` LL | _x1 = U; | ^^^^^^^ cannot assign twice to immutable variable @@ -74,7 +74,7 @@ LL | let (ref _x0, _x1, ref _x2, ..) = tup; | --- | | | first assignment to `_x1` - | help: make this binding mutable: `mut _x1` + | help: consider making this binding mutable: `mut _x1` LL | _x1 = U; | ^^^^^^^ cannot assign twice to immutable variable diff --git a/src/test/ui/proc-macro/attr-complex-fn.stdout b/src/test/ui/proc-macro/attr-complex-fn.stdout index a395a9ac3e8..72783efe08d 100644 --- a/src/test/ui/proc-macro/attr-complex-fn.stdout +++ b/src/test/ui/proc-macro/attr-complex-fn.stdout @@ -80,67 +80,67 @@ PRINT-ATTR INPUT (DISPLAY): impl < T > MyTrait < T > for MyStruct < { true } > { PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "impl", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:1: 21:5 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:5: 21:6 (#0), }, Ident { ident: "T", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:6: 21:7 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:7: 21:8 (#0), }, Ident { ident: "MyTrait", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:9: 21:16 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:16: 21:17 (#0), }, Ident { ident: "T", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:17: 21:18 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:18: 21:19 (#0), }, Ident { ident: "for", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:20: 21:23 (#0), }, Ident { ident: "MyStruct", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:24: 21:32 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:32: 21:33 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "true", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:34: 21:38 (#0), }, ], - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:33: 21:39 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:39: 21:40 (#0), }, Group { delimiter: Brace, @@ -148,24 +148,24 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:23:5: 23:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:23:6: 23:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustc_dummy", - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:23:8: 23:19 (#0), }, ], - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:23:7: 23:20 (#0), }, ], - span: $DIR/attr-complex-fn.rs:21:1: 24:2 (#0), + span: $DIR/attr-complex-fn.rs:21:41: 24:2 (#0), }, ] diff --git a/src/test/ui/proc-macro/attribute-after-derive.stdout b/src/test/ui/proc-macro/attribute-after-derive.stdout index 11f49235327..4c48e41ff33 100644 --- a/src/test/ui/proc-macro/attribute-after-derive.stdout +++ b/src/test/ui/proc-macro/attribute-after-derive.stdout @@ -87,32 +87,16 @@ PRINT-DERIVE INPUT (DISPLAY): struct AttributeDerive { } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/attribute-after-derive.rs:18:1: 21:2 (#0), + span: $DIR/attribute-after-derive.rs:18:1: 18:7 (#0), }, Ident { ident: "AttributeDerive", - span: $DIR/attribute-after-derive.rs:18:1: 21:2 (#0), - }, - Group { - delimiter: Brace, - stream: TokenStream [], - span: $DIR/attribute-after-derive.rs:18:1: 21:2 (#0), - }, -] -PRINT-ATTR INPUT (DISPLAY): struct DeriveAttribute { } -PRINT-ATTR INPUT (DEBUG): TokenStream [ - Ident { - ident: "struct", - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), - }, - Ident { - ident: "DeriveAttribute", - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:18:8: 18:23 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:18:24: 21:2 (#0), }, ] PRINT-DERIVE INPUT (DISPLAY): #[print_attr] struct DeriveAttribute { } @@ -120,29 +104,45 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:24:1: 24:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_attr", - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:24:3: 24:13 (#0), }, ], - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:24:2: 24:14 (#0), + }, + Ident { + ident: "struct", + span: $DIR/attribute-after-derive.rs:25:1: 25:7 (#0), + }, + Ident { + ident: "DeriveAttribute", + span: $DIR/attribute-after-derive.rs:25:8: 25:23 (#0), }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/attribute-after-derive.rs:25:24: 28:2 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): struct DeriveAttribute { } +PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:25:1: 25:7 (#0), }, Ident { ident: "DeriveAttribute", - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:25:8: 25:23 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/attribute-after-derive.rs:25:1: 28:2 (#0), + span: $DIR/attribute-after-derive.rs:25:24: 28:2 (#0), }, ] diff --git a/src/test/ui/proc-macro/auxiliary/multiple-derives.rs b/src/test/ui/proc-macro/auxiliary/multiple-derives.rs new file mode 100644 index 00000000000..e3f6607b2ae --- /dev/null +++ b/src/test/ui/proc-macro/auxiliary/multiple-derives.rs @@ -0,0 +1,22 @@ +// force-host +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +macro_rules! make_derives { + ($($name:ident),*) => { + $( + #[proc_macro_derive($name)] + pub fn $name(input: TokenStream) -> TokenStream { + println!("Derive {}: {}", stringify!($name), input); + TokenStream::new() + } + )* + } +} + +make_derives!(First, Second, Third, Fourth, Fifth); diff --git a/src/test/ui/proc-macro/auxiliary/nonterminal-recollect-attr.rs b/src/test/ui/proc-macro/auxiliary/nonterminal-recollect-attr.rs index a6903283aa1..ea5ff466570 100644 --- a/src/test/ui/proc-macro/auxiliary/nonterminal-recollect-attr.rs +++ b/src/test/ui/proc-macro/auxiliary/nonterminal-recollect-attr.rs @@ -10,6 +10,7 @@ use proc_macro::{TokenStream, quote}; #[proc_macro_attribute] pub fn first_attr(_: TokenStream, input: TokenStream) -> TokenStream { let recollected: TokenStream = input.into_iter().collect(); + println!("First recollected: {:#?}", recollected); quote! { #[second_attr] $recollected @@ -18,6 +19,7 @@ pub fn first_attr(_: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn second_attr(_: TokenStream, input: TokenStream) -> TokenStream { - let _recollected: TokenStream = input.into_iter().collect(); + let recollected: TokenStream = input.into_iter().collect(); + println!("Second recollected: {:#?}", recollected); TokenStream::new() } diff --git a/src/test/ui/proc-macro/cfg-eval-inner.rs b/src/test/ui/proc-macro/cfg-eval-inner.rs new file mode 100644 index 00000000000..5fd3ca0d163 --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval-inner.rs @@ -0,0 +1,39 @@ +// compile-flags: -Z span-debug +// aux-build:test-macros.rs +// check-pass + +#![feature(cfg_eval)] +#![feature(custom_inner_attributes)] +#![feature(stmt_expr_attributes)] +#![feature(rustc_attrs)] + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] +extern crate test_macros; + +struct Foo<T>(T); + +impl Foo<[u8; { + #![cfg_attr(not(FALSE), rustc_dummy(cursed_inner))] + #![allow(unused)] + struct Inner { + field: [u8; { + #![cfg_attr(not(FALSE), rustc_dummy(another_cursed_inner))] + 1 + }] + } + + 0 +}]> { + #![cfg_eval] + #![print_attr] + #![cfg_attr(not(FALSE), rustc_dummy(evaluated_attr))] + + fn bar() { + #[cfg(FALSE)] let a = 1; + } +} + +fn main() {} diff --git a/src/test/ui/proc-macro/cfg-eval-inner.stdout b/src/test/ui/proc-macro/cfg-eval-inner.stdout new file mode 100644 index 00000000000..1f2b0039589 --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval-inner.stdout @@ -0,0 +1,247 @@ +PRINT-ATTR INPUT (DISPLAY): impl Foo < +[u8 ; + { + # ! [rustc_dummy(cursed_inner)] # ! [allow(unused)] struct Inner + { field : [u8 ; { # ! [rustc_dummy(another_cursed_inner)] 1 }] } 0 + }] > { # ! [rustc_dummy(evaluated_attr)] fn bar() { } } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "impl", + span: $DIR/cfg-eval-inner.rs:18:1: 18:5 (#0), + }, + Ident { + ident: "Foo", + span: $DIR/cfg-eval-inner.rs:18:6: 18:9 (#0), + }, + Punct { + ch: '<', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:18:9: 18:10 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "u8", + span: $DIR/cfg-eval-inner.rs:18:11: 18:13 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:18:13: 18:14 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:19:5: 19:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:19:6: 19:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/cfg-eval-inner.rs:19:29: 19:40 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "cursed_inner", + span: $DIR/cfg-eval-inner.rs:19:41: 19:53 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:19:40: 19:54 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:19:5: 19:6 (#0), + }, + Punct { + ch: '#', + spacing: Joint, + span: $DIR/cfg-eval-inner.rs:20:5: 20:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:20:6: 20:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "allow", + span: $DIR/cfg-eval-inner.rs:20:8: 20:13 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "unused", + span: $DIR/cfg-eval-inner.rs:20:14: 20:20 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:20:13: 20:21 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:20:7: 20:22 (#0), + }, + Ident { + ident: "struct", + span: $DIR/cfg-eval-inner.rs:21:5: 21:11 (#0), + }, + Ident { + ident: "Inner", + span: $DIR/cfg-eval-inner.rs:21:12: 21:17 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "field", + span: $DIR/cfg-eval-inner.rs:22:9: 22:14 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:22:14: 22:15 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "u8", + span: $DIR/cfg-eval-inner.rs:22:17: 22:19 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:22:19: 22:20 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:23:13: 23:14 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:23:14: 23:15 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/cfg-eval-inner.rs:23:37: 23:48 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "another_cursed_inner", + span: $DIR/cfg-eval-inner.rs:23:49: 23:69 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:23:48: 23:70 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:23:13: 23:14 (#0), + }, + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/cfg-eval-inner.rs:24:13: 24:14 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:22:21: 25:10 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:22:16: 25:11 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:21:18: 26:6 (#0), + }, + Literal { + kind: Integer, + symbol: "0", + suffix: None, + span: $DIR/cfg-eval-inner.rs:28:5: 28:6 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:18:15: 29:2 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:18:10: 29:3 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:29:3: 29:4 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:32:5: 32:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/cfg-eval-inner.rs:32:6: 32:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/cfg-eval-inner.rs:32:29: 32:40 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "evaluated_attr", + span: $DIR/cfg-eval-inner.rs:32:41: 32:55 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:32:40: 32:56 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:32:5: 32:6 (#0), + }, + Ident { + ident: "fn", + span: $DIR/cfg-eval-inner.rs:34:5: 34:7 (#0), + }, + Ident { + ident: "bar", + span: $DIR/cfg-eval-inner.rs:34:8: 34:11 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/cfg-eval-inner.rs:34:11: 34:13 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/cfg-eval-inner.rs:34:14: 36:6 (#0), + }, + ], + span: $DIR/cfg-eval-inner.rs:29:5: 37:2 (#0), + }, +] diff --git a/src/test/ui/proc-macro/cfg-eval.rs b/src/test/ui/proc-macro/cfg-eval.rs index ea397df5452..fa6d015e48e 100644 --- a/src/test/ui/proc-macro/cfg-eval.rs +++ b/src/test/ui/proc-macro/cfg-eval.rs @@ -5,7 +5,7 @@ #![feature(cfg_eval)] #![feature(proc_macro_hygiene)] #![feature(stmt_expr_attributes)] - +#![feature(rustc_attrs)] #![no_std] // Don't load unnecessary hygiene information from std extern crate std; @@ -28,5 +28,10 @@ struct S1 { struct S2 {} fn main() { - let _ = #[cfg_eval] #[print_attr](#[cfg(FALSE)] 0, #[cfg(all(/*true*/))] 1); + // Subtle - we need a trailing comma after the '1' - otherwise, `#[cfg_eval]` will + // turn this into `(#[cfg(all())] 1)`, which is a parenthesized expression, not a tuple + // expression. `#[cfg]` is not supported inside parenthesized expressions, so this will + // produce an error when attribute collection runs. + let _ = #[cfg_eval] #[print_attr] #[cfg_attr(not(FALSE), rustc_dummy)] + (#[cfg(FALSE)] 0, #[cfg(all(/*true*/))] 1,); } diff --git a/src/test/ui/proc-macro/cfg-eval.stdout b/src/test/ui/proc-macro/cfg-eval.stdout index b98e8961bfe..6732caf08dd 100644 --- a/src/test/ui/proc-macro/cfg-eval.stdout +++ b/src/test/ui/proc-macro/cfg-eval.stdout @@ -2,11 +2,11 @@ PRINT-ATTR INPUT (DISPLAY): struct S1 { #[cfg(all())] #[allow()] field_true : u8 PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:17:1: 17:7 (#0), }, Ident { ident: "S1", - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:17:8: 17:10 (#0), }, Group { delimiter: Brace, @@ -14,122 +14,137 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:20:5: 20:6 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:20:7: 20:10 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "all", - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:20:11: 20:14 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:20:14: 20:24 (#0), }, ], - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:20:10: 20:25 (#0), }, ], - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:20:6: 20:26 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:22:5: 22:6 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "allow", - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:22:31: 22:36 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:22:36: 22:38 (#0), }, ], - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:22:5: 22:6 (#0), }, Ident { ident: "field_true", - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:23:5: 23:15 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:23:15: 23:16 (#0), }, Ident { ident: "u8", - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:23:17: 23:19 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:23:19: 23:20 (#0), }, ], - span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + span: $DIR/cfg-eval.rs:17:11: 24:2 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): (#[cfg(all())] 1,) +PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] (#[cfg(all())] 1,) PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval.rs:35:39: 35:40 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/cfg-eval.rs:35:62: 35:73 (#0), + }, + ], + span: $DIR/cfg-eval.rs:35:39: 35:40 (#0), + }, Group { delimiter: Parenthesis, stream: TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + span: $DIR/cfg-eval.rs:36:23: 36:24 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + span: $DIR/cfg-eval.rs:36:25: 36:28 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "all", - span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + span: $DIR/cfg-eval.rs:36:29: 36:32 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + span: $DIR/cfg-eval.rs:36:32: 36:42 (#0), }, ], - span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + span: $DIR/cfg-eval.rs:36:28: 36:43 (#0), }, ], - span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + span: $DIR/cfg-eval.rs:36:24: 36:44 (#0), }, Literal { kind: Integer, symbol: "1", suffix: None, - span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + span: $DIR/cfg-eval.rs:36:45: 36:46 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + span: $DIR/cfg-eval.rs:36:46: 36:47 (#0), }, ], - span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + span: $DIR/cfg-eval.rs:36:5: 36:48 (#0), }, ] diff --git a/src/test/ui/proc-macro/derive-expand-order.rs b/src/test/ui/proc-macro/derive-expand-order.rs new file mode 100644 index 00000000000..0cf1ceb91de --- /dev/null +++ b/src/test/ui/proc-macro/derive-expand-order.rs @@ -0,0 +1,14 @@ +// run-pass +// aux-build:multiple-derives.rs + +extern crate multiple_derives; + +use multiple_derives::*; + +#[derive(First)] +#[derive(Second)] +#[derive(Third, Fourth)] +#[derive(Fifth)] +pub struct Foo {} + +fn main() {} diff --git a/src/test/ui/proc-macro/derive-expand-order.stdout b/src/test/ui/proc-macro/derive-expand-order.stdout new file mode 100644 index 00000000000..3ac1adf92c2 --- /dev/null +++ b/src/test/ui/proc-macro/derive-expand-order.stdout @@ -0,0 +1,5 @@ +Derive First: #[derive(Second)] #[derive(Third, Fourth)] #[derive(Fifth)] pub struct Foo { } +Derive Second: #[derive(Third, Fourth)] #[derive(Fifth)] pub struct Foo { } +Derive Third: #[derive(Fifth)] pub struct Foo { } +Derive Fourth: #[derive(Fifth)] pub struct Foo { } +Derive Fifth: pub struct Foo { } diff --git a/src/test/ui/proc-macro/expand-to-derive.stdout b/src/test/ui/proc-macro/expand-to-derive.stdout index 7eb68643342..a6437982a37 100644 --- a/src/test/ui/proc-macro/expand-to-derive.stdout +++ b/src/test/ui/proc-macro/expand-to-derive.stdout @@ -1,40 +1,40 @@ PRINT-DERIVE INPUT (DISPLAY): struct Foo { field : - [bool ; { #[rustc_dummy] struct Inner { other_inner_field : u8, } 0 }], + [bool ; { #[rustc_dummy] struct Inner { other_inner_field : u8, } 0 }] } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:16:9: 16:15 (#4), }, Ident { ident: "Foo", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:16:16: 16:19 (#4), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "field", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:18:13: 18:18 (#4), }, Punct { ch: ':', spacing: Alone, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:18:18: 18:19 (#4), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "bool", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:18:21: 18:25 (#4), }, Punct { ch: ';', spacing: Alone, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:18:25: 18:26 (#4), }, Group { delimiter: Brace, @@ -42,68 +42,63 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:27:5: 27:6 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustc_dummy", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:27:28: 27:39 (#0), }, ], - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:27:5: 27:6 (#0), }, Ident { ident: "struct", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:28:5: 28:11 (#0), }, Ident { ident: "Inner", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:28:12: 28:17 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "other_inner_field", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:30:9: 30:26 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:30:26: 30:27 (#0), }, Ident { ident: "u8", - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:30:28: 30:30 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:30:30: 30:31 (#0), }, ], - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:28:18: 31:6 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:20:17: 20:18 (#4), }, ], - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:18:27: 21:14 (#4), }, ], - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), - }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:18:20: 21:15 (#4), }, ], - span: $DIR/expand-to-derive.rs:16:9: 22:10 (#4), + span: $DIR/expand-to-derive.rs:16:20: 22:10 (#4), }, ] diff --git a/src/test/ui/proc-macro/inner-attrs.rs b/src/test/ui/proc-macro/inner-attrs.rs index 5707621f80e..14ec57ad626 100644 --- a/src/test/ui/proc-macro/inner-attrs.rs +++ b/src/test/ui/proc-macro/inner-attrs.rs @@ -1,6 +1,6 @@ // compile-flags: -Z span-debug --error-format human // aux-build:test-macros.rs - +// edition:2018 #![feature(custom_inner_attributes)] #![feature(proc_macro_hygiene)] #![feature(stmt_expr_attributes)] @@ -19,10 +19,32 @@ fn foo() { #![print_target_and_args(fourth)] } +#[print_target_and_args(mod_first)] +#[print_target_and_args(mod_second)] +mod inline_mod { + #![print_target_and_args(mod_third)] + #![print_target_and_args(mod_fourth)] +} + struct MyStruct { field: bool } +#[derive(Print)] +struct MyDerivePrint { + field: [u8; { + match true { + #![cfg_attr(not(FALSE), rustc_dummy(first))] + #![cfg_attr(not(FALSE), rustc_dummy(second))] + _ => { + #![cfg_attr(not(FALSE), rustc_dummy(third))] + true + } + }; + 0 + }] +} + fn bar() { (#![print_target_and_args(fifth)] 1, 2); //~^ ERROR expected non-macro inner attribute, found attribute macro @@ -40,6 +62,19 @@ fn bar() { true; 0 ]; + #[print_target_and_args(tuple_attrs)] ( + #![cfg_attr(FALSE, rustc_dummy)] + 3, 4, { + #![cfg_attr(not(FALSE), rustc_dummy(innermost))] + 5 + } + ); + + #[print_target_and_args(array_attrs)] [ + #![rustc_dummy(inner)] + true; 0 + ]; + [#![print_target_and_args(sixth)] 1 , 2]; //~^ ERROR expected non-macro inner attribute, found attribute macro [#![print_target_and_args(seventh)] true ; 5]; @@ -53,8 +88,25 @@ fn bar() { MyStruct { #![print_target_and_args(ninth)] field: true }; //~^ ERROR expected non-macro inner attribute, found attribute macro + + for _ in &[true] { + #![print_attr] //~ ERROR expected non-macro inner attribute + } + + let _ = { + #![print_attr] //~ ERROR expected non-macro inner attribute + }; + + let _ = async { + #![print_attr] //~ ERROR expected non-macro inner attribute + }; + + { + #![print_attr] //~ ERROR expected non-macro inner attribute + }; } + extern { fn weird_extern() { #![print_target_and_args_consume(tenth)] diff --git a/src/test/ui/proc-macro/inner-attrs.stderr b/src/test/ui/proc-macro/inner-attrs.stderr index db774cbfb8f..7f22c5f30d6 100644 --- a/src/test/ui/proc-macro/inner-attrs.stderr +++ b/src/test/ui/proc-macro/inner-attrs.stderr @@ -1,32 +1,56 @@ error: expected non-macro inner attribute, found attribute macro `print_target_and_args` - --> $DIR/inner-attrs.rs:27:9 + --> $DIR/inner-attrs.rs:49:9 | LL | (#![print_target_and_args(fifth)] 1, 2); | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute error: expected non-macro inner attribute, found attribute macro `print_target_and_args` - --> $DIR/inner-attrs.rs:43:9 + --> $DIR/inner-attrs.rs:78:9 | LL | [#![print_target_and_args(sixth)] 1 , 2]; | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute error: expected non-macro inner attribute, found attribute macro `print_target_and_args` - --> $DIR/inner-attrs.rs:45:9 + --> $DIR/inner-attrs.rs:80:9 | LL | [#![print_target_and_args(seventh)] true ; 5]; | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute error: expected non-macro inner attribute, found attribute macro `print_target_and_args` - --> $DIR/inner-attrs.rs:49:12 + --> $DIR/inner-attrs.rs:84:12 | LL | #![print_target_and_args(eighth)] | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute error: expected non-macro inner attribute, found attribute macro `print_target_and_args` - --> $DIR/inner-attrs.rs:54:19 + --> $DIR/inner-attrs.rs:89:19 | LL | MyStruct { #![print_target_and_args(ninth)] field: true }; | ^^^^^^^^^^^^^^^^^^^^^ not a non-macro inner attribute -error: aborting due to 5 previous errors +error: expected non-macro inner attribute, found attribute macro `print_attr` + --> $DIR/inner-attrs.rs:93:12 + | +LL | #![print_attr] + | ^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_attr` + --> $DIR/inner-attrs.rs:97:12 + | +LL | #![print_attr] + | ^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_attr` + --> $DIR/inner-attrs.rs:101:12 + | +LL | #![print_attr] + | ^^^^^^^^^^ not a non-macro inner attribute + +error: expected non-macro inner attribute, found attribute macro `print_attr` + --> $DIR/inner-attrs.rs:105:12 + | +LL | #![print_attr] + | ^^^^^^^^^^ not a non-macro inner attribute + +error: aborting due to 9 previous errors diff --git a/src/test/ui/proc-macro/inner-attrs.stdout b/src/test/ui/proc-macro/inner-attrs.stdout index ae04544e533..2f442e83002 100644 --- a/src/test/ui/proc-macro/inner-attrs.stdout +++ b/src/test/ui/proc-macro/inner-attrs.stdout @@ -11,40 +11,40 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:16:1: 16:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:16:3: 16:24 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:16:25: 16:31 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:16:24: 16:32 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:16:2: 16:33 (#0), }, Ident { ident: "fn", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:1: 17:3 (#0), }, Ident { ident: "foo", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:4: 17:7 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:7: 17:9 (#0), }, Group { delimiter: Brace, @@ -52,72 +52,72 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:5: 18:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:6: 18:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:8: 18:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "third", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:30: 18:35 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:29: 18:36 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:7: 18:37 (#0), }, Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:5: 19:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:6: 19:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:8: 19:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "fourth", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:30: 19:36 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:29: 19:37 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:7: 19:38 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:10: 20:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): second PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "second", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:16:25: 16:31 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn foo() @@ -125,16 +125,16 @@ PRINT-ATTR INPUT (DISPLAY): fn foo() PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:1: 17:3 (#0), }, Ident { ident: "foo", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:4: 17:7 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:7: 17:9 (#0), }, Group { delimiter: Brace, @@ -142,88 +142,88 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:5: 18:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:6: 18:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:8: 18:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "third", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:30: 18:35 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:29: 18:36 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:7: 18:37 (#0), }, Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:5: 19:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:6: 19:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:8: 19:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "fourth", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:30: 19:36 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:29: 19:37 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:7: 19:38 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:10: 20:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): third PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "third", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:18:30: 18:35 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn foo() { # ! [print_target_and_args(fourth)] } PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:1: 17:3 (#0), }, Ident { ident: "foo", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:4: 17:7 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:7: 17:9 (#0), }, Group { delimiter: Brace, @@ -231,138 +231,753 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:5: 19:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:6: 19:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:8: 19:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "fourth", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:30: 19:36 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:29: 19:37 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:7: 19:38 (#0), }, ], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:10: 20:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): fourth PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "fourth", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:19:30: 19:36 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn foo() { } PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:1: 17:3 (#0), }, Ident { ident: "foo", - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:4: 17:7 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:7: 17:9 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/inner-attrs.rs:17:1: 20:2 (#0), + span: $DIR/inner-attrs.rs:17:10: 20:2 (#0), }, ] -PRINT-ATTR_ARGS INPUT (DISPLAY): tuple_attrs +PRINT-ATTR_ARGS INPUT (DISPLAY): mod_first PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { - ident: "tuple_attrs", - span: $DIR/inner-attrs.rs:30:29: 30:40 (#0), + ident: "mod_first", + span: $DIR/inner-attrs.rs:22:25: 22:34 (#0), }, ] -PRINT-ATTR INPUT (DISPLAY): (# ! [cfg_attr(FALSE, rustc_dummy)] 3, 4, - { # ! [cfg_attr(not(FALSE), rustc_dummy(innermost))] 5 }) ; +PRINT-ATTR INPUT (DISPLAY): #[print_target_and_args(mod_second)] mod inline_mod +{ + # ! [print_target_and_args(mod_third)] # ! + [print_target_and_args(mod_fourth)] +} PRINT-ATTR INPUT (DEBUG): TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/inner-attrs.rs:23:1: 23:2 (#0), + }, Group { - delimiter: Parenthesis, + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "print_target_and_args", + span: $DIR/inner-attrs.rs:23:3: 23:24 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "mod_second", + span: $DIR/inner-attrs.rs:23:25: 23:35 (#0), + }, + ], + span: $DIR/inner-attrs.rs:23:24: 23:36 (#0), + }, + ], + span: $DIR/inner-attrs.rs:23:2: 23:37 (#0), + }, + Ident { + ident: "mod", + span: $DIR/inner-attrs.rs:24:1: 24:4 (#0), + }, + Ident { + ident: "inline_mod", + span: $DIR/inner-attrs.rs:24:5: 24:15 (#0), + }, + Group { + delimiter: Brace, stream: TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:31:9: 31:10 (#0), + span: $DIR/inner-attrs.rs:25:5: 25:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:31:10: 31:11 (#0), + span: $DIR/inner-attrs.rs:25:6: 25:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { - ident: "cfg_attr", - span: $DIR/inner-attrs.rs:31:12: 31:20 (#0), + ident: "print_target_and_args", + span: $DIR/inner-attrs.rs:25:8: 25:29 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "mod_third", + span: $DIR/inner-attrs.rs:25:30: 25:39 (#0), + }, + ], + span: $DIR/inner-attrs.rs:25:29: 25:40 (#0), + }, + ], + span: $DIR/inner-attrs.rs:25:7: 25:41 (#0), + }, + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:26:5: 26:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:26:6: 26:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "print_target_and_args", + span: $DIR/inner-attrs.rs:26:8: 26:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { - ident: "FALSE", - span: $DIR/inner-attrs.rs:31:21: 31:26 (#0), + ident: "mod_fourth", + span: $DIR/inner-attrs.rs:26:30: 26:40 (#0), + }, + ], + span: $DIR/inner-attrs.rs:26:29: 26:41 (#0), + }, + ], + span: $DIR/inner-attrs.rs:26:7: 26:42 (#0), + }, + ], + span: $DIR/inner-attrs.rs:24:16: 27:2 (#0), + }, +] +PRINT-ATTR_ARGS INPUT (DISPLAY): mod_second +PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ + Ident { + ident: "mod_second", + span: $DIR/inner-attrs.rs:23:25: 23:35 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): mod inline_mod +{ + # ! [print_target_and_args(mod_third)] # ! + [print_target_and_args(mod_fourth)] +} +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "mod", + span: $DIR/inner-attrs.rs:24:1: 24:4 (#0), + }, + Ident { + ident: "inline_mod", + span: $DIR/inner-attrs.rs:24:5: 24:15 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:25:5: 25:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:25:6: 25:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "print_target_and_args", + span: $DIR/inner-attrs.rs:25:8: 25:29 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "mod_third", + span: $DIR/inner-attrs.rs:25:30: 25:39 (#0), + }, + ], + span: $DIR/inner-attrs.rs:25:29: 25:40 (#0), + }, + ], + span: $DIR/inner-attrs.rs:25:7: 25:41 (#0), + }, + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:26:5: 26:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:26:6: 26:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "print_target_and_args", + span: $DIR/inner-attrs.rs:26:8: 26:29 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "mod_fourth", + span: $DIR/inner-attrs.rs:26:30: 26:40 (#0), + }, + ], + span: $DIR/inner-attrs.rs:26:29: 26:41 (#0), + }, + ], + span: $DIR/inner-attrs.rs:26:7: 26:42 (#0), + }, + ], + span: $DIR/inner-attrs.rs:24:16: 27:2 (#0), + }, +] +PRINT-ATTR_ARGS INPUT (DISPLAY): mod_third +PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ + Ident { + ident: "mod_third", + span: $DIR/inner-attrs.rs:25:30: 25:39 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): mod inline_mod { # ! [print_target_and_args(mod_fourth)] } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "mod", + span: $DIR/inner-attrs.rs:24:1: 24:4 (#0), + }, + Ident { + ident: "inline_mod", + span: $DIR/inner-attrs.rs:24:5: 24:15 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:26:5: 26:6 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:26:6: 26:7 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "print_target_and_args", + span: $DIR/inner-attrs.rs:26:8: 26:29 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "mod_fourth", + span: $DIR/inner-attrs.rs:26:30: 26:40 (#0), + }, + ], + span: $DIR/inner-attrs.rs:26:29: 26:41 (#0), + }, + ], + span: $DIR/inner-attrs.rs:26:7: 26:42 (#0), + }, + ], + span: $DIR/inner-attrs.rs:24:16: 27:2 (#0), + }, +] +PRINT-ATTR_ARGS INPUT (DISPLAY): mod_fourth +PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ + Ident { + ident: "mod_fourth", + span: $DIR/inner-attrs.rs:26:30: 26:40 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): mod inline_mod { } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "mod", + span: $DIR/inner-attrs.rs:24:1: 24:4 (#0), + }, + Ident { + ident: "inline_mod", + span: $DIR/inner-attrs.rs:24:5: 24:15 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/inner-attrs.rs:24:16: 27:2 (#0), + }, +] +PRINT-DERIVE INPUT (DISPLAY): struct MyDerivePrint +{ + field : + [u8 ; + { + match true + { + # ! [rustc_dummy(first)] # ! [rustc_dummy(second)] _ => + { # ! [rustc_dummy(third)] true } + } ; 0 + }] +} +PRINT-DERIVE INPUT (DEBUG): TokenStream [ + Ident { + ident: "struct", + span: $DIR/inner-attrs.rs:34:1: 34:7 (#0), + }, + Ident { + ident: "MyDerivePrint", + span: $DIR/inner-attrs.rs:34:8: 34:21 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "field", + span: $DIR/inner-attrs.rs:35:5: 35:10 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/inner-attrs.rs:35:10: 35:11 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "u8", + span: $DIR/inner-attrs.rs:35:13: 35:15 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/inner-attrs.rs:35:15: 35:16 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "match", + span: $DIR/inner-attrs.rs:36:9: 36:14 (#0), + }, + Ident { + ident: "true", + span: $DIR/inner-attrs.rs:36:15: 36:19 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/inner-attrs.rs:37:13: 37:14 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:37:14: 37:15 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:37:37: 37:48 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "first", + span: $DIR/inner-attrs.rs:37:49: 37:54 (#0), + }, + ], + span: $DIR/inner-attrs.rs:37:48: 37:55 (#0), + }, + ], + span: $DIR/inner-attrs.rs:37:13: 37:14 (#0), + }, + Punct { + ch: '#', + spacing: Alone, + span: $DIR/inner-attrs.rs:38:13: 38:14 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:38:14: 38:15 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:38:37: 38:48 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "second", + span: $DIR/inner-attrs.rs:38:49: 38:55 (#0), + }, + ], + span: $DIR/inner-attrs.rs:38:48: 38:56 (#0), + }, + ], + span: $DIR/inner-attrs.rs:38:13: 38:14 (#0), + }, + Ident { + ident: "_", + span: $DIR/inner-attrs.rs:39:13: 39:14 (#0), + }, + Punct { + ch: '=', + spacing: Joint, + span: $DIR/inner-attrs.rs:39:15: 39:17 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/inner-attrs.rs:39:15: 39:17 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/inner-attrs.rs:40:17: 40:18 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:40:18: 40:19 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:40:41: 40:52 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "third", + span: $DIR/inner-attrs.rs:40:53: 40:58 (#0), + }, + ], + span: $DIR/inner-attrs.rs:40:52: 40:59 (#0), + }, + ], + span: $DIR/inner-attrs.rs:40:17: 40:18 (#0), + }, + Ident { + ident: "true", + span: $DIR/inner-attrs.rs:41:17: 41:21 (#0), + }, + ], + span: $DIR/inner-attrs.rs:39:18: 42:14 (#0), + }, + ], + span: $DIR/inner-attrs.rs:36:20: 43:10 (#0), }, Punct { - ch: ',', + ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:31:26: 31:27 (#0), + span: $DIR/inner-attrs.rs:43:10: 43:11 (#0), + }, + Literal { + kind: Integer, + symbol: "0", + suffix: None, + span: $DIR/inner-attrs.rs:44:9: 44:10 (#0), + }, + ], + span: $DIR/inner-attrs.rs:35:17: 45:6 (#0), + }, + ], + span: $DIR/inner-attrs.rs:35:12: 45:7 (#0), + }, + ], + span: $DIR/inner-attrs.rs:34:22: 46:2 (#0), + }, +] +PRINT-ATTR_ARGS INPUT (DISPLAY): tuple_attrs +PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ + Ident { + ident: "tuple_attrs", + span: $DIR/inner-attrs.rs:52:29: 52:40 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): (3, 4, { # ! [cfg_attr(not(FALSE), rustc_dummy(innermost))] 5 }) ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Literal { + kind: Integer, + symbol: "3", + suffix: None, + span: $DIR/inner-attrs.rs:54:9: 54:10 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/inner-attrs.rs:54:10: 54:11 (#0), + }, + Literal { + kind: Integer, + symbol: "4", + suffix: None, + span: $DIR/inner-attrs.rs:54:12: 54:13 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/inner-attrs.rs:54:13: 54:14 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:55:13: 55:14 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:55:14: 55:15 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "cfg_attr", + span: $DIR/inner-attrs.rs:55:16: 55:24 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "not", + span: $DIR/inner-attrs.rs:55:25: 55:28 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "FALSE", + span: $DIR/inner-attrs.rs:55:29: 55:34 (#0), + }, + ], + span: $DIR/inner-attrs.rs:55:28: 55:35 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/inner-attrs.rs:55:35: 55:36 (#0), + }, + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:55:37: 55:48 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "innermost", + span: $DIR/inner-attrs.rs:55:49: 55:58 (#0), + }, + ], + span: $DIR/inner-attrs.rs:55:48: 55:59 (#0), + }, + ], + span: $DIR/inner-attrs.rs:55:24: 55:60 (#0), }, + ], + span: $DIR/inner-attrs.rs:55:15: 55:61 (#0), + }, + Literal { + kind: Integer, + symbol: "5", + suffix: None, + span: $DIR/inner-attrs.rs:56:13: 56:14 (#0), + }, + ], + span: $DIR/inner-attrs.rs:54:15: 57:10 (#0), + }, + ], + span: $DIR/inner-attrs.rs:52:43: 58:6 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/inner-attrs.rs:58:6: 58:7 (#0), + }, +] +PRINT-ATTR_ARGS INPUT (DISPLAY): array_attrs +PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ + Ident { + ident: "array_attrs", + span: $DIR/inner-attrs.rs:60:29: 60:40 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): [# ! [rustc_dummy(inner)] true ; 0] ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Group { + delimiter: Bracket, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Joint, + span: $DIR/inner-attrs.rs:61:9: 61:10 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/inner-attrs.rs:61:10: 61:11 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "rustc_dummy", + span: $DIR/inner-attrs.rs:61:12: 61:23 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ Ident { - ident: "rustc_dummy", - span: $DIR/inner-attrs.rs:31:28: 31:39 (#0), + ident: "inner", + span: $DIR/inner-attrs.rs:61:24: 61:29 (#0), }, ], - span: $DIR/inner-attrs.rs:31:20: 31:40 (#0), + span: $DIR/inner-attrs.rs:61:23: 61:30 (#0), }, ], - span: $DIR/inner-attrs.rs:31:11: 31:41 (#0), + span: $DIR/inner-attrs.rs:61:11: 61:31 (#0), + }, + Ident { + ident: "true", + span: $DIR/inner-attrs.rs:62:9: 62:13 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/inner-attrs.rs:62:13: 62:14 (#0), }, Literal { kind: Integer, + symbol: "0", + suffix: None, + span: $DIR/inner-attrs.rs:62:15: 62:16 (#0), + }, + ], + span: $DIR/inner-attrs.rs:60:43: 63:6 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/inner-attrs.rs:63:6: 63:7 (#0), + }, +] +PRINT-ATTR_ARGS INPUT (DISPLAY): tuple_attrs +PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ + Ident { + ident: "tuple_attrs", + span: $DIR/inner-attrs.rs:65:29: 65:40 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): (3, 4, { # ! [cfg_attr(not(FALSE), rustc_dummy(innermost))] 5 }) ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Literal { + kind: Integer, symbol: "3", suffix: None, - span: $DIR/inner-attrs.rs:32:9: 32:10 (#0), + span: $DIR/inner-attrs.rs:67:9: 67:10 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:32:10: 32:11 (#0), + span: $DIR/inner-attrs.rs:67:10: 67:11 (#0), }, Literal { kind: Integer, symbol: "4", suffix: None, - span: $DIR/inner-attrs.rs:32:12: 32:13 (#0), + span: $DIR/inner-attrs.rs:67:12: 67:13 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:32:13: 32:14 (#0), + span: $DIR/inner-attrs.rs:67:13: 67:14 (#0), }, Group { delimiter: Brace, @@ -370,85 +985,85 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:33:13: 33:14 (#0), + span: $DIR/inner-attrs.rs:68:13: 68:14 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:33:14: 33:15 (#0), + span: $DIR/inner-attrs.rs:68:14: 68:15 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg_attr", - span: $DIR/inner-attrs.rs:33:16: 33:24 (#0), + span: $DIR/inner-attrs.rs:68:16: 68:24 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/inner-attrs.rs:33:25: 33:28 (#0), + span: $DIR/inner-attrs.rs:68:25: 68:28 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/inner-attrs.rs:33:29: 33:34 (#0), + span: $DIR/inner-attrs.rs:68:29: 68:34 (#0), }, ], - span: $DIR/inner-attrs.rs:33:28: 33:35 (#0), + span: $DIR/inner-attrs.rs:68:28: 68:35 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/inner-attrs.rs:33:35: 33:36 (#0), + span: $DIR/inner-attrs.rs:68:35: 68:36 (#0), }, Ident { ident: "rustc_dummy", - span: $DIR/inner-attrs.rs:33:37: 33:48 (#0), + span: $DIR/inner-attrs.rs:68:37: 68:48 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "innermost", - span: $DIR/inner-attrs.rs:33:49: 33:58 (#0), + span: $DIR/inner-attrs.rs:68:49: 68:58 (#0), }, ], - span: $DIR/inner-attrs.rs:33:48: 33:59 (#0), + span: $DIR/inner-attrs.rs:68:48: 68:59 (#0), }, ], - span: $DIR/inner-attrs.rs:33:24: 33:60 (#0), + span: $DIR/inner-attrs.rs:68:24: 68:60 (#0), }, ], - span: $DIR/inner-attrs.rs:33:15: 33:61 (#0), + span: $DIR/inner-attrs.rs:68:15: 68:61 (#0), }, Literal { kind: Integer, symbol: "5", suffix: None, - span: $DIR/inner-attrs.rs:34:13: 34:14 (#0), + span: $DIR/inner-attrs.rs:69:13: 69:14 (#0), }, ], - span: $DIR/inner-attrs.rs:32:15: 35:10 (#0), + span: $DIR/inner-attrs.rs:67:15: 70:10 (#0), }, ], - span: $DIR/inner-attrs.rs:30:43: 36:6 (#0), + span: $DIR/inner-attrs.rs:65:43: 71:6 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:36:6: 36:7 (#0), + span: $DIR/inner-attrs.rs:71:6: 71:7 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): array_attrs PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "array_attrs", - span: $DIR/inner-attrs.rs:38:29: 38:40 (#0), + span: $DIR/inner-attrs.rs:73:29: 73:40 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): [# ! [rustc_dummy(inner)] true ; 0] ; @@ -459,82 +1074,82 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + span: $DIR/inner-attrs.rs:74:9: 74:10 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + span: $DIR/inner-attrs.rs:74:10: 74:11 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustc_dummy", - span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + span: $DIR/inner-attrs.rs:74:12: 74:23 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "inner", - span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + span: $DIR/inner-attrs.rs:74:24: 74:29 (#0), }, ], - span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + span: $DIR/inner-attrs.rs:74:23: 74:30 (#0), }, ], - span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + span: $DIR/inner-attrs.rs:74:11: 74:31 (#0), }, Ident { ident: "true", - span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + span: $DIR/inner-attrs.rs:75:9: 75:13 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + span: $DIR/inner-attrs.rs:75:13: 75:14 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + span: $DIR/inner-attrs.rs:75:15: 75:16 (#0), }, ], - span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + span: $DIR/inner-attrs.rs:73:43: 76:6 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/inner-attrs.rs:38:43: 41:7 (#0), + span: $DIR/inner-attrs.rs:76:6: 76:7 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): tenth PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "tenth", - span: $DIR/inner-attrs.rs:60:42: 60:47 (#0), + span: $DIR/inner-attrs.rs:112:42: 112:47 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): fn weird_extern() { } PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "fn", - span: $DIR/inner-attrs.rs:59:5: 61:6 (#0), + span: $DIR/inner-attrs.rs:111:5: 111:7 (#0), }, Ident { ident: "weird_extern", - span: $DIR/inner-attrs.rs:59:5: 61:6 (#0), + span: $DIR/inner-attrs.rs:111:8: 111:20 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/inner-attrs.rs:59:5: 61:6 (#0), + span: $DIR/inner-attrs.rs:111:20: 111:22 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/inner-attrs.rs:59:5: 61:6 (#0), + span: $DIR/inner-attrs.rs:111:23: 113:6 (#0), }, ] diff --git a/src/test/ui/proc-macro/issue-36935.stderr b/src/test/ui/proc-macro/issue-36935.stderr index 2b2e28fdb2f..079e134c6f8 100644 --- a/src/test/ui/proc-macro/issue-36935.stderr +++ b/src/test/ui/proc-macro/issue-36935.stderr @@ -1,11 +1,3 @@ -error: proc-macro derive panicked - --> $DIR/issue-36935.rs:6:20 - | -LL | #[derive(Identity, Panic)] - | ^^^^^ - | - = help: message: panic-derive - error[E0428]: the name `Baz` is defined multiple times --> $DIR/issue-36935.rs:7:1 | @@ -17,6 +9,14 @@ LL | struct Baz { | = note: `Baz` must be defined only once in the type namespace of this module +error: proc-macro derive panicked + --> $DIR/issue-36935.rs:6:20 + | +LL | #[derive(Identity, Panic)] + | ^^^^^ + | + = help: message: panic-derive + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0428`. diff --git a/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout b/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout index 7cbc0c669a5..d7adc5331cd 100644 --- a/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout +++ b/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout @@ -1279,152 +1279,152 @@ PRINT-DERIVE INPUT (DISPLAY): #[print_helper(a)] #[allow(dead_code)] #[print_hel [u8 ; { #[cfg(not(FALSE))] struct Inner ; match true - { #[allow(warnings)] false => { } _ => { } } ; #[print_helper(c)] + { #[allow(warnings)] false => { }, _ => { } } ; #[print_helper(c)] #[cfg(not(FALSE))] fn kept_fn() { # ! [cfg(not(FALSE))] let my_val = true ; } enum TupleEnum - { Foo(#[cfg(not(FALSE))] i32, u8), } struct + { Foo(#[cfg(not(FALSE))] i32, u8) } struct TupleStruct(#[cfg(not(FALSE))] i32, u8) ; 0 - }], #[print_helper(d)] fourth : B, + }], #[print_helper(d)] fourth : B } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:19:1: 19:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_helper", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:19:3: 19:15 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "a", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:19:16: 19:17 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:19:15: 19:18 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:19:2: 19:19 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 21:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "allow", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:24: 21:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "dead_code", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:30: 21:39 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:29: 21:40 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 21:2 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:24:1: 24:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_helper", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:24:3: 24:15 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "b", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:24:16: 24:17 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:24:15: 24:18 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:24:2: 24:19 (#0), }, Ident { ident: "struct", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:25:1: 25:7 (#0), }, Ident { ident: "Foo", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:25:8: 25:11 (#0), }, Punct { ch: '<', - spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + spacing: Joint, + span: $DIR/issue-75930-derive-cfg.rs:25:11: 25:12 (#0), }, Ident { ident: "B", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:25:29: 25:30 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:25:30: 25:31 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "second", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:27:40: 27:46 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:27:46: 27:47 (#0), }, Ident { ident: "bool", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:27:48: 27:52 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:27:52: 27:53 (#0), }, Ident { ident: "third", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:28:5: 28:10 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:28:10: 28:11 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "u8", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:28:13: 28:15 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:28:15: 28:16 (#0), }, Group { delimiter: Brace, @@ -1432,58 +1432,58 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:9: 30:10 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:11: 30:14 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:15: 30:18 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:19: 30:24 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:18: 30:25 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:14: 30:26 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:10: 30:27 (#0), }, Ident { ident: "struct", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:28: 30:34 (#0), }, Ident { ident: "Inner", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:35: 30:40 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:30:40: 30:41 (#0), }, Ident { ident: "match", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:32:9: 32:14 (#0), }, Ident { ident: "true", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:32:15: 32:19 (#0), }, Group { delimiter: Brace, @@ -1491,146 +1491,151 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:13: 34:14 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "allow", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:36: 34:41 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "warnings", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:42: 34:50 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:41: 34:51 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:13: 34:14 (#0), }, Ident { ident: "false", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:54: 34:59 (#0), }, Punct { ch: '=', spacing: Joint, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:60: 34:62 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:60: 34:62 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:34:63: 34:65 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/issue-75930-derive-cfg.rs:34:65: 34:66 (#0), }, Ident { ident: "_", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:35:13: 35:14 (#0), }, Punct { ch: '=', spacing: Joint, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:35:15: 35:17 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:35:15: 35:17 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:35:18: 35:20 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:32:20: 36:10 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:36:10: 36:11 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:9: 43:10 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_helper", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:11: 43:23 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "c", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:24: 43:25 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:23: 43:26 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:10: 43:27 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:28: 43:29 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:30: 43:33 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:34: 43:37 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:38: 43:43 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:37: 43:44 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:33: 43:45 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:29: 43:46 (#0), }, Ident { ident: "fn", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:47: 43:49 (#0), }, Ident { ident: "kept_fn", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:50: 43:57 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:57: 43:59 (#0), }, Group { delimiter: Brace, @@ -1638,82 +1643,82 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:13: 44:14 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:14: 44:15 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:16: 44:19 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:20: 44:23 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:24: 44:29 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:23: 44:30 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:19: 44:31 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:44:15: 44:32 (#0), }, Ident { ident: "let", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:45:13: 45:16 (#0), }, Ident { ident: "my_val", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:45:17: 45:23 (#0), }, Punct { ch: '=', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:45:24: 45:25 (#0), }, Ident { ident: "true", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:45:26: 45:30 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:45:30: 45:31 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:43:60: 46:10 (#0), }, Ident { ident: "enum", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:48:9: 48:13 (#0), }, Ident { ident: "TupleEnum", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:48:14: 48:23 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "Foo", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:49:13: 49:16 (#0), }, Group { delimiter: Parenthesis, @@ -1721,69 +1726,64 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:17: 52:18 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:19: 52:22 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:23: 52:26 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:27: 52:32 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:26: 52:33 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:22: 52:34 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:18: 52:35 (#0), }, Ident { ident: "i32", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:36: 52:39 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:52:39: 52:40 (#0), }, Ident { ident: "u8", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:53:39: 53:41 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), - }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:49:16: 54:14 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:48:24: 55:10 (#0), }, Ident { ident: "struct", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:57:9: 57:15 (#0), }, Ident { ident: "TupleStruct", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:57:16: 57:27 (#0), }, Group { delimiter: Parenthesis, @@ -1791,120 +1791,115 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:13: 59:14 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:15: 59:18 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:19: 59:22 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:23: 59:28 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:22: 59:29 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:18: 59:30 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:14: 59:31 (#0), }, Ident { ident: "i32", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:32: 59:35 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:59:35: 59:36 (#0), }, Ident { ident: "u8", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:61:13: 61:15 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:57:27: 62:10 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:62:10: 62:11 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:68:9: 68:10 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:28:17: 69:6 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:28:12: 69:7 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:69:7: 69:8 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:70:5: 70:6 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_helper", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:70:7: 70:19 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "d", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:70:20: 70:21 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:70:19: 70:22 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:70:6: 70:23 (#0), }, Ident { ident: "fourth", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:71:5: 71:11 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:71:11: 71:12 (#0), }, Ident { ident: "B", - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), - }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:71:13: 71:14 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:25:1: 72:2 (#0), + span: $DIR/issue-75930-derive-cfg.rs:25:32: 72:2 (#0), }, ] diff --git a/src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout b/src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout index 9b467a5970b..607c2954c7c 100644 --- a/src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout +++ b/src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout @@ -35,48 +35,48 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ stream: TokenStream [ Ident { ident: "mod", - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 27:8 (#0), }, Ident { ident: "bar", - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:9: 27:12 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Punct { ch: '#', - spacing: Joint, - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + spacing: Alone, + span: $DIR/issue-78675-captured-inner-attrs.rs:28:9: 28:16 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:28:9: 28:16 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "doc", - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:28:9: 28:16 (#0), }, Punct { ch: '=', spacing: Alone, - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:28:9: 28:16 (#0), }, Literal { kind: StrRaw(0), symbol: " Foo", suffix: None, - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:28:9: 28:16 (#0), }, ], - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:28:9: 28:16 (#0), }, ], - span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:13: 29:6 (#0), }, ], span: $DIR/issue-78675-captured-inner-attrs.rs:22:13: 22:18 (#4), diff --git a/src/test/ui/proc-macro/macro-rules-derive-cfg.stdout b/src/test/ui/proc-macro/macro-rules-derive-cfg.stdout index 5db18590bdf..a0b0cbb19e4 100644 --- a/src/test/ui/proc-macro/macro-rules-derive-cfg.stdout +++ b/src/test/ui/proc-macro/macro-rules-derive-cfg.stdout @@ -5,172 +5,167 @@ PRINT-DERIVE INPUT (DISPLAY): struct Foo { let a = #[rustc_dummy(first)] #[rustc_dummy(second)] { # ! [allow(unused)] 30 } ; 0 - }], + }] } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:17:9: 17:15 (#4), }, Ident { ident: "Foo", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:17:16: 17:19 (#4), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "val", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:18:13: 18:16 (#4), }, Punct { ch: ':', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:18:16: 18:17 (#4), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "bool", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:18:19: 18:23 (#4), }, Punct { ch: ';', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:18:23: 18:24 (#4), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "let", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:17: 19:20 (#4), }, Ident { ident: "a", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:21: 19:22 (#4), }, Punct { ch: '=', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:23: 19:24 (#4), }, Punct { ch: '#', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:25: 19:26 (#4), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustc_dummy", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:48: 19:59 (#4), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "first", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:60: 19:65 (#4), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:59: 19:66 (#4), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:25: 19:26 (#4), }, Punct { ch: '#', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:26:13: 26:14 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustc_dummy", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:26:36: 26:47 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:26:48: 26:54 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:26:47: 26:55 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:26:13: 26:14 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Punct { ch: '#', - spacing: Joint, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + spacing: Alone, + span: $DIR/macro-rules-derive-cfg.rs:27:5: 27:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:27:6: 27:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "allow", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:27:29: 27:34 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "unused", - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:27:35: 27:41 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:27:34: 27:42 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:27:5: 27:6 (#0), }, Literal { kind: Integer, symbol: "30", suffix: None, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:28:5: 28:7 (#0), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:26:58: 29:2 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:19:74: 19:75 (#4), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:20:17: 20:18 (#4), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:18:25: 21:14 (#4), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), - }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:18:18: 21:15 (#4), }, ], - span: $DIR/macro-rules-derive-cfg.rs:17:9: 22:10 (#4), + span: $DIR/macro-rules-derive-cfg.rs:17:20: 22:10 (#4), }, ] diff --git a/src/test/ui/proc-macro/nested-derive-cfg.stdout b/src/test/ui/proc-macro/nested-derive-cfg.stdout index cf4e5d94d8a..9a562c971c8 100644 --- a/src/test/ui/proc-macro/nested-derive-cfg.stdout +++ b/src/test/ui/proc-macro/nested-derive-cfg.stdout @@ -1,94 +1,81 @@ PRINT-DERIVE INPUT (DISPLAY): struct Foo -{ - my_array : - [bool ; { struct Inner { non_removed_inner_field : usize, } 0 }], -} +{ my_array : [bool ; { struct Inner { non_removed_inner_field : usize } 0 }] } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "struct", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:12:1: 12:7 (#0), }, Ident { ident: "Foo", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:12:8: 12:11 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "my_array", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:14:5: 14:13 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:14:13: 14:14 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "bool", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:14:16: 14:20 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:14:20: 14:21 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "struct", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:15:9: 15:15 (#0), }, Ident { ident: "Inner", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:15:16: 15:21 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "non_removed_inner_field", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:17:13: 17:36 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:17:36: 17:37 (#0), }, Ident { ident: "usize", - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), - }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:17:38: 17:43 (#0), }, ], - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:15:22: 18:10 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:19:9: 19:10 (#0), }, ], - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:14:22: 20:6 (#0), }, ], - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), - }, - Punct { - ch: ',', - spacing: Alone, - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:14:15: 20:7 (#0), }, ], - span: $DIR/nested-derive-cfg.rs:12:1: 21:2 (#0), + span: $DIR/nested-derive-cfg.rs:12:12: 21:2 (#0), }, ] diff --git a/src/test/ui/proc-macro/nonterminal-recollect-attr.rs b/src/test/ui/proc-macro/nonterminal-recollect-attr.rs index 5d4649b78c2..79c4ad4cd2a 100644 --- a/src/test/ui/proc-macro/nonterminal-recollect-attr.rs +++ b/src/test/ui/proc-macro/nonterminal-recollect-attr.rs @@ -1,6 +1,10 @@ // check-pass +// compile-flags: -Z span-debug // aux-build:nonterminal-recollect-attr.rs +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + extern crate nonterminal_recollect_attr; use nonterminal_recollect_attr::*; diff --git a/src/test/ui/proc-macro/nonterminal-recollect-attr.stdout b/src/test/ui/proc-macro/nonterminal-recollect-attr.stdout new file mode 100644 index 00000000000..7331a25abd3 --- /dev/null +++ b/src/test/ui/proc-macro/nonterminal-recollect-attr.stdout @@ -0,0 +1,72 @@ +First recollected: TokenStream [ + Group { + delimiter: None, + stream: TokenStream [ + Ident { + ident: "pub", + span: $DIR/nonterminal-recollect-attr.rs:20:11: 20:14 (#0), + }, + ], + span: $DIR/nonterminal-recollect-attr.rs:14:9: 14:11 (#4), + }, + Ident { + ident: "struct", + span: $DIR/nonterminal-recollect-attr.rs:14:12: 14:18 (#4), + }, + Ident { + ident: "Foo", + span: $DIR/nonterminal-recollect-attr.rs:14:19: 14:22 (#4), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "field", + span: $DIR/nonterminal-recollect-attr.rs:15:13: 15:18 (#4), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/nonterminal-recollect-attr.rs:15:18: 15:19 (#4), + }, + Ident { + ident: "u8", + span: $DIR/nonterminal-recollect-attr.rs:15:20: 15:22 (#4), + }, + ], + span: $DIR/nonterminal-recollect-attr.rs:14:23: 16:10 (#4), + }, +] +Second recollected: TokenStream [ + Ident { + ident: "pub", + span: $DIR/nonterminal-recollect-attr.rs:20:11: 20:14 (#0), + }, + Ident { + ident: "struct", + span: $DIR/nonterminal-recollect-attr.rs:14:12: 14:18 (#4), + }, + Ident { + ident: "Foo", + span: $DIR/nonterminal-recollect-attr.rs:14:19: 14:22 (#4), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "field", + span: $DIR/nonterminal-recollect-attr.rs:15:13: 15:18 (#4), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/nonterminal-recollect-attr.rs:15:18: 15:19 (#4), + }, + Ident { + ident: "u8", + span: $DIR/nonterminal-recollect-attr.rs:15:20: 15:22 (#4), + }, + ], + span: $DIR/nonterminal-recollect-attr.rs:14:23: 16:10 (#4), + }, +] diff --git a/src/test/ui/proc-macro/simple-tuple.rs b/src/test/ui/proc-macro/simple-tuple.rs new file mode 100644 index 00000000000..c94c5877e22 --- /dev/null +++ b/src/test/ui/proc-macro/simple-tuple.rs @@ -0,0 +1,19 @@ +// check-pass +// compile-flags: -Z span-debug --error-format human +// aux-build:test-macros.rs +// edition:2018 + +#![feature(proc_macro_hygiene)] + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] +extern crate test_macros; + +fn main() { + #[print_target_and_args(my_arg)] ( + #![cfg_attr(not(FALSE), allow(unused))] + 1, 2, 3 + ); +} diff --git a/src/test/ui/proc-macro/simple-tuple.stdout b/src/test/ui/proc-macro/simple-tuple.stdout new file mode 100644 index 00000000000..1cc8579a467 --- /dev/null +++ b/src/test/ui/proc-macro/simple-tuple.stdout @@ -0,0 +1,79 @@ +PRINT-ATTR_ARGS INPUT (DISPLAY): my_arg +PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ + Ident { + ident: "my_arg", + span: $DIR/simple-tuple.rs:15:29: 15:35 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): (# ! [allow(unused)] 1, 2, 3) ; +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/simple-tuple.rs:16:9: 16:10 (#0), + }, + Punct { + ch: '!', + spacing: Alone, + span: $DIR/simple-tuple.rs:16:10: 16:11 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "allow", + span: $DIR/simple-tuple.rs:16:33: 16:38 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "unused", + span: $DIR/simple-tuple.rs:16:39: 16:45 (#0), + }, + ], + span: $DIR/simple-tuple.rs:16:38: 16:46 (#0), + }, + ], + span: $DIR/simple-tuple.rs:16:9: 16:10 (#0), + }, + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/simple-tuple.rs:17:9: 17:10 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/simple-tuple.rs:17:10: 17:11 (#0), + }, + Literal { + kind: Integer, + symbol: "2", + suffix: None, + span: $DIR/simple-tuple.rs:17:12: 17:13 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/simple-tuple.rs:17:13: 17:14 (#0), + }, + Literal { + kind: Integer, + symbol: "3", + suffix: None, + span: $DIR/simple-tuple.rs:17:15: 17:16 (#0), + }, + ], + span: $DIR/simple-tuple.rs:15:38: 18:6 (#0), + }, + Punct { + ch: ';', + spacing: Alone, + span: $DIR/simple-tuple.rs:18:6: 18:7 (#0), + }, +] diff --git a/src/test/ui/proc-macro/trait-fn-args-2015.rs b/src/test/ui/proc-macro/trait-fn-args-2015.rs index 3a448d4b220..6b8df78a061 100644 --- a/src/test/ui/proc-macro/trait-fn-args-2015.rs +++ b/src/test/ui/proc-macro/trait-fn-args-2015.rs @@ -3,6 +3,8 @@ // check-pass // aux-build:test-macros.rs +#![allow(anonymous_parameters)] + #[macro_use] extern crate test_macros; diff --git a/src/test/ui/proc-macro/weird-braces.stdout b/src/test/ui/proc-macro/weird-braces.stdout index 25f0eaf0dd4..990829456e8 100644 --- a/src/test/ui/proc-macro/weird-braces.stdout +++ b/src/test/ui/proc-macro/weird-braces.stdout @@ -15,40 +15,40 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:17:1: 17:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:17:3: 17:24 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second_outer", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:17:25: 17:37 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:17:24: 17:38 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:17:2: 17:39 (#0), }, Ident { ident: "impl", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:1: 18:5 (#0), }, Ident { ident: "Bar", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:6: 18:9 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:9: 18:10 (#0), }, Group { delimiter: Brace, @@ -57,54 +57,54 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "1", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:11: 18:12 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:13: 18:14 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:15: 18:16 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:10: 18:17 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:17: 18:18 (#0), }, Ident { ident: "for", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:19: 18:22 (#0), }, Ident { ident: "Foo", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:23: 18:26 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:26: 18:27 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "true", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:28: 18:32 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:27: 18:33 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:33: 18:34 (#0), }, Group { delimiter: Brace, @@ -112,72 +112,72 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:5: 19:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:6: 19:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:8: 19:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "first_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:30: 19:41 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:29: 19:42 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:7: 19:43 (#0), }, Punct { ch: '#', spacing: Joint, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:5: 20:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:6: 20:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:8: 20:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:30: 20:42 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:29: 20:43 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:7: 20:44 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:35: 21:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): second_outer PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "second_outer", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:17:25: 17:37 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > @@ -188,16 +188,16 @@ PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "impl", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:1: 18:5 (#0), }, Ident { ident: "Bar", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:6: 18:9 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:9: 18:10 (#0), }, Group { delimiter: Brace, @@ -206,54 +206,54 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "1", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:11: 18:12 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:13: 18:14 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:15: 18:16 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:10: 18:17 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:17: 18:18 (#0), }, Ident { ident: "for", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:19: 18:22 (#0), }, Ident { ident: "Foo", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:23: 18:26 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:26: 18:27 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "true", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:28: 18:32 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:27: 18:33 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:33: 18:34 (#0), }, Group { delimiter: Brace, @@ -261,72 +261,72 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:5: 19:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:6: 19:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:8: 19:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "first_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:30: 19:41 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:29: 19:42 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:7: 19:43 (#0), }, Punct { ch: '#', spacing: Joint, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:5: 20:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:6: 20:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:8: 20:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:30: 20:42 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:29: 20:43 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:7: 20:44 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:35: 21:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): first_inner PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "first_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:19:30: 19:41 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > @@ -334,16 +334,16 @@ PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "impl", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:1: 18:5 (#0), }, Ident { ident: "Bar", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:6: 18:9 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:9: 18:10 (#0), }, Group { delimiter: Brace, @@ -352,54 +352,54 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "1", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:11: 18:12 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:13: 18:14 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:15: 18:16 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:10: 18:17 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:17: 18:18 (#0), }, Ident { ident: "for", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:19: 18:22 (#0), }, Ident { ident: "Foo", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:23: 18:26 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:26: 18:27 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "true", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:28: 18:32 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:27: 18:33 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:33: 18:34 (#0), }, Group { delimiter: Brace, @@ -407,58 +407,58 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:5: 20:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:6: 20:7 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_target_and_args", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:8: 20:29 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "second_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:30: 20:42 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:29: 20:43 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:7: 20:44 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:35: 21:2 (#0), }, ] PRINT-ATTR_ARGS INPUT (DISPLAY): second_inner PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "second_inner", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:20:30: 20:42 (#0), }, ] PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > { } PRINT-ATTR INPUT (DEBUG): TokenStream [ Ident { ident: "impl", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:1: 18:5 (#0), }, Ident { ident: "Bar", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:6: 18:9 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:9: 18:10 (#0), }, Group { delimiter: Brace, @@ -467,58 +467,58 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ kind: Integer, symbol: "1", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:11: 18:12 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:13: 18:14 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:15: 18:16 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:10: 18:17 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:17: 18:18 (#0), }, Ident { ident: "for", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:19: 18:22 (#0), }, Ident { ident: "Foo", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:23: 18:26 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:26: 18:27 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "true", - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:28: 18:32 (#0), }, ], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:27: 18:33 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:33: 18:34 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/weird-braces.rs:18:1: 21:2 (#0), + span: $DIR/weird-braces.rs:18:35: 21:2 (#0), }, ] diff --git a/src/test/ui/resolve/auxiliary/issue-80079.rs b/src/test/ui/resolve/auxiliary/issue-80079.rs new file mode 100644 index 00000000000..190ca75aba8 --- /dev/null +++ b/src/test/ui/resolve/auxiliary/issue-80079.rs @@ -0,0 +1,18 @@ +#![crate_type = "lib"] + +pub mod public { + use private_import; + + // should not be suggested since it is private + struct Foo; + + mod private_module { + // should not be suggested since it is private + pub struct Foo; + } +} + +mod private_import { + // should not be suggested since it is private + pub struct Foo; +} diff --git a/src/test/ui/resolve/issue-80079.rs b/src/test/ui/resolve/issue-80079.rs new file mode 100644 index 00000000000..4795ed062c8 --- /dev/null +++ b/src/test/ui/resolve/issue-80079.rs @@ -0,0 +1,12 @@ +// aux-build:issue-80079.rs + +// using a module from another crate should not cause errors to suggest private +// items in that module + +extern crate issue_80079; + +use issue_80079::public; + +fn main() { + let _ = Foo; //~ ERROR cannot find value `Foo` in this scope +} diff --git a/src/test/ui/resolve/issue-80079.stderr b/src/test/ui/resolve/issue-80079.stderr new file mode 100644 index 00000000000..93e8c0341a1 --- /dev/null +++ b/src/test/ui/resolve/issue-80079.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find value `Foo` in this scope + --> $DIR/issue-80079.rs:11:13 + | +LL | let _ = Foo; + | ^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/resolve/issue-81508.rs b/src/test/ui/resolve/issue-81508.rs new file mode 100644 index 00000000000..23605cd2fd9 --- /dev/null +++ b/src/test/ui/resolve/issue-81508.rs @@ -0,0 +1,22 @@ +// Confusing diagnostic when using variable as a type: +// +// Previous warnings indicate Foo is not used, when in fact it is +// used improperly as a variable or constant. New warning points +// out user may be trying to use variable as a type. Test demonstrates +// cases for both local variable and const. + +fn main() { + let Baz: &str = ""; + + println!("{}", Baz::Bar); //~ ERROR: failed to resolve: use of undeclared type `Baz` +} + +#[allow(non_upper_case_globals)] +pub const Foo: &str = ""; + +mod submod { + use super::Foo; + fn function() { + println!("{}", Foo::Bar); //~ ERROR: failed to resolve: use of undeclared type `Foo` + } +} diff --git a/src/test/ui/resolve/issue-81508.stderr b/src/test/ui/resolve/issue-81508.stderr new file mode 100644 index 00000000000..15555631b90 --- /dev/null +++ b/src/test/ui/resolve/issue-81508.stderr @@ -0,0 +1,21 @@ +error[E0433]: failed to resolve: use of undeclared type `Baz` + --> $DIR/issue-81508.rs:11:20 + | +LL | let Baz: &str = ""; + | --- help: `Baz` is defined here, but is not a type +LL | +LL | println!("{}", Baz::Bar); + | ^^^ use of undeclared type `Baz` + +error[E0433]: failed to resolve: use of undeclared type `Foo` + --> $DIR/issue-81508.rs:20:24 + | +LL | use super::Foo; + | ---------- help: `Foo` is defined here, but is not a type +LL | fn function() { +LL | println!("{}", Foo::Bar); + | ^^^ use of undeclared type `Foo` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/ui/rfc-2091-track-caller/error-with-naked.rs b/src/test/ui/rfc-2091-track-caller/error-with-naked.rs index 70ec0e3033c..9464ffe8722 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-naked.rs +++ b/src/test/ui/rfc-2091-track-caller/error-with-naked.rs @@ -1,3 +1,4 @@ +// needs-asm-support #![feature(asm, naked_functions)] #[track_caller] //~ ERROR cannot use `#[track_caller]` with `#[naked]` diff --git a/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr b/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr index 1b49148d629..5f17d6b2b51 100644 --- a/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr +++ b/src/test/ui/rfc-2091-track-caller/error-with-naked.stderr @@ -1,11 +1,11 @@ error[E0736]: cannot use `#[track_caller]` with `#[naked]` - --> $DIR/error-with-naked.rs:3:1 + --> $DIR/error-with-naked.rs:4:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ error[E0736]: cannot use `#[track_caller]` with `#[naked]` - --> $DIR/error-with-naked.rs:12:5 + --> $DIR/error-with-naked.rs:13:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ diff --git a/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs b/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs index fcfa610ec85..54f2f451bbe 100644 --- a/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs +++ b/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs @@ -1,6 +1,7 @@ // aux-build:ident-mac.rs #![feature(c_variadic)] +#![allow(anonymous_parameters)] extern crate ident_mac; use ident_mac::id; diff --git a/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr b/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr index 38c5050f342..e74d05d49b8 100644 --- a/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr +++ b/src/test/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr @@ -1,173 +1,173 @@ error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:10:23 + --> $DIR/proc-macro-cannot-be-used.rs:11:23 | LL | extern "C" { fn ffi(#[id] arg1: i32, #[id] ...); } | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:10:40 + --> $DIR/proc-macro-cannot-be-used.rs:11:40 | LL | extern "C" { fn ffi(#[id] arg1: i32, #[id] ...); } | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:14:40 + --> $DIR/proc-macro-cannot-be-used.rs:15:40 | LL | unsafe extern "C" fn cvar(arg1: i32, #[id] mut args: ...) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:17:30 + --> $DIR/proc-macro-cannot-be-used.rs:18:30 | LL | type Alias = extern "C" fn(#[id] u8, #[id] ...); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:17:40 + --> $DIR/proc-macro-cannot-be-used.rs:18:40 | LL | type Alias = extern "C" fn(#[id] u8, #[id] ...); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:21:11 + --> $DIR/proc-macro-cannot-be-used.rs:22:11 | LL | fn free(#[id] arg1: u8) { | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:23:18 + --> $DIR/proc-macro-cannot-be-used.rs:24:18 | LL | let lam = |#[id] W(x), #[id] y: usize| (); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:23:30 + --> $DIR/proc-macro-cannot-be-used.rs:24:30 | LL | let lam = |#[id] W(x), #[id] y: usize| (); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:29:20 + --> $DIR/proc-macro-cannot-be-used.rs:30:20 | LL | fn inherent1(#[id] self, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:29:32 + --> $DIR/proc-macro-cannot-be-used.rs:30:32 | LL | fn inherent1(#[id] self, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:32:20 + --> $DIR/proc-macro-cannot-be-used.rs:33:20 | LL | fn inherent2(#[id] &self, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:32:33 + --> $DIR/proc-macro-cannot-be-used.rs:33:33 | LL | fn inherent2(#[id] &self, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:35:24 + --> $DIR/proc-macro-cannot-be-used.rs:36:24 | LL | fn inherent3<'a>(#[id] &'a mut self, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:35:44 + --> $DIR/proc-macro-cannot-be-used.rs:36:44 | LL | fn inherent3<'a>(#[id] &'a mut self, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:38:24 + --> $DIR/proc-macro-cannot-be-used.rs:39:24 | LL | fn inherent4<'a>(#[id] self: Box<Self>, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:38:47 + --> $DIR/proc-macro-cannot-be-used.rs:39:47 | LL | fn inherent4<'a>(#[id] self: Box<Self>, #[id] arg1: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:41:40 + --> $DIR/proc-macro-cannot-be-used.rs:42:40 | LL | fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:41:56 + --> $DIR/proc-macro-cannot-be-used.rs:42:56 | LL | fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8) {} | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:47:17 + --> $DIR/proc-macro-cannot-be-used.rs:48:17 | LL | fn trait1(#[id] self, #[id] arg1: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:47:29 + --> $DIR/proc-macro-cannot-be-used.rs:48:29 | LL | fn trait1(#[id] self, #[id] arg1: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:50:17 + --> $DIR/proc-macro-cannot-be-used.rs:51:17 | LL | fn trait2(#[id] &self, #[id] arg1: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:50:30 + --> $DIR/proc-macro-cannot-be-used.rs:51:30 | LL | fn trait2(#[id] &self, #[id] arg1: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:53:21 + --> $DIR/proc-macro-cannot-be-used.rs:54:21 | LL | fn trait3<'a>(#[id] &'a mut self, #[id] arg1: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:53:41 + --> $DIR/proc-macro-cannot-be-used.rs:54:41 | LL | fn trait3<'a>(#[id] &'a mut self, #[id] arg1: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:56:21 + --> $DIR/proc-macro-cannot-be-used.rs:57:21 | LL | fn trait4<'a>(#[id] self: Box<Self>, #[id] arg1: u8, #[id] Vec<u8>); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:56:44 + --> $DIR/proc-macro-cannot-be-used.rs:57:44 | LL | fn trait4<'a>(#[id] self: Box<Self>, #[id] arg1: u8, #[id] Vec<u8>); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:56:60 + --> $DIR/proc-macro-cannot-be-used.rs:57:60 | LL | fn trait4<'a>(#[id] self: Box<Self>, #[id] arg1: u8, #[id] Vec<u8>); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:60:40 + --> $DIR/proc-macro-cannot-be-used.rs:61:40 | LL | fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8); | ^^ not a non-macro attribute error: expected non-macro attribute, found attribute macro `id` - --> $DIR/proc-macro-cannot-be-used.rs:60:56 + --> $DIR/proc-macro-cannot-be-used.rs:61:56 | LL | fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8); | ^^ not a non-macro attribute diff --git a/src/test/ui/sanitize/hwaddress.rs b/src/test/ui/sanitize/hwaddress.rs index ad5d0245457..88769b7cb45 100644 --- a/src/test/ui/sanitize/hwaddress.rs +++ b/src/test/ui/sanitize/hwaddress.rs @@ -1,7 +1,8 @@ // needs-sanitizer-support // needs-sanitizer-hwaddress // -// compile-flags: -Z sanitizer=hwaddress -O -g +// FIXME(#83989): codegen-units=1 triggers linker errors on aarch64-gnu +// compile-flags: -Z sanitizer=hwaddress -O -g -C codegen-units=16 // // run-fail // error-pattern: HWAddressSanitizer: tag-mismatch diff --git a/src/test/ui/simd/simd-intrinsic-float-math.rs b/src/test/ui/simd/simd-intrinsic-float-math.rs index 220a59535ca..7a4f7466559 100644 --- a/src/test/ui/simd/simd-intrinsic-float-math.rs +++ b/src/test/ui/simd/simd-intrinsic-float-math.rs @@ -20,16 +20,20 @@ extern "platform-intrinsic" { fn simd_fabs<T>(x: T) -> T; fn simd_fsin<T>(x: T) -> T; fn simd_fcos<T>(x: T) -> T; - fn simd_ceil<T>(x: T) -> T; fn simd_fexp<T>(x: T) -> T; fn simd_fexp2<T>(x: T) -> T; - fn simd_floor<T>(x: T) -> T; fn simd_fma<T>(x: T, y: T, z: T) -> T; fn simd_flog<T>(x: T) -> T; fn simd_flog10<T>(x: T) -> T; fn simd_flog2<T>(x: T) -> T; fn simd_fpow<T>(x: T, y: T) -> T; fn simd_fpowi<T>(x: T, y: i32) -> T; + + // rounding functions + fn simd_ceil<T>(x: T) -> T; + fn simd_floor<T>(x: T) -> T; + fn simd_round<T>(x: T) -> T; + fn simd_trunc<T>(x: T) -> T; } macro_rules! assert_approx_eq_f32 { @@ -64,18 +68,12 @@ fn main() { let r = simd_fcos(z); assert_approx_eq!(x, r); - let r = simd_ceil(h); - assert_approx_eq!(x, r); - let r = simd_fexp(z); assert_approx_eq!(x, r); let r = simd_fexp2(z); assert_approx_eq!(x, r); - let r = simd_floor(h); - assert_approx_eq!(z, r); - let r = simd_fma(x, h, h); assert_approx_eq!(x, r); @@ -99,5 +97,18 @@ fn main() { let r = simd_fsin(z); assert_approx_eq!(z, r); + + // rounding functions + let r = simd_floor(h); + assert_eq!(z, r); + + let r = simd_ceil(h); + assert_eq!(x, r); + + let r = simd_round(h); + assert_eq!(x, r); + + let r = simd_trunc(h); + assert_eq!(z, r); } } diff --git a/src/test/ui/specialization/issue-39448.rs b/src/test/ui/specialization/issue-39448.rs index 9dd47a4a17e..a15c4bd6b7f 100644 --- a/src/test/ui/specialization/issue-39448.rs +++ b/src/test/ui/specialization/issue-39448.rs @@ -18,7 +18,7 @@ impl FromA<u8> for u16 { } trait FromA<T> { - fn from(T) -> Self; + fn from(t: T) -> Self; } impl<T: A, U: A + FromA<T>> FromA<T> for U { diff --git a/src/test/ui/test-attrs/test-main-not-dead-attr.rs b/src/test/ui/test-attrs/test-main-not-dead-attr.rs index 628b1896ace..0b2a9a3541b 100644 --- a/src/test/ui/test-attrs/test-main-not-dead-attr.rs +++ b/src/test/ui/test-attrs/test-main-not-dead-attr.rs @@ -1,9 +1,9 @@ // run-pass // compile-flags: --test -#![feature(main)] +#![feature(rustc_attrs)] #![deny(dead_code)] -#[main] +#[rustc_main] fn foo() { panic!(); } diff --git a/src/test/ui/test-attrs/test-runner-hides-buried-main.rs b/src/test/ui/test-attrs/test-runner-hides-buried-main.rs index bf5482056d4..346aa868eb4 100644 --- a/src/test/ui/test-attrs/test-runner-hides-buried-main.rs +++ b/src/test/ui/test-attrs/test-runner-hides-buried-main.rs @@ -1,14 +1,14 @@ // run-pass // compile-flags: --test -#![feature(main)] +#![feature(rustc_attrs)] #![allow(dead_code)] mod a { fn b() { (|| { - #[main] + #[rustc_main] fn c() { panic!(); } })(); } diff --git a/src/test/ui/traits/associated_type_bound/issue-51446.rs b/src/test/ui/traits/associated_type_bound/issue-51446.rs new file mode 100644 index 00000000000..7dd95de73ba --- /dev/null +++ b/src/test/ui/traits/associated_type_bound/issue-51446.rs @@ -0,0 +1,34 @@ +// Regression test for #51446. +// check-pass + +trait Foo { + type Item; + fn get(&self) -> Self::Item; +} + +fn blah<T, F>(x: T, f: F) -> B<T::Item, impl Fn(T::Item)> +where + T: Foo, + F: Fn(T::Item), +{ + B { x: x.get(), f } +} + +pub struct B<T, F> +where + F: Fn(T), +{ + pub x: T, + pub f: F, +} + +impl Foo for i32 { + type Item = i32; + fn get(&self) -> i32 { + *self + } +} + +fn main() { + let _ = blah(0, |_| ()); +} diff --git a/src/test/ui/typeck/issue-65611.rs b/src/test/ui/typeck/issue-65611.rs index b74ee1b0c6e..7645311496d 100644 --- a/src/test/ui/typeck/issue-65611.rs +++ b/src/test/ui/typeck/issue-65611.rs @@ -17,7 +17,7 @@ pub unsafe trait Array { pub trait Index : PartialEq + Copy { fn to_usize(self) -> usize; - fn from(usize) -> Self; + fn from(i: usize) -> Self; } impl Index for usize { diff --git a/src/test/ui/union/union-derive.stderr b/src/test/ui/union/union-derive.stderr index 919c6d5ceda..6ef72c901a5 100644 --- a/src/test/ui/union/union-derive.stderr +++ b/src/test/ui/union/union-derive.stderr @@ -1,20 +1,14 @@ error: this trait cannot be derived for unions - --> $DIR/union-derive.rs:9:5 - | -LL | Debug, - | ^^^^^ - -error: this trait cannot be derived for unions - --> $DIR/union-derive.rs:8:5 + --> $DIR/union-derive.rs:4:5 | -LL | Default, - | ^^^^^^^ +LL | PartialEq, + | ^^^^^^^^^ error: this trait cannot be derived for unions - --> $DIR/union-derive.rs:7:5 + --> $DIR/union-derive.rs:5:5 | -LL | Hash, - | ^^^^ +LL | PartialOrd, + | ^^^^^^^^^^ error: this trait cannot be derived for unions --> $DIR/union-derive.rs:6:5 @@ -23,16 +17,22 @@ LL | Ord, | ^^^ error: this trait cannot be derived for unions - --> $DIR/union-derive.rs:5:5 + --> $DIR/union-derive.rs:7:5 | -LL | PartialOrd, - | ^^^^^^^^^^ +LL | Hash, + | ^^^^ error: this trait cannot be derived for unions - --> $DIR/union-derive.rs:4:5 + --> $DIR/union-derive.rs:8:5 | -LL | PartialEq, - | ^^^^^^^^^ +LL | Default, + | ^^^^^^^ + +error: this trait cannot be derived for unions + --> $DIR/union-derive.rs:9:5 + | +LL | Debug, + | ^^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/unwind-no-uwtable.rs b/src/test/ui/unwind-no-uwtable.rs new file mode 100644 index 00000000000..f249d3f4574 --- /dev/null +++ b/src/test/ui/unwind-no-uwtable.rs @@ -0,0 +1,34 @@ +// run-pass +// ignore-windows target requires uwtable +// ignore-wasm32-bare no proper panic=unwind support +// compile-flags: -C panic=unwind -C force-unwind-tables=n + +use std::panic::{self, AssertUnwindSafe}; + +struct Increase<'a>(&'a mut u8); + +impl Drop for Increase<'_> { + fn drop(&mut self) { + *self.0 += 1; + } +} + +#[inline(never)] +fn unwind() { + panic!(); +} + +#[inline(never)] +fn increase(count: &mut u8) { + let _increase = Increase(count); + unwind(); +} + +fn main() { + let mut count = 0; + assert!(panic::catch_unwind(AssertUnwindSafe( + #[inline(never)] + || increase(&mut count) + )).is_err()); + assert_eq!(count, 1); +} diff --git a/src/test/ui/wrong-mul-method-signature.stderr b/src/test/ui/wrong-mul-method-signature.stderr index 4c367fb9e9c..9f8896f01ee 100644 --- a/src/test/ui/wrong-mul-method-signature.stderr +++ b/src/test/ui/wrong-mul-method-signature.stderr @@ -1,26 +1,35 @@ error[E0053]: method `mul` has an incompatible type for trait - --> $DIR/wrong-mul-method-signature.rs:16:5 + --> $DIR/wrong-mul-method-signature.rs:16:21 | LL | fn mul(self, s: &f64) -> Vec1 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `f64`, found `&f64` + | ^^^^ + | | + | expected `f64`, found `&f64` + | help: change the parameter type to match the trait: `f64` | = note: expected fn pointer `fn(Vec1, f64) -> Vec1` found fn pointer `fn(Vec1, &f64) -> Vec1` error[E0053]: method `mul` has an incompatible type for trait - --> $DIR/wrong-mul-method-signature.rs:33:5 + --> $DIR/wrong-mul-method-signature.rs:33:21 | LL | fn mul(self, s: f64) -> Vec2 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Vec2`, found `f64` + | ^^^ + | | + | expected struct `Vec2`, found `f64` + | help: change the parameter type to match the trait: `Vec2` | = note: expected fn pointer `fn(Vec2, Vec2) -> f64` found fn pointer `fn(Vec2, f64) -> Vec2` error[E0053]: method `mul` has an incompatible type for trait - --> $DIR/wrong-mul-method-signature.rs:52:5 + --> $DIR/wrong-mul-method-signature.rs:52:29 | LL | fn mul(self, s: f64) -> f64 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `f64` + | ^^^ + | | + | expected `i32`, found `f64` + | help: change the output type to match the trait: `i32` | = note: expected fn pointer `fn(Vec3, _) -> i32` found fn pointer `fn(Vec3, _) -> f64` diff --git a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs index 995787c5336..7ff5a16ed86 100644 --- a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs +++ b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs @@ -1,11 +1,11 @@ // ignore-macos // ignore-windows -#![feature(main)] +#![feature(rustc_attrs)] #[warn(clippy::main_recursion)] #[allow(unconditional_recursion)] -#[main] +#[rustc_main] fn a() { println!("Hello, World!"); a(); diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 531a23d76a2..f31a24738df 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -44,6 +44,7 @@ impl EarlyProps { let mut props = EarlyProps::default(); let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some(); let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(); + let has_asm_support = util::has_asm_support(&config.target); let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target); @@ -76,6 +77,10 @@ impl EarlyProps { props.ignore = true; } + if !has_asm_support && config.parse_name_directive(ln, "needs-asm-support") { + props.ignore = true; + } + if !rustc_has_profiler_support && config.parse_needs_profiler_support(ln) { props.ignore = true; } @@ -131,9 +136,7 @@ impl EarlyProps { props.aux_crate.push(ac); } - if let Some(r) = config.parse_revisions(ln) { - props.revisions.extend(r); - } + config.parse_and_update_revisions(ln, &mut props.revisions); props.should_fail = props.should_fail || config.parse_name_directive(ln, "should-fail"); }); @@ -427,9 +430,7 @@ impl TestProps { self.compile_flags.push(format!("--edition={}", edition)); } - if let Some(r) = config.parse_revisions(ln) { - self.revisions.extend(r); - } + config.parse_and_update_revisions(ln, &mut self.revisions); if self.run_flags.is_none() { self.run_flags = config.parse_run_flags(ln); @@ -718,9 +719,16 @@ impl Config { self.parse_name_value_directive(line, "compile-flags") } - fn parse_revisions(&self, line: &str) -> Option<Vec<String>> { - self.parse_name_value_directive(line, "revisions") - .map(|r| r.split_whitespace().map(|t| t.to_string()).collect()) + fn parse_and_update_revisions(&self, line: &str, existing: &mut Vec<String>) { + if let Some(raw) = self.parse_name_value_directive(line, "revisions") { + let mut duplicates: HashSet<_> = existing.iter().cloned().collect(); + for revision in raw.split_whitespace().map(|r| r.to_string()) { + if !duplicates.insert(revision.clone()) { + panic!("Duplicate revision: `{}` in line `{}`", revision, raw); + } + existing.push(revision); + } + } } fn parse_run_flags(&self, line: &str) -> Option<String> { diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index ec99fde0df9..ca7458d255c 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -224,6 +224,17 @@ fn sanitizers() { } #[test] +fn asm_support() { + let mut config = config(); + + config.target = "avr-unknown-gnu-atmega328".to_owned(); + assert!(parse_rs(&config, "// needs-asm-support").ignore); + + config.target = "i686-unknown-netbsd".to_owned(); + assert!(!parse_rs(&config, "// needs-asm-support").ignore); +} + +#[test] fn test_extract_version_range() { use super::{extract_llvm_version, extract_version_range}; @@ -237,3 +248,10 @@ fn test_extract_version_range() { assert_eq!(extract_version_range(" - 4.5.6", extract_llvm_version), None); assert_eq!(extract_version_range("0 -", extract_llvm_version), None); } + +#[test] +#[should_panic(expected = "Duplicate revision: `rpass1` in line ` rpass1 rpass1`")] +fn test_duplicate_revisions() { + let config = config(); + parse_rs(&config, "// revisions: rpass1 rpass1"); +} diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 7c8cd699fe0..ecbaccf744d 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1951,6 +1951,7 @@ impl<'test> TestCx<'test> { if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) { rustc.args(&["--error-format", "json"]); } + rustc.arg("-Ccodegen-units=1"); rustc.arg("-Zui-testing"); rustc.arg("-Zdeduplicate-diagnostics=no"); rustc.arg("-Zemit-future-incompat-report"); diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index b302953708c..7dbd70948b8 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -128,6 +128,15 @@ const BIG_ENDIAN: &[&str] = &[ "sparcv9", ]; +static ASM_SUPPORTED_ARCHS: &[&str] = &[ + "x86", "x86_64", "arm", "aarch64", "riscv32", "riscv64", "nvptx64", "hexagon", "mips", + "mips64", "spirv", "wasm32", +]; + +pub fn has_asm_support(triple: &str) -> bool { + ASM_SUPPORTED_ARCHS.contains(&get_arch(triple)) +} + pub fn matches_os(triple: &str, name: &str) -> bool { // For the wasm32 bare target we ignore anything also ignored on emscripten // and then we also recognize `wasm32-bare` as the os for the target diff --git a/src/tools/miri b/src/tools/miri -Subproject 685ad70647c867944128f6c6bacf9483995eff7 +Subproject a86eab3e6c3d645769bfdb2d09efd84d1e1fcbc diff --git a/src/tools/rls b/src/tools/rls -Subproject fd1df1554a22accde727e8c4bdeb2a065627d10 +Subproject 32c0fe006dcdc13e1ca0ca31de543e4436c1299 diff --git a/src/tools/rust-analyzer b/src/tools/rust-analyzer -Subproject 19e09a4a54c75312aeaac04577f2d0e067463ab +Subproject 7be06139b632ee615fc18af04dd67947e2c794b diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index a551a97bda5..e583bd225a9 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -246,7 +246,7 @@ function lookForEntry(entry, data) { return null; } -function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) { +function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) { if (searchIndex[searchIndex.length - 1].length === 0) { searchIndex.pop(); } @@ -270,9 +270,9 @@ function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) { ALIASES = {}; finalJS += 'window = { "currentCrate": "' + crate + '", rootPath: "../" };\n'; finalJS += loadThings(["hasOwnProperty", "onEach"], 'function', extractFunction, storageJs); - finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, mainJs); - finalJS += loadThings(variablesToLoad, 'variable', extractVariable, mainJs); - finalJS += loadThings(functionsToLoad, 'function', extractFunction, mainJs); + finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, searchJs); + finalJS += loadThings(variablesToLoad, 'variable', extractVariable, searchJs); + finalJS += loadThings(functionsToLoad, 'function', extractFunction, searchJs); var loaded = loadContent(finalJS); var index = loaded.buildIndex(searchIndex.rawSearchIndex); @@ -382,12 +382,12 @@ function runChecks(testFile, loaded, index) { } function load_files(doc_folder, resource_suffix, crate) { - var mainJs = readFile(path.join(doc_folder, "main" + resource_suffix + ".js")); + var searchJs = readFile(path.join(doc_folder, "search" + resource_suffix + ".js")); var storageJs = readFile(path.join(doc_folder, "storage" + resource_suffix + ".js")); var searchIndex = readFile( path.join(doc_folder, "search-index" + resource_suffix + ".js")).split("\n"); - return loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate); + return loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate); } function showHelp() { diff --git a/src/tools/rustfmt b/src/tools/rustfmt -Subproject 7de6968ee22696b7feb6b477a05656de8927529 +Subproject 0bd2b1927c2b02a6fe7447d58e897cf1f1a1d41 diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index e99dd453280..3d5f39e8c90 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -379,11 +379,9 @@ pub fn check(path: &Path, bad: &mut bool) { if let Directive::Ignore(false) = skip_tab { tidy_error!(bad, "{}: ignoring tab characters unnecessarily", file.display()); } - // FIXME: Temporarily disabled to simplify landing the ignore-rules for the line - // length check (https://github.com/rust-lang/rust/issues/77548): - //if let Directive::Ignore(false) = skip_line_length { - // tidy_error!(bad, "{}: ignoring line length unnecessarily", file.display()); - //} + if let Directive::Ignore(false) = skip_line_length { + tidy_error!(bad, "{}: ignoring line length unnecessarily", file.display()); + } if let Directive::Ignore(false) = skip_file_length { tidy_error!(bad, "{}: ignoring file length unnecessarily", file.display()); } |
