diff options
459 files changed, 5709 insertions, 3181 deletions
diff --git a/.github/ISSUE_TEMPLATE/library_tracking_issue.md b/.github/ISSUE_TEMPLATE/library_tracking_issue.md index 3e42594c828..cbc4465fcfe 100644 --- a/.github/ISSUE_TEMPLATE/library_tracking_issue.md +++ b/.github/ISSUE_TEMPLATE/library_tracking_issue.md @@ -33,21 +33,41 @@ For most library features, it'd be useful to include a summarized version of the --> ```rust -... +// core::magic + +pub struct Magic; + +impl Magic { + pub fn magic(self); +} ``` ### Steps / History <!-- -In the simplest case, this is a PR implementing the feature followed by a PR -that stabilises the feature. However it's not uncommon for the feature to be -changed before stabilization. For larger features, the implementation could be -split up in multiple steps. +For larger features, more steps might be involved. +If the feature is changed later, please add those PRs here as well. --> -- [ ] Implementation: ... +- [ ] Implementation: #... +- [ ] Final commenting period (FCP) - [ ] Stabilization PR +<!-- +Once the feature has gone through a few release cycles and there are no +unresolved questions left, the feature might be ready for stabilization. + +If this feature didn't go through the RFC process, a final commenting period +(FCP) is always needed before stabilization. This works as follows: + +A library team member can kick off the stabilization process, at which point +the rfcbot will ask all the team members to verify they agree with +stabilization. Once enough members agree and there are no concerns, the final +commenting period begins: this issue will be marked as such and will be listed +in the next This Week in Rust newsletter. If no blocking concerns are raised in +that period of 10 days, a stabilzation PR can be opened by anyone. +--> + ### Unresolved Questions <!-- diff --git a/Cargo.lock b/Cargo.lock index 4676e4127e8..322b1632031 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -295,7 +295,7 @@ checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da" [[package]] name = "cargo" -version = "0.51.0" +version = "0.52.0" dependencies = [ "anyhow", "atty", @@ -568,7 +568,7 @@ dependencies = [ [[package]] name = "clippy" -version = "0.0.212" +version = "0.1.51" dependencies = [ "cargo_metadata 0.12.0", "clippy-mini-macro-test", @@ -589,7 +589,7 @@ version = "0.2.0" [[package]] name = "clippy_lints" -version = "0.0.212" +version = "0.1.51" dependencies = [ "cargo_metadata 0.12.0", "if_chain", @@ -666,9 +666,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.36" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd0782e0a7da7598164153173e5a5d4d9b1da094473c98dce0ff91406112369" +checksum = "3748f82c7d366a0b4950257d19db685d4958d2fa27c6d164a3f069fec42b748b" dependencies = [ "cc", "rustc-std-workspace-core", @@ -1973,9 +1973,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29be448fcafb00c5a8966c4020c2a5ffbbc333e5b96d0bb5ef54b5bd0524d9ff" +checksum = "21251d3eb9ca5e8ac5b73384ddaa483a9bbc7d7dcd656b1fa8f266634810334a" dependencies = [ "ammonia", "anyhow", @@ -3745,6 +3745,7 @@ version = "0.0.0" dependencies = [ "rustc_ast", "rustc_data_structures", + "rustc_feature", "rustc_index", "rustc_macros", "rustc_serialize", @@ -5343,7 +5344,7 @@ dependencies = [ "chrono", "lazy_static", "matchers", - "parking_lot 0.9.0", + "parking_lot 0.11.0", "regex", "serde", "serde_json", diff --git a/RELEASES.md b/RELEASES.md index 8f04980e390..4409b6ad7b1 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -45,7 +45,6 @@ Libraries - [`RangeInclusive` now checks for exhaustion when calling `contains` and indexing.][78109] - [`ToString::to_string` now no longer shrinks the internal buffer in the default implementation.][77997] -- [`ops::{Index, IndexMut}` are now implemented for fixed sized arrays of any length.][74989] Stabilized APIs --------------- @@ -110,7 +109,6 @@ related tools. [76199]: https://github.com/rust-lang/rust/pull/76199 [76119]: https://github.com/rust-lang/rust/pull/76119 [75914]: https://github.com/rust-lang/rust/pull/75914 -[74989]: https://github.com/rust-lang/rust/pull/74989 [79004]: https://github.com/rust-lang/rust/pull/79004 [78676]: https://github.com/rust-lang/rust/pull/78676 [79904]: https://github.com/rust-lang/rust/issues/79904 diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 8167bde0322..8601da6fa06 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -23,8 +23,8 @@ pub use GenericArgs::*; pub use UnsafeSource::*; use crate::ptr::P; -use crate::token::{self, CommentKind, DelimToken}; -use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream}; +use crate::token::{self, CommentKind, DelimToken, Token}; +use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -433,9 +433,9 @@ pub enum WherePredicate { impl WherePredicate { pub fn span(&self) -> Span { match self { - &WherePredicate::BoundPredicate(ref p) => p.span, - &WherePredicate::RegionPredicate(ref p) => p.span, - &WherePredicate::EqPredicate(ref p) => p.span, + WherePredicate::BoundPredicate(p) => p.span, + WherePredicate::RegionPredicate(p) => p.span, + WherePredicate::EqPredicate(p) => p.span, } } } @@ -1464,8 +1464,8 @@ pub enum MacArgs { Eq( /// Span of the `=` token. Span, - /// Token stream of the "value". - TokenStream, + /// "value" as a nonterminal token. + Token, ), } @@ -1478,10 +1478,10 @@ impl MacArgs { } pub fn span(&self) -> Option<Span> { - match *self { + match self { MacArgs::Empty => None, MacArgs::Delimited(dspan, ..) => Some(dspan.entire()), - MacArgs::Eq(eq_span, ref tokens) => Some(eq_span.to(tokens.span().unwrap_or(eq_span))), + MacArgs::Eq(eq_span, token) => Some(eq_span.to(token.span)), } } @@ -1490,7 +1490,8 @@ impl MacArgs { pub fn inner_tokens(&self) -> TokenStream { match self { MacArgs::Empty => TokenStream::default(), - MacArgs::Delimited(.., tokens) | MacArgs::Eq(.., tokens) => tokens.clone(), + MacArgs::Delimited(.., tokens) => tokens.clone(), + MacArgs::Eq(.., token) => TokenTree::Token(token.clone()).into(), } } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 726ae5e51f7..6a54cb4766b 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -476,7 +476,7 @@ impl MetaItemKind { pub fn mac_args(&self, span: Span) -> MacArgs { match self { MetaItemKind::Word => MacArgs::Empty, - MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.token_tree().into()), + MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.to_token()), MetaItemKind::List(list) => { let mut tts = Vec::new(); for (i, item) in list.iter().enumerate() { @@ -498,7 +498,10 @@ impl MetaItemKind { match *self { MetaItemKind::Word => vec![], MetaItemKind::NameValue(ref lit) => { - vec![TokenTree::token(token::Eq, span).into(), lit.token_tree().into()] + vec![ + TokenTree::token(token::Eq, span).into(), + TokenTree::Token(lit.to_token()).into(), + ] } MetaItemKind::List(ref list) => { let mut tokens = Vec::new(); @@ -554,10 +557,7 @@ impl MetaItemKind { MetaItemKind::list_from_tokens(tokens.clone()) } MacArgs::Delimited(..) => None, - MacArgs::Eq(_, tokens) => { - assert!(tokens.len() == 1); - MetaItemKind::name_value_from_tokens(&mut tokens.trees()) - } + MacArgs::Eq(_, token) => Lit::from_token(token).ok().map(MetaItemKind::NameValue), MacArgs::Empty => Some(MetaItemKind::Word), } } @@ -592,7 +592,7 @@ impl NestedMetaItem { fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> { match *self { NestedMetaItem::MetaItem(ref item) => item.token_trees_and_spacings(), - NestedMetaItem::Literal(ref lit) => vec![lit.token_tree().into()], + NestedMetaItem::Literal(ref lit) => vec![TokenTree::Token(lit.to_token()).into()], } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 97966cc3260..575552c4dce 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -365,18 +365,16 @@ pub fn visit_mac_args<T: MutVisitor>(args: &mut MacArgs, vis: &mut T) { visit_delim_span(dspan, vis); visit_tts(tokens, vis); } - MacArgs::Eq(eq_span, tokens) => { + MacArgs::Eq(eq_span, token) => { vis.visit_span(eq_span); - visit_tts(tokens, vis); - // The value in `#[key = VALUE]` must be visited as an expression for backward - // compatibility, so that macros can be expanded in that position. - if !vis.token_visiting_enabled() { - match Lrc::make_mut(&mut tokens.0).get_mut(0) { - Some((TokenTree::Token(token), _spacing)) => match &mut token.kind { - token::Interpolated(nt) => match Lrc::make_mut(nt) { - token::NtExpr(expr) => vis.visit_expr(expr), - t => panic!("unexpected token in key-value attribute: {:?}", t), - }, + if vis.token_visiting_enabled() { + visit_token(token, vis); + } else { + // The value in `#[key = VALUE]` must be visited as an expression for backward + // compatibility, so that macros can be expanded in that position. + match &mut token.kind { + token::Interpolated(nt) => match Lrc::make_mut(nt) { + token::NtExpr(expr) => vis.visit_expr(expr), t => panic!("unexpected token in key-value attribute: {:?}", t), }, t => panic!("unexpected token in key-value attribute: {:?}", t), diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index b311f9fdcb9..90bfb01d6c7 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -771,7 +771,7 @@ impl fmt::Display for NonterminalKind { } impl Nonterminal { - fn span(&self) -> Span { + pub fn span(&self) -> Span { match self { NtItem(item) => item.span, NtBlock(block) => block.span, diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 0550f53a96f..00354b42ebb 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -1,15 +1,15 @@ //! # Token Streams //! //! `TokenStream`s represent syntactic objects before they are converted into ASTs. -//! A `TokenStream` is, roughly speaking, a sequence (eg stream) of `TokenTree`s, -//! which are themselves a single `Token` or a `Delimited` subsequence of tokens. +//! A `TokenStream` is, roughly speaking, a sequence of [`TokenTree`]s, +//! which are themselves a single [`Token`] or a `Delimited` subsequence of tokens. //! //! ## Ownership //! //! `TokenStream`s are persistent data structures constructed as ropes with reference //! counted-children. In general, this means that calling an operation on a `TokenStream` //! (such as `slice`) produces an entirely new `TokenStream` from the borrowed reference to -//! the original. This essentially coerces `TokenStream`s into 'views' of their subparts, +//! the original. This essentially coerces `TokenStream`s into "views" of their subparts, //! and a borrowed `TokenStream` is sufficient to build an owned `TokenStream` without taking //! ownership of the original. @@ -24,9 +24,9 @@ use smallvec::{smallvec, SmallVec}; use std::{fmt, iter, mem}; -/// When the main rust parser encounters a syntax-extension invocation, it -/// parses the arguments to the invocation as a token-tree. This is a very -/// loose structure, such that all sorts of different AST-fragments can +/// When the main Rust parser encounters a syntax-extension invocation, it +/// parses the arguments to the invocation as a token tree. This is a very +/// loose structure, such that all sorts of different AST fragments can /// be passed to syntax extensions using a uniform type. /// /// If the syntax extension is an MBE macro, it will attempt to match its @@ -38,9 +38,9 @@ use std::{fmt, iter, mem}; /// Nothing special happens to misnamed or misplaced `SubstNt`s. #[derive(Debug, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] pub enum TokenTree { - /// A single token + /// A single token. Token(Token), - /// A delimited sequence of token trees + /// A delimited sequence of token trees. Delimited(DelimSpan, DelimToken, TokenStream), } @@ -62,7 +62,7 @@ where } impl TokenTree { - /// Checks if this TokenTree is equal to the other, regardless of span information. + /// Checks if this `TokenTree` is equal to the other, regardless of span information. pub fn eq_unspanned(&self, other: &TokenTree) -> bool { match (self, other) { (TokenTree::Token(token), TokenTree::Token(token2)) => token.kind == token2.kind, @@ -73,7 +73,7 @@ impl TokenTree { } } - /// Retrieves the TokenTree's span. + /// Retrieves the `TokenTree`'s span. pub fn span(&self) -> Span { match self { TokenTree::Token(token) => token.span, @@ -140,7 +140,7 @@ impl CreateTokenStream for TokenStream { } } -/// A lazy version of `TokenStream`, which defers creation +/// A lazy version of [`TokenStream`], which defers creation /// of an actual `TokenStream` until it is needed. /// `Box` is here only to reduce the structure size. #[derive(Clone)] @@ -188,11 +188,12 @@ impl<CTX> HashStable<CTX> for LazyTokenStream { } } -/// A `TokenStream` is an abstract sequence of tokens, organized into `TokenTree`s. +/// 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 /// instead of a representation of the abstract syntax tree. -/// Today's `TokenTree`s can still contain AST via `token::Interpolated` for back-compat. +/// Today's `TokenTree`s can still contain AST via `token::Interpolated` for +/// backwards compatability. #[derive(Clone, Debug, Default, Encodable, Decodable)] pub struct TokenStream(pub(crate) Lrc<Vec<TreeAndSpacing>>); @@ -429,7 +430,7 @@ impl TokenStreamBuilder { } } -/// By-reference iterator over a `TokenStream`. +/// By-reference iterator over a [`TokenStream`]. #[derive(Clone)] pub struct CursorRef<'t> { stream: &'t TokenStream, @@ -457,8 +458,8 @@ impl<'t> Iterator for CursorRef<'t> { } } -/// Owning by-value iterator over a `TokenStream`. -/// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones. +/// Owning by-value iterator over a [`TokenStream`]. +// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones. #[derive(Clone)] pub struct Cursor { pub stream: TokenStream, diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index f6f1ad0a9c3..106ffb2a0fd 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -2,7 +2,6 @@ use crate::ast::{self, Lit, LitKind}; use crate::token::{self, Token}; -use crate::tokenstream::TokenTree; use rustc_lexer::unescape::{unescape_byte, unescape_char}; use rustc_lexer::unescape::{unescape_byte_literal, unescape_literal, Mode}; @@ -225,13 +224,13 @@ impl Lit { Lit { token: kind.to_lit_token(), kind, span } } - /// Losslessly convert an AST literal into a token stream. - pub fn token_tree(&self) -> TokenTree { - let token = match self.token.kind { + /// Losslessly convert an AST literal into a token. + pub fn to_token(&self) -> Token { + let kind = match self.token.kind { token::Bool => token::Ident(self.token.symbol, false), _ => token::Literal(self.token), }; - TokenTree::token(token, self.span) + Token::new(kind, self.span) } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index a696626f8c4..2ba1c49edfa 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -15,7 +15,6 @@ use crate::ast::*; use crate::token; -use crate::tokenstream::TokenTree; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -905,12 +904,9 @@ pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) { MacArgs::Delimited(_dspan, _delim, _tokens) => {} // The value in `#[key = VALUE]` must be visited as an expression for backward // compatibility, so that macros can be expanded in that position. - MacArgs::Eq(_eq_span, tokens) => match tokens.trees_ref().next() { - Some(TokenTree::Token(token)) => match &token.kind { - token::Interpolated(nt) => match &**nt { - token::NtExpr(expr) => visitor.visit_expr(expr), - t => panic!("unexpected token in key-value attribute: {:?}", t), - }, + MacArgs::Eq(_eq_span, token) => match &token.kind { + token::Interpolated(nt) => match &**nt { + token::NtExpr(expr) => visitor.visit_expr(expr), t => panic!("unexpected token in key-value attribute: {:?}", t), }, t => panic!("unexpected token in key-value attribute: {:?}", t), diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index f81dc39842c..89557c29dd1 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -57,7 +57,7 @@ use rustc_session::lint::{builtin::BARE_TRAIT_OBJECTS, BuiltinLintDiagnostics, L use rustc_session::parse::ParseSess; use rustc_session::Session; use rustc_span::hygiene::ExpnId; -use rustc_span::source_map::{respan, DesugaringKind, ExpnData, ExpnKind}; +use rustc_span::source_map::{respan, DesugaringKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -206,8 +206,7 @@ pub trait ResolverAstLowering { ) -> LocalDefId; } -type NtToTokenstream = - fn(&Nonterminal, &ParseSess, Span, CanSynthesizeMissingTokens) -> TokenStream; +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. @@ -417,12 +416,7 @@ impl<'a> TokenStreamLowering<'a> { fn lower_token(&mut self, token: Token) -> TokenStream { match token.kind { token::Interpolated(nt) => { - let tts = (self.nt_to_tokenstream)( - &nt, - self.parse_sess, - token.span, - self.synthesize_tokens, - ); + let tts = (self.nt_to_tokenstream)(&nt, self.parse_sess, self.synthesize_tokens); TokenTree::Delimited( DelimSpan::from_single(token.span), DelimToken::NoDelim, @@ -743,10 +737,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: Span, allow_internal_unstable: Option<Lrc<[Symbol]>>, ) -> Span { - span.fresh_expansion(ExpnData { - allow_internal_unstable, - ..ExpnData::default(ExpnKind::Desugaring(reason), span, self.sess.edition(), None) - }) + span.mark_with_reason(allow_internal_unstable, reason, self.sess.edition()) } fn with_anonymous_lifetime_mode<R>( @@ -1022,10 +1013,36 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // This is an inert key-value attribute - it will never be visible to macros // after it gets lowered to HIR. Therefore, we can synthesize tokens with fake // spans to handle nonterminals in `#[doc]` (e.g. `#[doc = $e]`). - MacArgs::Eq(eq_span, ref tokens) => MacArgs::Eq( - eq_span, - self.lower_token_stream(tokens.clone(), CanSynthesizeMissingTokens::Yes), - ), + MacArgs::Eq(eq_span, ref token) => { + // In valid code the value is always representable as a single literal token. + fn unwrap_single_token(sess: &Session, tokens: TokenStream, span: Span) -> Token { + if tokens.len() != 1 { + sess.diagnostic() + .delay_span_bug(span, "multiple tokens in key-value attribute's value"); + } + match tokens.into_trees().next() { + Some(TokenTree::Token(token)) => token, + Some(TokenTree::Delimited(_, delim, tokens)) => { + if delim != token::NoDelim { + sess.diagnostic().delay_span_bug( + span, + "unexpected delimiter in key-value attribute's value", + ) + } + unwrap_single_token(sess, tokens, span) + } + None => Token::dummy(), + } + } + + let tokens = TokenStreamLowering { + parse_sess: &self.sess.parse_sess, + synthesize_tokens: CanSynthesizeMissingTokens::Yes, + nt_to_tokenstream: self.nt_to_tokenstream, + } + .lower_token(token.clone()); + MacArgs::Eq(eq_span, unwrap_single_token(self.sess, tokens, token.span)) + } } } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index e172f9d71ff..baeadb216dc 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -16,7 +16,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{error_code, pluralize, struct_span_err, Applicability}; use rustc_parse::validate_attr; use rustc_session::lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY; -use rustc_session::lint::LintBuffer; +use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::Session; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; @@ -213,14 +213,14 @@ impl<'a> AstValidator<'a> { err.emit(); } - fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, bool)) { + fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, Option<Ident>, bool)) { for Param { pat, .. } in &decl.inputs { match pat.kind { PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, None) | PatKind::Wild => {} - PatKind::Ident(BindingMode::ByValue(Mutability::Mut), _, None) => { - report_err(pat.span, true) + PatKind::Ident(BindingMode::ByValue(Mutability::Mut), ident, None) => { + report_err(pat.span, Some(ident), true) } - _ => report_err(pat.span, false), + _ => report_err(pat.span, None, false), } } } @@ -834,7 +834,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { match ty.kind { TyKind::BareFn(ref bfty) => { self.check_fn_decl(&bfty.decl, SelfSemantic::No); - Self::check_decl_no_pat(&bfty.decl, |span, _| { + Self::check_decl_no_pat(&bfty.decl, |span, _, _| { struct_span_err!( self.session, span, @@ -1212,11 +1212,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_pat(&mut self, pat: &'a Pat) { - match pat.kind { - PatKind::Lit(ref expr) => { + match &pat.kind { + PatKind::Lit(expr) => { self.check_expr_within_pat(expr, false); } - PatKind::Range(ref start, ref end, _) => { + PatKind::Range(start, end, _) => { if let Some(expr) = start { self.check_expr_within_pat(expr, true); } @@ -1289,7 +1289,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { // Functions without bodies cannot have patterns. if let FnKind::Fn(ctxt, _, sig, _, None) = fk { - Self::check_decl_no_pat(&sig.decl, |span, mut_ident| { + Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| { let (code, msg, label) = match ctxt { FnCtxt::Foreign => ( error_code!(E0130), @@ -1303,7 +1303,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ), }; if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) { - self.lint_buffer.buffer_lint(PATTERNS_IN_FNS_WITHOUT_BODY, id, span, msg); + if let Some(ident) = ident { + let diag = BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident); + self.lint_buffer.buffer_lint_with_diagnostic( + PATTERNS_IN_FNS_WITHOUT_BODY, + id, + span, + msg, + diag, + ) + } } else { self.err_handler() .struct_span_err(span, msg) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 435f32535b6..d65bc820f8f 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -14,6 +14,17 @@ use rustc_span::Span; use tracing::debug; macro_rules! gate_feature_fn { + ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{ + let (visitor, has_feature, span, name, explain, help) = + (&*$visitor, $has_feature, $span, $name, $explain, $help); + let has_feature: bool = has_feature(visitor.features); + debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature); + if !has_feature && !span.allows_unstable($name) { + feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain) + .help(help) + .emit(); + } + }}; ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{ let (visitor, has_feature, span, name, explain) = (&*$visitor, $has_feature, $span, $name, $explain); @@ -27,6 +38,9 @@ macro_rules! gate_feature_fn { } macro_rules! gate_feature_post { + ($visitor: expr, $feature: ident, $span: expr, $explain: expr, $help: expr) => { + gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain, $help) + }; ($visitor: expr, $feature: ident, $span: expr, $explain: expr) => { gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain) }; @@ -597,6 +611,13 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { let spans = sess.parse_sess.gated_spans.spans.borrow(); macro_rules! gate_all { + ($gate:ident, $msg:literal, $help:literal) => { + if let Some(spans) = spans.get(&sym::$gate) { + for span in spans { + gate_feature_post!(&visitor, $gate, *span, $msg, $help); + } + } + }; ($gate:ident, $msg:literal) => { if let Some(spans) = spans.get(&sym::$gate) { for span in spans { @@ -607,7 +628,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { } gate_all!(if_let_guard, "`if let` guards are experimental"); gate_all!(let_chains, "`let` expressions in this position are experimental"); - gate_all!(async_closure, "async closures are unstable"); + gate_all!( + async_closure, + "async closures are unstable", + "to use an async block, remove the `||`: `async {`" + ); gate_all!(generators, "yield syntax is experimental"); gate_all!(or_patterns, "or-patterns syntax is experimental"); gate_all!(raw_ref_op, "raw address of syntax is experimental"); diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs index b34ea41ab55..b88699f6ee1 100644 --- a/compiler/rustc_ast_pretty/src/pprust/mod.rs +++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs @@ -8,11 +8,6 @@ use rustc_ast as ast; use rustc_ast::token::{Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; -pub fn nonterminal_to_string_no_extra_parens(nt: &Nonterminal) -> String { - let state = State::without_insert_extra_parens(); - state.nonterminal_to_string(nt) -} - pub fn nonterminal_to_string(nt: &Nonterminal) -> String { State::new().nonterminal_to_string(nt) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index bdd378b34e1..2c8caf68f00 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -88,13 +88,6 @@ pub struct State<'a> { comments: Option<Comments<'a>>, ann: &'a (dyn PpAnn + 'a), is_expanded: bool, - // If `true`, additional parenthesis (separate from `ExprKind::Paren`) - // are inserted to ensure that proper precedence is preserved - // in the pretty-printed output. - // - // This is usually `true`, except when performing the pretty-print/reparse - // check in `nt_to_tokenstream` - insert_extra_parens: bool, } crate const INDENT_UNIT: usize = 4; @@ -115,7 +108,6 @@ pub fn print_crate<'a>( comments: Some(Comments::new(sm, filename, input)), ann, is_expanded, - insert_extra_parens: true, }; if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) { @@ -235,7 +227,6 @@ impl std::ops::DerefMut for State<'_> { } pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::DerefMut { - fn insert_extra_parens(&self) -> bool; fn comments(&mut self) -> &mut Option<Comments<'a>>; fn print_ident(&mut self, ident: Ident); fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool); @@ -454,10 +445,11 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere ), MacArgs::Empty | MacArgs::Eq(..) => { self.print_path(&item.path, false, 0); - if let MacArgs::Eq(_, tokens) = &item.args { + if let MacArgs::Eq(_, token) = &item.args { self.space(); self.word_space("="); - self.print_tts(tokens, true); + let token_str = self.token_to_string_ext(token, true); + self.word(token_str); } } } @@ -819,16 +811,12 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere fn to_string(&self, f: impl FnOnce(&mut State<'_>)) -> String { let mut printer = State::new(); - printer.insert_extra_parens = self.insert_extra_parens(); f(&mut printer); printer.s.eof() } } impl<'a> PrintState<'a> for State<'a> { - fn insert_extra_parens(&self) -> bool { - self.insert_extra_parens - } fn comments(&mut self) -> &mut Option<Comments<'a>> { &mut self.comments } @@ -865,17 +853,7 @@ impl<'a> PrintState<'a> for State<'a> { impl<'a> State<'a> { pub fn new() -> State<'a> { - State { - s: pp::mk_printer(), - comments: None, - ann: &NoAnn, - is_expanded: false, - insert_extra_parens: true, - } - } - - pub(super) fn without_insert_extra_parens() -> State<'a> { - State { insert_extra_parens: false, ..State::new() } + State { s: pp::mk_printer(), comments: None, ann: &NoAnn, is_expanded: false } } // Synthesizes a comment that was not textually present in the original source @@ -1680,8 +1658,7 @@ impl<'a> State<'a> { } /// Prints `expr` or `(expr)` when `needs_par` holds. - fn print_expr_cond_paren(&mut self, expr: &ast::Expr, mut needs_par: bool) { - needs_par &= self.insert_extra_parens; + fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) { if needs_par { self.popen(); } @@ -2677,7 +2654,6 @@ impl<'a> State<'a> { s.print_type_bounds(":", ¶m.bounds); if let Some(ref _default) = default { // FIXME(const_generics_defaults): print the `default` value here - todo!(); } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index 868f863b990..7114b987680 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -48,8 +48,8 @@ pub fn expand_deriving_hash( } fn hash_substructure(cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>) -> P<Expr> { - let state_expr = match &substr.nonself_args { - &[o_f] => o_f, + let state_expr = match substr.nonself_args { + [o_f] => o_f, _ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`"), }; let call_hash = |span, thing_expr| { @@ -64,9 +64,9 @@ fn hash_substructure(cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructu }; let mut stmts = Vec::new(); - let fields = match *substr.fields { - Struct(_, ref fs) | EnumMatching(_, 1, .., ref fs) => fs, - EnumMatching(.., ref fs) => { + let fields = match substr.fields { + Struct(_, fs) | EnumMatching(_, 1, .., fs) => fs, + EnumMatching(.., fs) => { let variant_value = deriving::call_intrinsic( cx, trait_span, diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 97cadb913ca..635890644d0 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -14,10 +14,9 @@ extern crate proc_macro; use crate::deriving::*; -use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtension, SyntaxExtensionKind}; +use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind}; use rustc_expand::proc_macro::BangProcMacro; -use rustc_span::edition::Edition; -use rustc_span::symbol::{sym, Ident}; +use rustc_span::symbol::sym; mod asm; mod assert; @@ -44,13 +43,8 @@ pub mod proc_macro_harness; pub mod standard_library_imports; pub mod test_harness; -pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand, edition: Edition) { - let mut register = |name, kind| { - resolver.register_builtin_macro( - Ident::with_dummy_span(name), - SyntaxExtension { is_builtin: true, ..SyntaxExtension::default(kind, edition) }, - ) - }; +pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { + let mut register = |name, kind| resolver.register_builtin_macro(name, kind); macro register_bang($($name:ident: $f:expr,)*) { $(register(sym::$name, SyntaxExtensionKind::LegacyBang(Box::new($f as MacroExpanderFn)));)* } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 230e11f274e..68f319ade1e 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -164,7 +164,8 @@ pub fn target_machine_factory( let code_model = to_llvm_code_model(sess.code_model()); - let features = attributes::llvm_target_features(sess).collect::<Vec<_>>(); + let mut features = llvm_util::handle_native_features(sess); + features.extend(attributes::llvm_target_features(sess).map(|s| s.to_owned())); let mut singlethread = sess.target.singlethread; // On the wasm target once the `atomics` feature is enabled that means that diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 707aaa2b53f..e359d9f8c9c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1708,6 +1708,10 @@ extern "C" { PM: &PassManager<'_>, ); + pub fn LLVMGetHostCPUFeatures() -> *mut c_char; + + pub fn LLVMDisposeMessage(message: *mut c_char); + // Stuff that's in llvm-wrapper/ because it's not upstream yet. /// Opens an object file. diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index a3139ce5a34..a9d57ea8b8a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -8,7 +8,7 @@ use rustc_session::config::PrintRequest; use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::slice; use std::str; @@ -221,6 +221,37 @@ pub fn target_cpu(sess: &Session) -> &str { handle_native(name) } +pub fn handle_native_features(sess: &Session) -> Vec<String> { + match sess.opts.cg.target_cpu { + Some(ref s) => { + if s != "native" { + return vec![]; + } + + let features_string = unsafe { + let ptr = llvm::LLVMGetHostCPUFeatures(); + let features_string = if !ptr.is_null() { + CStr::from_ptr(ptr) + .to_str() + .unwrap_or_else(|e| { + bug!("LLVM returned a non-utf8 features string: {}", e); + }) + .to_owned() + } else { + bug!("could not allocate host CPU features, LLVM returned a `null` string"); + }; + + llvm::LLVMDisposeMessage(ptr); + + features_string + }; + + features_string.split(",").map(|s| s.to_owned()).collect() + } + None => vec![], + } +} + pub fn tune_cpu(sess: &Session) -> Option<&str> { match sess.opts.debugging_opts.tune_cpu { Some(ref s) => Some(handle_native(&**s)), diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 3fc56eecdd0..07fde27b5a3 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -9,7 +9,7 @@ use rustc_codegen_ssa::{ }; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::Ty; -use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size}; +use rustc_target::abi::{Align, Endian, HasDataLayout, LayoutOf, Size}; fn round_pointer_up_to_alignment( bx: &mut Builder<'a, 'll, 'tcx>, @@ -52,7 +52,7 @@ fn emit_direct_ptr_va_arg( let next = bx.inbounds_gep(addr, &[full_direct_size]); bx.store(next, va_list_addr, bx.tcx().data_layout.pointer_align.abi); - if size.bytes() < slot_size.bytes() && &*bx.tcx().sess.target.endian == "big" { + if size.bytes() < slot_size.bytes() && bx.tcx().sess.target.endian == Endian::Big { let adjusted_size = bx.cx().const_i32((slot_size.bytes() - size.bytes()) as i32); let adjusted = bx.inbounds_gep(addr, &[adjusted_size]); (bx.bitcast(adjusted, bx.cx().type_ptr_to(llty)), addr_align) @@ -105,7 +105,7 @@ fn emit_aapcs_va_arg( let mut end = bx.build_sibling_block("va_arg.end"); let zero = bx.const_i32(0); let offset_align = Align::from_bytes(4).unwrap(); - assert!(&*bx.tcx().sess.target.endian == "little"); + assert_eq!(bx.tcx().sess.target.endian, Endian::Little); let gr_type = target_ty.is_any_ptr() || target_ty.is_integral(); let (reg_off, reg_top_index, slot_size) = if gr_type { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index a3a2ef04175..728795cf50b 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -895,7 +895,7 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { .unwrap_or_default(); match sess.opts.target_triple.triple() { - "x86_64-apple-darwin" => { + "aarch64-apple-darwin" | "x86_64-apple-darwin" => { // On Apple platforms, the sanitizer is always built as a dylib, and // LLVM will link to `@rpath/*.dylib`, so we need to specify an // rpath to the library as well (the rpath should be absolute, see @@ -1276,6 +1276,7 @@ fn exec_linker( fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind { let kind = match (crate_type, sess.crt_static(Some(crate_type)), sess.relocation_model()) { + (CrateType::Executable, _, _) if sess.is_wasi_reactor() => LinkOutputKind::WasiReactorExe, (CrateType::Executable, false, RelocModel::Pic) => LinkOutputKind::DynamicPicExe, (CrateType::Executable, false, _) => LinkOutputKind::DynamicNoPicExe, (CrateType::Executable, true, RelocModel::Pic) => LinkOutputKind::StaticPicExe, diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 3df956c465e..bb35e7ec894 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -314,6 +314,10 @@ impl<'a> Linker for GccLinker<'a> { self.cmd.arg("-static"); self.build_dylib(out_filename); } + LinkOutputKind::WasiReactorExe => { + self.linker_arg("--entry"); + self.linker_arg("_initialize"); + } } // VxWorks compiler driver introduced `--static-crt` flag specifically for rustc, // it switches linking for libc and similar system libraries to static without using @@ -662,6 +666,9 @@ impl<'a> Linker for MsvcLinker<'a> { arg.push(out_filename.with_extension("dll.lib")); self.cmd.arg(arg); } + LinkOutputKind::WasiReactorExe => { + panic!("can't link as reactor on non-wasi target"); + } } } @@ -1085,6 +1092,10 @@ impl<'a> Linker for WasmLd<'a> { LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => { self.cmd.arg("--no-entry"); } + LinkOutputKind::WasiReactorExe => { + self.cmd.arg("--entry"); + self.cmd.arg("_initialize"); + } } } diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 57e49ba8d1a..b1e372afc65 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -104,7 +104,7 @@ impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { ) { let cx = self.fx.cx; - if let &[ref proj_base @ .., elem] = place_ref.projection { + if let Some((place_base, elem)) = place_ref.last_projection() { let mut base_context = if context.is_mutating_use() { PlaceContext::MutatingUse(MutatingUseContext::Projection) } else { @@ -119,8 +119,7 @@ impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { ) ); if is_consume { - let base_ty = - mir::Place::ty_from(place_ref.local, proj_base, self.fx.mir, cx.tcx()); + let base_ty = mir::PlaceRef::ty(&place_base, self.fx.mir, cx.tcx()); let base_ty = self.fx.monomorphize(base_ty); // ZSTs don't require any actual memory access. @@ -175,11 +174,7 @@ impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { base_context = context; } - self.process_place( - &mir::PlaceRef { local: place_ref.local, projection: proj_base }, - base_context, - location, - ); + self.process_place(&place_base, base_context, location); // HACK(eddyb) this emulates the old `visit_projection_elem`, this // entire `visit_place`-like `process_place` method should be rewritten, // now that we have moved to the "slice of projections" representation. diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index e4f4c884470..958e4ebd078 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -178,16 +178,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { // Get the alignment of the field let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta); - // Bump the unaligned offset up to the appropriate alignment using the - // following expression: - // - // (unaligned offset + (align - 1)) & -align - - // Calculate offset. - let align_sub_1 = bx.sub(unsized_align, bx.cx().const_usize(1u64)); - let and_lhs = bx.add(unaligned_offset, align_sub_1); - let and_rhs = bx.neg(unsized_align); - let offset = bx.and(and_lhs, and_rhs); + // Bump the unaligned offset up to the appropriate alignment + let offset = round_up_const_value_to_alignment(bx, unaligned_offset, unsized_align); debug!("struct_field_ptr: DST field offset: {:?}", offset); @@ -514,7 +506,49 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn monomorphized_place_ty(&self, place_ref: mir::PlaceRef<'tcx>) -> Ty<'tcx> { let tcx = self.cx.tcx(); - let place_ty = mir::Place::ty_from(place_ref.local, place_ref.projection, self.mir, tcx); + let place_ty = mir::PlaceRef::ty(&place_ref, self.mir, tcx); self.monomorphize(place_ty.ty) } } + +fn round_up_const_value_to_alignment<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + value: Bx::Value, + align: Bx::Value, +) -> Bx::Value { + // In pseudo code: + // + // if value & (align - 1) == 0 { + // value + // } else { + // (value & !(align - 1)) + align + // } + // + // Usually this is written without branches as + // + // (value + align - 1) & !(align - 1) + // + // But this formula cannot take advantage of constant `value`. E.g. if `value` is known + // at compile time to be `1`, this expression should be optimized to `align`. However, + // optimization only holds if `align` is a power of two. Since the optimizer doesn't know + // that `align` is a power of two, it cannot perform this optimization. + // + // Instead we use + // + // value + (-value & (align - 1)) + // + // Since `align` is used only once, the expression can be optimized. For `value = 0` + // its optimized to `0` even in debug mode. + // + // NB: The previous version of this code used + // + // (value + align - 1) & -align + // + // Even though `-align == !(align - 1)`, LLVM failed to optimize this even for + // `value = 0`. Bug report: https://bugs.llvm.org/show_bug.cgi?id=48559 + let one = bx.const_usize(1); + let align_minus_1 = bx.sub(align, one); + let neg_value = bx.neg(value); + let offset = bx.and(neg_value, align_minus_1); + bx.add(value, offset) +} diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs index 01efcaf6f44..8afe94ac8db 100644 --- a/compiler/rustc_data_structures/src/fingerprint.rs +++ b/compiler/rustc_data_structures/src/fingerprint.rs @@ -4,7 +4,7 @@ use rustc_serialize::{ Decodable, Encodable, }; use std::hash::{Hash, Hasher}; -use std::mem; +use std::mem::{self, MaybeUninit}; #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)] pub struct Fingerprint(u64, u64); @@ -61,7 +61,7 @@ impl Fingerprint { } pub fn decode_opaque(decoder: &mut opaque::Decoder<'_>) -> Result<Fingerprint, String> { - let mut bytes = [0; 16]; + let mut bytes: [MaybeUninit<u8>; 16] = MaybeUninit::uninit_array(); decoder.read_raw_bytes(&mut bytes)?; diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index d57ab2433ad..c2a0d8ef7ea 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -566,6 +566,25 @@ fn stdout_isatty() -> bool { } } +// FIXME remove these and use winapi 0.3 instead +#[cfg(unix)] +fn stderr_isatty() -> bool { + unsafe { libc::isatty(libc::STDERR_FILENO) != 0 } +} + +#[cfg(windows)] +fn stderr_isatty() -> bool { + use winapi::um::consoleapi::GetConsoleMode; + use winapi::um::processenv::GetStdHandle; + use winapi::um::winbase::STD_ERROR_HANDLE; + + unsafe { + let handle = GetStdHandle(STD_ERROR_HANDLE); + let mut out = 0; + GetConsoleMode(handle, &mut out) != 0 + } +} + fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { let normalised = if code.starts_with('E') { code.to_string() } else { format!("E{0:0>4}", code) }; @@ -1290,7 +1309,7 @@ pub fn init_env_logger(env: &str) { Ok(value) => match value.as_ref() { "always" => true, "never" => false, - "auto" => stdout_isatty(), + "auto" => stderr_isatty(), _ => early_error( ErrorOutputType::default(), &format!( @@ -1299,7 +1318,7 @@ pub fn init_env_logger(env: &str) { ), ), }, - Err(std::env::VarError::NotPresent) => stdout_isatty(), + Err(std::env::VarError::NotPresent) => stderr_isatty(), Err(std::env::VarError::NotUnicode(_value)) => early_error( ErrorOutputType::default(), "non-Unicode log color value: expected one of always, never, or auto", diff --git a/compiler/rustc_error_codes/src/error_codes/E0435.md b/compiler/rustc_error_codes/src/error_codes/E0435.md index 424e5ce1e2e..798a20d48b6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0435.md +++ b/compiler/rustc_error_codes/src/error_codes/E0435.md @@ -7,6 +7,12 @@ let foo = 42; let a: [u8; foo]; // error: attempt to use a non-constant value in a constant ``` +'constant' means 'a compile-time value'. + +More details can be found in the [Variables and Mutability] section of the book. + +[Variables and Mutability]: https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants + To fix this error, please replace the value with a constant. Example: ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0492.md b/compiler/rustc_error_codes/src/error_codes/E0492.md index 1caa59999ae..79e7c069a91 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0492.md +++ b/compiler/rustc_error_codes/src/error_codes/E0492.md @@ -6,7 +6,7 @@ Erroneous code example: use std::sync::atomic::AtomicUsize; const A: AtomicUsize = AtomicUsize::new(0); -static B: &'static AtomicUsize = &A; +const B: &'static AtomicUsize = &A; // error: cannot borrow a constant which may contain interior mutability, // create a static instead ``` @@ -18,7 +18,7 @@ can't be changed via a shared `&` pointer, but interior mutability would allow it. That is, a constant value could be mutated. On the other hand, a `static` is explicitly a single memory location, which can be mutated at will. -So, in order to solve this error, either use statics which are `Sync`: +So, in order to solve this error, use statics which are `Sync`: ``` use std::sync::atomic::AtomicUsize; diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 32104e6f00d..00882bb287a 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -644,6 +644,8 @@ impl EmitterWriter { code_offset: usize, margin: Margin, ) { + // Tabs are assumed to have been replaced by spaces in calling code. + assert!(!source_string.contains('\t')); let line_len = source_string.len(); // Create the source line we will highlight. let left = margin.left(line_len); @@ -707,7 +709,7 @@ impl EmitterWriter { } let source_string = match file.get_line(line.line_index - 1) { - Some(s) => s, + Some(s) => replace_tabs(&*s), None => return Vec::new(), }; @@ -1376,8 +1378,17 @@ impl EmitterWriter { let file = annotated_file.file.clone(); let line = &annotated_file.lines[line_idx]; if let Some(source_string) = file.get_line(line.line_index - 1) { - let leading_whitespace = - source_string.chars().take_while(|c| c.is_whitespace()).count(); + let leading_whitespace = source_string + .chars() + .take_while(|c| c.is_whitespace()) + .map(|c| { + match c { + // Tabs are displayed as 4 spaces + '\t' => 4, + _ => 1, + } + }) + .sum(); if source_string.chars().any(|c| !c.is_whitespace()) { whitespace_margin = min(whitespace_margin, leading_whitespace); } @@ -1502,7 +1513,7 @@ impl EmitterWriter { self.draw_line( &mut buffer, - &unannotated_line, + &replace_tabs(&unannotated_line), annotated_file.lines[line_idx + 1].line_index - 1, last_buffer_line_num, width_offset, @@ -1598,7 +1609,7 @@ impl EmitterWriter { ); // print the suggestion draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); - buffer.append(row_num, line, Style::NoStyle); + buffer.append(row_num, &replace_tabs(line), Style::NoStyle); row_num += 1; } @@ -1930,6 +1941,10 @@ impl FileWithAnnotatedLines { } } +fn replace_tabs(str: &str) -> String { + str.replace('\t', " ") +} + fn draw_col_separator(buffer: &mut StyledBuffer, line: usize, col: usize) { buffer.puts(line, col, "| ", Style::LineNumber); } diff --git a/compiler/rustc_errors/src/styled_buffer.rs b/compiler/rustc_errors/src/styled_buffer.rs index f2d255d7d95..a4dd0f391bd 100644 --- a/compiler/rustc_errors/src/styled_buffer.rs +++ b/compiler/rustc_errors/src/styled_buffer.rs @@ -13,34 +13,13 @@ impl StyledBuffer { StyledBuffer { text: vec![], styles: vec![] } } - fn replace_tabs(&mut self) { - for (line_pos, line) in self.text.iter_mut().enumerate() { - let mut tab_pos = vec![]; - for (pos, c) in line.iter().enumerate() { - if *c == '\t' { - tab_pos.push(pos); - } - } - // start with the tabs at the end of the line to replace them with 4 space chars - for pos in tab_pos.iter().rev() { - assert_eq!(line.remove(*pos), '\t'); - // fix the position of the style to match up after replacing the tabs - let s = self.styles[line_pos].remove(*pos); - for _ in 0..4 { - line.insert(*pos, ' '); - self.styles[line_pos].insert(*pos, s); - } - } - } - } + pub fn render(&self) -> Vec<Vec<StyledString>> { + // Tabs are assumed to have been replaced by spaces in calling code. + assert!(self.text.iter().all(|r| !r.contains(&'\t'))); - pub fn render(&mut self) -> Vec<Vec<StyledString>> { let mut output: Vec<Vec<StyledString>> = vec![]; let mut styled_vec: Vec<StyledString> = vec![]; - // before we render, replace tabs with spaces - self.replace_tabs(); - for (row, row_style) in self.text.iter().zip(&self.styles) { let mut current_style = Style::NoStyle; let mut current_text = String::new(); diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 774a0764d11..2f43940a9dc 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -12,7 +12,7 @@ use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{DiagnosticBuilder, ErrorReported}; use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS}; use rustc_session::{parse::ParseSess, Limit, Session}; -use rustc_span::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::def_id::DefId; use rustc_span::edition::Edition; use rustc_span::hygiene::{AstPass, ExpnData, ExpnId, ExpnKind}; use rustc_span::source_map::SourceMap; @@ -141,7 +141,7 @@ impl Annotatable { } crate fn into_tokens(self, sess: &ParseSess) -> TokenStream { - nt_to_tokenstream(&self.into_nonterminal(), sess, DUMMY_SP, CanSynthesizeMissingTokens::No) + nt_to_tokenstream(&self.into_nonterminal(), sess, CanSynthesizeMissingTokens::No) } pub fn expect_item(self) -> P<ast::Item> { @@ -728,9 +728,7 @@ pub struct SyntaxExtension { pub edition: Edition, /// Built-in macros have a couple of special properties like availability /// in `#[no_implicit_prelude]` modules, so we have to keep this flag. - pub is_builtin: bool, - /// We have to identify macros providing a `Copy` impl early for compatibility reasons. - pub is_derive_copy: bool, + pub builtin_name: Option<Symbol>, } impl SyntaxExtension { @@ -758,8 +756,7 @@ impl SyntaxExtension { deprecation: None, helper_attrs: Vec::new(), edition, - is_builtin: false, - is_derive_copy: false, + builtin_name: None, kind, } } @@ -785,7 +782,9 @@ impl SyntaxExtension { } } - let is_builtin = sess.contains_name(attrs, sym::rustc_builtin_macro); + let builtin_name = sess + .find_by_name(attrs, sym::rustc_builtin_macro) + .map(|a| a.value_str().unwrap_or(name)); let (stability, const_stability) = attr::find_stability(&sess, attrs, span); if const_stability.is_some() { sess.parse_sess @@ -803,8 +802,7 @@ impl SyntaxExtension { deprecation: attr::find_deprecation(&sess, attrs).map(|(d, _)| d), helper_attrs, edition, - is_builtin, - is_derive_copy: is_builtin && name == sym::Copy, + builtin_name, } } @@ -842,19 +840,17 @@ impl SyntaxExtension { descr: Symbol, macro_def_id: Option<DefId>, ) -> ExpnData { - ExpnData { - kind: ExpnKind::Macro(self.macro_kind(), descr), + ExpnData::new( + ExpnKind::Macro(self.macro_kind(), descr), parent, call_site, - def_site: self.span, - allow_internal_unstable: self.allow_internal_unstable.clone(), - allow_internal_unsafe: self.allow_internal_unsafe, - local_inner_macros: self.local_inner_macros, - edition: self.edition, + self.span, + self.allow_internal_unstable.clone(), + self.allow_internal_unsafe, + self.local_inner_macros, + self.edition, macro_def_id, - krate: LOCAL_CRATE, - orig_id: None, - } + ) } } @@ -872,7 +868,7 @@ pub trait ResolverExpand { fn resolve_dollar_crates(&mut self); fn visit_ast_fragment_with_placeholders(&mut self, expn_id: ExpnId, fragment: &AstFragment); - fn register_builtin_macro(&mut self, ident: Ident, ext: SyntaxExtension); + fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind); fn expansion_for_ast_pass( &mut self, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 5d40d478b96..16913dbb1ab 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -743,7 +743,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { AttrStyle::Inner => rustc_parse::fake_token_stream( &self.cx.sess.parse_sess, &item.into_nonterminal(), - span, ), }; let attr_item = attr.unwrap_normal_item(); @@ -1020,15 +1019,16 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // with exception of the derive container case which is not resolved and can get // its expansion data immediately. let expn_data = match &kind { - InvocationKind::DeriveContainer { item, .. } => Some(ExpnData { - parent: self.cx.current_expansion.id, - ..ExpnData::default( + InvocationKind::DeriveContainer { item, .. } => { + let mut expn_data = ExpnData::default( ExpnKind::Macro(MacroKind::Attr, sym::derive), item.span(), self.cx.sess.parse_sess.edition, None, - ) - }), + ); + expn_data.parent = self.cx.current_expansion.id; + Some(expn_data) + } _ => None, }; let expn_id = ExpnId::fresh(expn_data); @@ -1543,13 +1543,8 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { } fn visit_item_kind(&mut self, item: &mut ast::ItemKind) { - match item { - ast::ItemKind::MacroDef(..) => {} - _ => { - self.cfg.configure_item_kind(item); - noop_visit_item_kind(item, self); - } - } + self.cfg.configure_item_kind(item); + noop_visit_item_kind(item, self); } fn flat_map_generic_param( diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index ce19e813bb3..d040539cd7e 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -258,12 +258,9 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> { fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> { match item.kind { - ast::ItemKind::MacCall(_) => return self.remove(item.id).make_items(), - ast::ItemKind::MacroDef(_) => return smallvec![item], - _ => {} + ast::ItemKind::MacCall(_) => self.remove(item.id).make_items(), + _ => noop_flat_map_item(item, self), } - - noop_flat_map_item(item, self) } fn flat_map_trait_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> { diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index e8e098b6212..02129e9b5e5 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -94,12 +94,7 @@ impl MultiItemModifier for ProcMacroDerive { let input = if item.pretty_printing_compatibility_hack() { TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into() } else { - nt_to_tokenstream( - &item, - &ecx.sess.parse_sess, - DUMMY_SP, - CanSynthesizeMissingTokens::Yes, - ) + nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::Yes) }; let server = proc_macro_server::Rustc::new(ecx); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 02ae842675f..b6195d3bbc4 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -179,7 +179,7 @@ impl FromInternal<(TreeAndSpacing, &'_ ParseSess, &'_ mut Vec<Self>)> { TokenTree::Ident(Ident::new(sess, name.name, is_raw, name.span)) } else { - let stream = nt_to_tokenstream(&nt, sess, span, CanSynthesizeMissingTokens::No); + let stream = nt_to_tokenstream(&nt, sess, CanSynthesizeMissingTokens::No); TokenTree::Group(Group { delimiter: Delimiter::None, stream, diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 252e96b47c6..3b54ffbc3f0 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -626,6 +626,9 @@ declare_features! ( /// Allows const generics to have default values (e.g. `struct Foo<const N: usize = 3>(...);`). (active, const_generics_defaults, "1.51.0", Some(44580), None), + /// Allows references to types with interior mutability within constants + (active, const_refs_to_cell, "1.51.0", Some(80384), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index fa8edba629e..3ed5320da73 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -442,7 +442,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Internal attributes, Macro related: // ========================================================================== - rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word), IMPL_DETAIL), + rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word, NameValueStr: "name"), IMPL_DETAIL), rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE), rustc_attr!( rustc_macro_transparency, AssumedUsed, diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs index 7742961e65d..87e97c746ef 100644 --- a/compiler/rustc_fs_util/src/lib.rs +++ b/compiler/rustc_fs_util/src/lib.rs @@ -62,8 +62,10 @@ pub enum LinkOrCopy { pub fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Q) -> io::Result<LinkOrCopy> { let p = p.as_ref(); let q = q.as_ref(); - if q.exists() { - fs::remove_file(&q)?; + match fs::remove_file(&q) { + Ok(()) => (), + Err(err) if err.kind() == io::ErrorKind::NotFound => (), + Err(err) => return Err(err), } match fs::hard_link(p, q) { diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index b24c208c76a..c14165454ed 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -9,6 +9,7 @@ doctest = false [dependencies] rustc_target = { path = "../rustc_target" } +rustc_feature = { path = "../rustc_feature" } rustc_macros = { path = "../rustc_macros" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ebeb1bae2a3..acd254ae85c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -290,6 +290,14 @@ impl GenericArg<'_> { GenericArg::Const(_) => "const", } } + + pub fn to_ord(&self, feats: &rustc_feature::Features) -> ast::ParamKindOrd { + match self { + GenericArg::Lifetime(_) => ast::ParamKindOrd::Lifetime, + GenericArg::Type(_) => ast::ParamKindOrd::Type, + GenericArg::Const(_) => ast::ParamKindOrd::Const { unordered: feats.const_generics }, + } + } } #[derive(Debug, HashStable_Generic)] @@ -378,9 +386,9 @@ impl GenericBound<'_> { pub fn span(&self) -> Span { match self { - &GenericBound::Trait(ref t, ..) => t.span, - &GenericBound::LangItemTrait(_, span, ..) => span, - &GenericBound::Outlives(ref l) => l.span, + GenericBound::Trait(t, ..) => t.span, + GenericBound::LangItemTrait(_, span, ..) => *span, + GenericBound::Outlives(l) => l.span, } } } @@ -538,9 +546,9 @@ pub enum WherePredicate<'hir> { impl WherePredicate<'_> { pub fn span(&self) -> Span { match self { - &WherePredicate::BoundPredicate(ref p) => p.span, - &WherePredicate::RegionPredicate(ref p) => p.span, - &WherePredicate::EqPredicate(ref p) => p.span, + WherePredicate::BoundPredicate(p) => p.span, + WherePredicate::RegionPredicate(p) => p.span, + WherePredicate::EqPredicate(p) => p.span, } } } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 87a2434152f..b0e82214cf9 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -896,8 +896,8 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( visitor: &mut V, predicate: &'v WherePredicate<'v>, ) { - match predicate { - &WherePredicate::BoundPredicate(WhereBoundPredicate { + match *predicate { + WherePredicate::BoundPredicate(WhereBoundPredicate { ref bounded_ty, bounds, bound_generic_params, @@ -907,11 +907,11 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_generic_param, bound_generic_params); } - &WherePredicate::RegionPredicate(WhereRegionPredicate { ref lifetime, bounds, .. }) => { + WherePredicate::RegionPredicate(WhereRegionPredicate { ref lifetime, bounds, .. }) => { visitor.visit_lifetime(lifetime); walk_list!(visitor, visit_param_bound, bounds); } - &WherePredicate::EqPredicate(WhereEqPredicate { + WherePredicate::EqPredicate(WhereEqPredicate { hir_id, ref lhs_ty, ref rhs_ty, .. }) => { visitor.visit_id(hir_id); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 85bc38daa3d..a9aa192bbcc 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -138,9 +138,6 @@ impl std::ops::DerefMut for State<'_> { } impl<'a> PrintState<'a> for State<'a> { - fn insert_extra_parens(&self) -> bool { - true - } fn comments(&mut self) -> &mut Option<Comments<'a>> { &mut self.comments } @@ -2210,7 +2207,6 @@ impl<'a> State<'a> { self.print_type(ty); if let Some(ref _default) = default { // FIXME(const_generics_defaults): print the `default` value here - todo!(); } } } @@ -2234,19 +2230,19 @@ impl<'a> State<'a> { } match predicate { - &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - ref bound_generic_params, - ref bounded_ty, + hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + bound_generic_params, + bounded_ty, bounds, .. }) => { self.print_formal_generic_params(bound_generic_params); self.print_type(&bounded_ty); - self.print_bounds(":", bounds); + self.print_bounds(":", *bounds); } - &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { - ref lifetime, - ref bounds, + hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { + lifetime, + bounds, .. }) => { self.print_lifetime(lifetime); @@ -2265,10 +2261,8 @@ impl<'a> State<'a> { } } } - &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { - ref lhs_ty, - ref rhs_ty, - .. + hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { + lhs_ty, rhs_ty, .. }) => { self.print_type(lhs_ty); self.s.space(); diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 9b4388c911f..f39a92b9a32 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -310,13 +310,13 @@ fn filter_nodes<'q>( sources: &Option<FxHashSet<&'q DepNode>>, targets: &Option<FxHashSet<&'q DepNode>>, ) -> FxHashSet<&'q DepNode> { - if let &Some(ref sources) = sources { - if let &Some(ref targets) = targets { + if let Some(sources) = sources { + if let Some(targets) = targets { walk_between(query, sources, targets) } else { walk_nodes(query, sources, OUTGOING) } - } else if let &Some(ref targets) = targets { + } else if let Some(targets) = targets { walk_nodes(query, targets, INCOMING) } else { query.nodes().into_iter().collect() diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs index e185ee24d17..c86122f8939 100644 --- a/compiler/rustc_incremental/src/persist/file_format.rs +++ b/compiler/rustc_incremental/src/persist/file_format.rs @@ -52,11 +52,11 @@ pub fn read_file( path: &Path, nightly_build: bool, ) -> io::Result<Option<(Vec<u8>, usize)>> { - if !path.exists() { - return Ok(None); - } - - let data = fs::read(path)?; + let data = match fs::read(path) { + Ok(data) => data, + Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(None), + Err(err) => return Err(err), + }; let mut file = io::Cursor::new(data); diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 9fdf0a56d9d..7a1976bed4b 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -922,22 +922,24 @@ fn all_except_most_recent( /// before passing it to std::fs::remove_dir_all(). This will convert the path /// into the '\\?\' format, which supports much longer paths. fn safe_remove_dir_all(p: &Path) -> io::Result<()> { - if p.exists() { - let canonicalized = p.canonicalize()?; - std_fs::remove_dir_all(canonicalized) - } else { - Ok(()) - } + let canonicalized = match std_fs::canonicalize(p) { + Ok(canonicalized) => canonicalized, + Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()), + Err(err) => return Err(err), + }; + + std_fs::remove_dir_all(canonicalized) } fn safe_remove_file(p: &Path) -> io::Result<()> { - if p.exists() { - let canonicalized = p.canonicalize()?; - match std_fs::remove_file(canonicalized) { - Err(ref err) if err.kind() == io::ErrorKind::NotFound => Ok(()), - result => result, - } - } else { - Ok(()) + let canonicalized = match std_fs::canonicalize(p) { + Ok(canonicalized) => canonicalized, + Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(()), + Err(err) => return Err(err), + }; + + match std_fs::remove_file(canonicalized) { + Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(()), + result => result, } } diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 102a77e8e79..2169f5a89e1 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -6,6 +6,7 @@ use rustc_serialize::opaque::Encoder; use rustc_serialize::Encodable as RustcEncodable; use rustc_session::Session; use std::fs; +use std::io; use std::path::PathBuf; use super::data::*; @@ -101,19 +102,18 @@ where // Note: It's important that we actually delete the old file and not just // truncate and overwrite it, since it might be a shared hard-link, the // underlying data of which we don't want to modify - if path_buf.exists() { - match fs::remove_file(&path_buf) { - Ok(()) => { - debug!("save: remove old file"); - } - Err(err) => { - sess.err(&format!( - "unable to delete old dep-graph at `{}`: {}", - path_buf.display(), - err - )); - return; - } + match fs::remove_file(&path_buf) { + Ok(()) => { + debug!("save: remove old file"); + } + Err(err) if err.kind() == io::ErrorKind::NotFound => (), + Err(err) => { + sess.err(&format!( + "unable to delete old dep-graph at `{}`: {}", + path_buf.display(), + err + )); + return; } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 14a56119f21..d1d6cb43fc7 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -915,7 +915,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty); return Some(()); } - if let &ty::Adt(def, _) = ta.kind() { + if let ty::Adt(def, _) = ta.kind() { let path_ = self.tcx.def_path_str(def.did); if path_ == other_path { self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty); diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 373f0a602c0..e097264ec8a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -43,22 +43,18 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { } fn node_ty_contains_target(&mut self, hir_id: HirId) -> Option<Ty<'tcx>> { - let ty_opt = self - .infcx + self.infcx .in_progress_typeck_results - .and_then(|typeck_results| typeck_results.borrow().node_type_opt(hir_id)); - match ty_opt { - Some(ty) => { - let ty = self.infcx.resolve_vars_if_possible(ty); - if ty.walk().any(|inner| { + .and_then(|typeck_results| typeck_results.borrow().node_type_opt(hir_id)) + .map(|ty| self.infcx.resolve_vars_if_possible(ty)) + .filter(|ty| { + ty.walk().any(|inner| { inner == self.target || match (inner.unpack(), self.target.unpack()) { (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => { + use ty::{Infer, TyVar}; match (inner_ty.kind(), target_ty.kind()) { - ( - &ty::Infer(ty::TyVar(a_vid)), - &ty::Infer(ty::TyVar(b_vid)), - ) => self + (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => self .infcx .inner .borrow_mut() @@ -69,14 +65,8 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { } _ => false, } - }) { - Some(ty) - } else { - None - } - } - None => None, - } + }) + }) } } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 461ee085922..b67704119bc 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -236,7 +236,7 @@ fn configure_and_expand_inner<'a>( pre_expansion_lint(sess, lint_store, &krate); let mut resolver = Resolver::new(sess, &krate, crate_name, metadata_loader, &resolver_arenas); - rustc_builtin_macros::register_builtin_macros(&mut resolver, sess.edition()); + rustc_builtin_macros::register_builtin_macros(&mut resolver); krate = sess.time("crate_injection", || { let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s)); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 3e94f163773..55d521a9b5f 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -7,7 +7,7 @@ use rustc_session::config::{build_configuration, build_session_options, to_crate use rustc_session::config::{rustc_optgroups, ErrorOutputType, ExternLocation, Options, Passes}; use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; use rustc_session::config::{ - Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion, + Externs, OutputType, OutputTypes, SanitizerSet, SymbolManglingVersion, WasiExecModel, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; @@ -597,6 +597,7 @@ fn test_debugging_options_tracking_hash() { tracked!(unleash_the_miri_inside_of_you, true); tracked!(use_ctors_section, Some(true)); tracked!(verify_llvm_ir, true); + tracked!(wasi_exec_model, Some(WasiExecModel::Reactor)); } #[test] diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index c0045d3f79b..297f3d19ca1 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -33,7 +33,7 @@ impl<'a> Cursor<'a> { #[cfg(not(debug_assertions))] { - '\0' + EOF_CHAR } } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index a8371274f61..374bd6d0d79 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -938,8 +938,8 @@ impl EarlyLintPass for DeprecatedAttr { if attr.ident().map(|ident| ident.name) == Some(n) { if let &AttributeGate::Gated( Stability::Deprecated(link, suggestion), - ref name, - ref reason, + name, + reason, _, ) = g { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index c82fe50af87..f4740be34cb 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -597,6 +597,9 @@ pub trait LintContext: Sized { db.help("to document an item produced by a macro, \ the macro must produce the documentation as part of its expansion"); } + BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident) => { + db.span_suggestion(span, "remove `mut` from the parameter", ident.to_string(), Applicability::MachineApplicable); + } } // Rewrap `db`, and pass control to the user. decorate(LintDiagnosticBuilder::new(db)); diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 6d61b86f32e..ebd6190dc74 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -275,10 +275,25 @@ impl NonSnakeCase { // We have a valid span in almost all cases, but we don't have one when linting a crate // name provided via the command line. if !ident.span.is_dummy() { + let sc_ident = Ident::from_str_and_span(&sc, ident.span); + let (message, suggestion) = if sc_ident.is_reserved() { + // We shouldn't suggest a reserved identifier to fix non-snake-case identifiers. + // Instead, recommend renaming the identifier entirely or, if permitted, + // escaping it to create a raw identifier. + if sc_ident.name.can_be_raw() { + ("rename the identifier or convert it to a snake case raw identifier", sc_ident.to_string()) + } else { + err.note(&format!("`{}` cannot be used as a raw identifier", sc)); + ("rename the identifier", String::new()) + } + } else { + ("convert the identifier to snake case", sc) + }; + err.span_suggestion( ident.span, - "convert the identifier to snake case", - sc, + message, + suggestion, Applicability::MaybeIncorrect, ); } else { @@ -397,7 +412,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { } fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) { - if let &PatKind::Binding(_, hid, ident, _) = &p.kind { + if let PatKind::Binding(_, hid, ident, _) = p.kind { if let hir::Node::Pat(parent_pat) = cx.tcx.hir().get(cx.tcx.hir().get_parent_node(hid)) { if let PatKind::Struct(_, field_pats, _) = &parent_pat.kind { diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 5e1f94c071c..35915dc7a97 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -862,11 +862,11 @@ impl EarlyLintPass for UnusedParens { } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { - if let &ast::TyKind::Paren(ref r) = &ty.kind { + if let ast::TyKind::Paren(r) = &ty.kind { match &r.kind { - &ast::TyKind::TraitObject(..) => {} - &ast::TyKind::ImplTrait(_, ref bounds) if bounds.len() > 1 => {} - &ast::TyKind::Array(_, ref len) => { + ast::TyKind::TraitObject(..) => {} + ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {} + ast::TyKind::Array(_, len) => { self.check_unused_delims_expr( cx, &len.value, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 1c692d4f207..f90fc7fc9ab 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -221,14 +221,10 @@ declare_lint! { /// /// ### Explanation /// - /// This lint detects code that is very likely incorrect. When possible, - /// the compiler will attempt to detect situations where code can be - /// evaluated at compile-time to generate more efficient code. While - /// evaluating such code, if it detects that the code will unconditionally - /// panic, this usually indicates that it is doing something incorrectly. - /// If this lint is allowed, then the code will not be evaluated at - /// compile-time, and instead continue to generate code to evaluate at - /// runtime, which may panic during runtime. + /// This lint detects code that is very likely incorrect because it will + /// always panic, such as division by zero and out-of-bounds array + /// accesses. Consider adjusting your code if this is a bug, or using the + /// `panic!` or `unreachable!` macro instead in case the panic is intended. pub UNCONDITIONAL_PANIC, Deny, "operation will cause a panic at runtime" diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index aec0fc253ca..2bfc6a85576 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -253,6 +253,7 @@ pub enum BuiltinLintDiagnostics { RedundantImport(Vec<(Span, bool)>, Ident), DeprecatedMacro(Option<Symbol>, Span), UnusedDocComment(Span), + PatternsInFnsWithoutBody(Span, Ident), } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index dbeb3c75504..72bd4804e98 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -203,7 +203,7 @@ fn encodable_body( #field_name, #field_idx, |__encoder| - ::rustc_serialize::Encodable::encode(#bind_ident, __encoder), + ::rustc_serialize::Encodable::<#encoder_ty>::encode(#bind_ident, __encoder), ) { ::std::result::Result::Ok(()) => (), ::std::result::Result::Err(__err) @@ -237,7 +237,7 @@ fn encodable_body( __encoder, #field_idx, |__encoder| - ::rustc_serialize::Encodable::encode(#bind_ident, __encoder), + ::rustc_serialize::Encodable::<#encoder_ty>::encode(#bind_ident, __encoder), ) { ::std::result::Result::Ok(()) => (), ::std::result::Result::Err(__err) diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 744fdc83a91..5b33678b25a 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -192,13 +192,13 @@ impl Collector<'tcx> { fn process_command_line(&mut self) { // First, check for errors let mut renames = FxHashSet::default(); - for &(ref name, ref new_name, _) in &self.tcx.sess.opts.libs { - if let &Some(ref new_name) = new_name { + for (name, new_name, _) in &self.tcx.sess.opts.libs { + if let Some(ref new_name) = new_name { let any_duplicate = self .libs .iter() .filter_map(|lib| lib.name.as_ref()) - .any(|n| n.as_str() == *name); + .any(|n| &n.as_str() == name); if new_name.is_empty() { self.tcx.sess.err(&format!( "an empty renaming target was specified for library `{}`", @@ -240,7 +240,7 @@ impl Collector<'tcx> { if kind != NativeLibKind::Unspecified { lib.kind = kind; } - if let &Some(ref new_name) = new_name { + if let Some(new_name) = new_name { lib.name = Some(Symbol::intern(new_name)); } return true; diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 43f7b2a9928..6e381fd2965 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -11,6 +11,7 @@ use rustc_data_structures::fingerprint::{Fingerprint, FingerprintDecoder}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{AtomicCell, Lock, LockGuard, Lrc, OnceCell}; +use rustc_data_structures::unhash::UnhashMap; use rustc_errors::ErrorReported; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, ProcMacroDerive}; @@ -80,7 +81,7 @@ crate struct CrateMetadata { /// For every definition in this crate, maps its `DefPathHash` to its /// `DefIndex`. See `raw_def_id_to_def_id` for more details about how /// this is used. - def_path_hash_map: OnceCell<FxHashMap<DefPathHash, DefIndex>>, + def_path_hash_map: OnceCell<UnhashMap<DefPathHash, DefIndex>>, /// Used for decoding interpret::AllocIds in a cached & thread-safe manner. alloc_decoding_state: AllocDecodingState, /// The `DepNodeIndex` of the `DepNode` representing this upstream crate. @@ -1055,19 +1056,15 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // Iterate over all children. let macros_only = self.dep_kind.lock().macros_only(); - let children = self.root.tables.children.get(self, id).unwrap_or_else(Lazy::empty); - for child_index in children.decode((self, sess)) { - if macros_only { - continue; - } - - // Get the item. - if let Some(child_kind) = self.maybe_kind(child_index) { - match child_kind { - EntryKind::MacroDef(..) => {} - _ if macros_only => continue, - _ => {} - } + if !macros_only { + let children = self.root.tables.children.get(self, id).unwrap_or_else(Lazy::empty); + + for child_index in children.decode((self, sess)) { + // Get the item. + let child_kind = match self.maybe_kind(child_index) { + Some(child_kind) => child_kind, + None => continue, + }; // Hand off the item to the callback. match child_kind { @@ -1102,8 +1099,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } let def_key = self.def_key(child_index); - let span = self.get_span(child_index, sess); if def_key.disambiguated_data.data.get_opt_name().is_some() { + let span = self.get_span(child_index, sess); let kind = self.def_kind(child_index); let ident = self.item_ident(child_index, sess); let vis = self.get_visibility(child_index); @@ -1137,9 +1134,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // within the crate. We only need this for fictive constructors, // for other constructors correct visibilities // were already encoded in metadata. - let attrs: Vec<_> = - self.get_item_attrs(def_id.index, sess).collect(); - if sess.contains_name(&attrs, sym::non_exhaustive) { + let mut attrs = self.get_item_attrs(def_id.index, sess); + if attrs.any(|item| item.has_name(sym::non_exhaustive)) { let crate_def_id = self.local_def_id(CRATE_DEF_INDEX); vis = ty::Visibility::Restricted(crate_def_id); } @@ -1345,15 +1341,14 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { return &[]; } - // Do a reverse lookup beforehand to avoid touching the crate_num - // hash map in the loop below. - let filter = match filter.map(|def_id| self.reverse_translate_def_id(def_id)) { - Some(Some(def_id)) => Some((def_id.krate.as_u32(), def_id.index)), - Some(None) => return &[], - None => None, - }; + if let Some(def_id) = filter { + // Do a reverse lookup beforehand to avoid touching the crate_num + // hash map in the loop below. + let filter = match self.reverse_translate_def_id(def_id) { + Some(def_id) => (def_id.krate.as_u32(), def_id.index), + None => return &[], + }; - if let Some(filter) = filter { if let Some(impls) = self.trait_impls.get(&filter) { tcx.arena.alloc_from_iter( impls.decode(self).map(|(idx, simplified_self_ty)| { @@ -1560,7 +1555,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // stored in this crate. let map = self.cdata.def_path_hash_map.get_or_init(|| { let end_id = self.root.tables.def_path_hashes.size() as u32; - let mut map = FxHashMap::with_capacity_and_hasher(end_id as usize, Default::default()); + let mut map = UnhashMap::with_capacity_and_hasher(end_id as usize, Default::default()); for i in 0..end_id { let def_index = DefIndex::from_u32(i); // There may be gaps in the encoded table if we're decoding a proc-macro crate diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index ac6ceafaba8..f6ae8275a8f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -694,7 +694,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { println!(" lang item bytes: {}", lang_item_bytes); println!(" diagnostic item bytes: {}", diagnostic_item_bytes); println!(" native bytes: {}", native_lib_bytes); - println!(" source_map bytes: {}", source_map_bytes); + println!(" source_map bytes: {}", source_map_bytes); println!(" impl bytes: {}", impl_bytes); println!(" exp. symbols bytes: {}", exported_symbols_bytes); println!(" def-path table bytes: {}", def_path_table_bytes); diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index d954c8ab5fb..b775846bba4 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -29,8 +29,8 @@ //! contained no `DefId` for thing that had been removed. //! //! `DepNode` definition happens in the `define_dep_nodes!()` macro. This macro -//! defines the `DepKind` enum and a corresponding `DepConstructor` enum. The -//! `DepConstructor` enum links a `DepKind` to the parameters that are needed at +//! defines the `DepKind` enum and a corresponding `dep_constructor` module. The +//! `dep_constructor` module links a `DepKind` to the parameters that are needed at //! runtime in order to construct a valid `DepNode` fingerprint. //! //! Because the macro sees what parameters a given `DepKind` requires, it can @@ -44,7 +44,7 @@ //! `DefId` it was computed from. In other cases, too much information gets //! lost during fingerprint computation. //! -//! The `DepConstructor` enum, together with `DepNode::new()`, ensures that only +//! The `dep_constructor` module, together with `DepNode::new()`, ensures that only //! valid `DepNode` instances can be constructed. For example, the API does not //! allow for constructing parameterless `DepNode`s with anything other //! than a zeroed out fingerprint. More generally speaking, it relieves the @@ -66,10 +66,104 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_INDEX}; use rustc_hir::definitions::DefPathHash; use rustc_hir::HirId; use rustc_span::symbol::Symbol; +use rustc_span::DUMMY_SP; use std::hash::Hash; pub use rustc_query_system::dep_graph::{DepContext, DepNodeParams}; +/// This struct stores metadata about each DepKind. +/// +/// Information is retrieved by indexing the `DEP_KINDS` array using the integer value +/// of the `DepKind`. Overall, this allows to implement `DepContext` using this manual +/// jump table instead of large matches. +pub struct DepKindStruct { + /// Whether the DepNode has parameters (query keys). + pub(super) has_params: bool, + + /// Anonymous queries cannot be replayed from one compiler invocation to the next. + /// When their result is needed, it is recomputed. They are useful for fine-grained + /// dependency tracking, and caching within one compiler invocation. + pub(super) is_anon: bool, + + /// Eval-always queries do not track their dependencies, and are always recomputed, even if + /// their inputs have not changed since the last compiler invocation. The result is still + /// cached within one compiler invocation. + pub(super) is_eval_always: bool, + + /// Whether the query key can be recovered from the hashed fingerprint. + /// See [DepNodeParams] trait for the behaviour of each key type. + // FIXME: Make this a simple boolean once DepNodeParams::can_reconstruct_query_key + // can be made a specialized associated const. + can_reconstruct_query_key: fn() -> bool, + + /// The red/green evaluation system will try to mark a specific DepNode in the + /// dependency graph as green by recursively trying to mark the dependencies of + /// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` + /// where we don't know if it is red or green and we therefore actually have + /// to recompute its value in order to find out. Since the only piece of + /// information that we have at that point is the `DepNode` we are trying to + /// re-evaluate, we need some way to re-run a query from just that. This is what + /// `force_from_dep_node()` implements. + /// + /// In the general case, a `DepNode` consists of a `DepKind` and an opaque + /// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint + /// is usually constructed by computing a stable hash of the query-key that the + /// `DepNode` corresponds to. Consequently, it is not in general possible to go + /// back from hash to query-key (since hash functions are not reversible). For + /// this reason `force_from_dep_node()` is expected to fail from time to time + /// because we just cannot find out, from the `DepNode` alone, what the + /// corresponding query-key is and therefore cannot re-run the query. + /// + /// The system deals with this case letting `try_mark_green` fail which forces + /// the root query to be re-evaluated. + /// + /// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. + /// Fortunately, we can use some contextual information that will allow us to + /// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we + /// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a + /// valid `DefPathHash`. Since we also always build a huge table that maps every + /// `DefPathHash` in the current codebase to the corresponding `DefId`, we have + /// everything we need to re-run the query. + /// + /// Take the `mir_promoted` query as an example. Like many other queries, it + /// just has a single parameter: the `DefId` of the item it will compute the + /// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` + /// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` + /// is actually a `DefPathHash`, and can therefore just look up the corresponding + /// `DefId` in `tcx.def_path_hash_to_def_id`. + /// + /// When you implement a new query, it will likely have a corresponding new + /// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As + /// a rule of thumb, if your query takes a `DefId` or `LocalDefId` as sole parameter, + /// then `force_from_dep_node()` should not fail for it. Otherwise, you can just + /// add it to the "We don't have enough information to reconstruct..." group in + /// the match below. + pub(super) force_from_dep_node: fn(tcx: TyCtxt<'_>, dep_node: &DepNode) -> bool, + + /// Invoke a query to put the on-disk cached value in memory. + pub(super) try_load_from_on_disk_cache: fn(TyCtxt<'_>, &DepNode), +} + +impl std::ops::Deref for DepKind { + type Target = DepKindStruct; + fn deref(&self) -> &DepKindStruct { + &DEP_KINDS[*self as usize] + } +} + +impl DepKind { + #[inline(always)] + pub fn can_reconstruct_query_key(&self) -> bool { + // Only fetch the DepKindStruct once. + let data: &DepKindStruct = &**self; + if data.is_anon { + return false; + } + + (data.can_reconstruct_query_key)() + } +} + // erase!() just makes tokens go away. It's used to specify which macro argument // is repeated (i.e., which sub-expression of the macro we are in) but don't need // to actually use any of the arguments. @@ -103,6 +197,131 @@ macro_rules! contains_eval_always_attr { ($($attr:ident $(($($attr_args:tt)*))* ),*) => ({$(is_eval_always_attr!($attr) | )* false}); } +#[allow(non_upper_case_globals)] +pub mod dep_kind { + use super::*; + use crate::ty::query::{queries, query_keys}; + use rustc_query_system::query::{force_query, QueryDescription}; + + // We use this for most things when incr. comp. is turned off. + pub const Null: DepKindStruct = DepKindStruct { + has_params: false, + is_anon: false, + is_eval_always: false, + + can_reconstruct_query_key: || true, + force_from_dep_node: |_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node), + try_load_from_on_disk_cache: |_, _| {}, + }; + + // Represents metadata from an extern crate. + pub const CrateMetadata: DepKindStruct = DepKindStruct { + has_params: true, + is_anon: false, + is_eval_always: true, + + can_reconstruct_query_key: || true, + force_from_dep_node: |_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node), + try_load_from_on_disk_cache: |_, _| {}, + }; + + pub const TraitSelect: DepKindStruct = DepKindStruct { + has_params: false, + is_anon: true, + is_eval_always: false, + + can_reconstruct_query_key: || true, + force_from_dep_node: |_, _| false, + try_load_from_on_disk_cache: |_, _| {}, + }; + + pub const CompileCodegenUnit: DepKindStruct = DepKindStruct { + has_params: true, + is_anon: false, + is_eval_always: false, + + can_reconstruct_query_key: || false, + force_from_dep_node: |_, _| false, + try_load_from_on_disk_cache: |_, _| {}, + }; + + macro_rules! define_query_dep_kinds { + ($( + [$($attrs:tt)*] + $variant:ident $(( $tuple_arg_ty:ty $(,)? ))* + ,)*) => ( + $(pub const $variant: DepKindStruct = { + const has_params: bool = $({ erase!($tuple_arg_ty); true } |)* false; + const is_anon: bool = contains_anon_attr!($($attrs)*); + const is_eval_always: bool = contains_eval_always_attr!($($attrs)*); + + #[inline(always)] + fn can_reconstruct_query_key() -> bool { + <query_keys::$variant<'_> as DepNodeParams<TyCtxt<'_>>> + ::can_reconstruct_query_key() + } + + fn recover<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<query_keys::$variant<'tcx>> { + <query_keys::$variant<'_> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node) + } + + fn force_from_dep_node(tcx: TyCtxt<'_>, dep_node: &DepNode) -> bool { + if is_anon { + return false; + } + + if !can_reconstruct_query_key() { + return false; + } + + if let Some(key) = recover(tcx, dep_node) { + force_query::<queries::$variant<'_>, _>( + tcx, + key, + DUMMY_SP, + *dep_node + ); + return true; + } + + false + } + + fn try_load_from_on_disk_cache(tcx: TyCtxt<'_>, dep_node: &DepNode) { + if is_anon { + return + } + + if !can_reconstruct_query_key() { + return + } + + debug_assert!(tcx.dep_graph + .node_color(dep_node) + .map(|c| c.is_green()) + .unwrap_or(false)); + + let key = recover(tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash)); + if queries::$variant::cache_on_disk(tcx, &key, None) { + let _ = tcx.$variant(key); + } + } + + DepKindStruct { + has_params, + is_anon, + is_eval_always, + can_reconstruct_query_key, + force_from_dep_node, + try_load_from_on_disk_cache, + } + };)* + ); + } + + rustc_dep_node_append!([define_query_dep_kinds!][]); +} + macro_rules! define_dep_nodes { (<$tcx:tt> $( @@ -110,72 +329,19 @@ macro_rules! define_dep_nodes { $variant:ident $(( $tuple_arg_ty:ty $(,)? ))* ,)* ) => ( - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] + static DEP_KINDS: &[DepKindStruct] = &[ $(dep_kind::$variant),* ]; + + /// This enum serves as an index into the `DEP_KINDS` array. + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] #[allow(non_camel_case_types)] pub enum DepKind { $($variant),* } - impl DepKind { - #[allow(unreachable_code)] - pub fn can_reconstruct_query_key<$tcx>(&self) -> bool { - match *self { - $( - DepKind :: $variant => { - if contains_anon_attr!($($attrs)*) { - return false; - } - - // tuple args - $({ - return <$tuple_arg_ty as DepNodeParams<TyCtxt<'_>>> - ::can_reconstruct_query_key(); - })* - - true - } - )* - } - } - - pub fn is_anon(&self) -> bool { - match *self { - $( - DepKind :: $variant => { contains_anon_attr!($($attrs)*) } - )* - } - } - - pub fn is_eval_always(&self) -> bool { - match *self { - $( - DepKind :: $variant => { contains_eval_always_attr!($($attrs)*) } - )* - } - } - - #[allow(unreachable_code)] - pub fn has_params(&self) -> bool { - match *self { - $( - DepKind :: $variant => { - // tuple args - $({ - erase!($tuple_arg_ty); - return true; - })* - - false - } - )* - } - } - } - - pub struct DepConstructor; - #[allow(non_camel_case_types)] - impl DepConstructor { + pub mod dep_constructor { + use super::*; + $( #[inline(always)] #[allow(unreachable_code, non_snake_case)] @@ -191,101 +357,10 @@ macro_rules! define_dep_nodes { )* } - pub type DepNode = rustc_query_system::dep_graph::DepNode<DepKind>; - - // We keep a lot of `DepNode`s in memory during compilation. It's not - // required that their size stay the same, but we don't want to change - // it inadvertently. This assert just ensures we're aware of any change. - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - static_assert_size!(DepNode, 17); - - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - static_assert_size!(DepNode, 24); - - pub trait DepNodeExt: Sized { - /// Construct a DepNode from the given DepKind and DefPathHash. This - /// method will assert that the given DepKind actually requires a - /// single DefId/DefPathHash parameter. - fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> Self; - - /// Extracts the DefId corresponding to this DepNode. This will work - /// if two conditions are met: - /// - /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and - /// 2. the item that the DefPath refers to exists in the current tcx. - /// - /// Condition (1) is determined by the DepKind variant of the - /// DepNode. Condition (2) might not be fulfilled if a DepNode - /// refers to something from the previous compilation session that - /// has been removed. - fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option<DefId>; - - /// Used in testing - fn from_label_string(label: &str, def_path_hash: DefPathHash) - -> Result<Self, ()>; - - /// Used in testing - fn has_label_string(label: &str) -> bool; - } - - impl DepNodeExt for DepNode { - /// Construct a DepNode from the given DepKind and DefPathHash. This - /// method will assert that the given DepKind actually requires a - /// single DefId/DefPathHash parameter. - fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> DepNode { - debug_assert!(kind.can_reconstruct_query_key() && kind.has_params()); - DepNode { - kind, - hash: def_path_hash.0.into(), - } - } - - /// Extracts the DefId corresponding to this DepNode. This will work - /// if two conditions are met: - /// - /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and - /// 2. the item that the DefPath refers to exists in the current tcx. - /// - /// Condition (1) is determined by the DepKind variant of the - /// DepNode. Condition (2) might not be fulfilled if a DepNode - /// refers to something from the previous compilation session that - /// has been removed. - fn extract_def_id(&self, tcx: TyCtxt<'tcx>) -> Option<DefId> { - if self.kind.can_reconstruct_query_key() { - tcx.queries.on_disk_cache.as_ref()?.def_path_hash_to_def_id(tcx, DefPathHash(self.hash.into())) - } else { - None - } - } - - /// Used in testing - fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result<DepNode, ()> { - let kind = match label { - $( - stringify!($variant) => DepKind::$variant, - )* - _ => return Err(()), - }; - - if !kind.can_reconstruct_query_key() { - return Err(()); - } - - if kind.has_params() { - Ok(DepNode::from_def_path_hash(def_path_hash, kind)) - } else { - Ok(DepNode::new_no_params(kind)) - } - } - - /// Used in testing - fn has_label_string(label: &str) -> bool { - match label { - $( - stringify!($variant) => true, - )* - _ => false, - } + fn dep_kind_from_label_string(label: &str) -> Result<DepKind, ()> { + match label { + $(stringify!($variant) => Ok(DepKind::$variant),)* + _ => Err(()), } } @@ -312,8 +387,110 @@ rustc_dep_node_append!([define_dep_nodes!][ <'tcx> [] CompileCodegenUnit(Symbol), ]); +pub type DepNode = rustc_query_system::dep_graph::DepNode<DepKind>; + +// We keep a lot of `DepNode`s in memory during compilation. It's not +// required that their size stay the same, but we don't want to change +// it inadvertently. This assert just ensures we're aware of any change. +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +static_assert_size!(DepNode, 17); + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +static_assert_size!(DepNode, 24); + +pub trait DepNodeExt: Sized { + /// Construct a DepNode from the given DepKind and DefPathHash. This + /// method will assert that the given DepKind actually requires a + /// single DefId/DefPathHash parameter. + fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> Self; + + /// Extracts the DefId corresponding to this DepNode. This will work + /// if two conditions are met: + /// + /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and + /// 2. the item that the DefPath refers to exists in the current tcx. + /// + /// Condition (1) is determined by the DepKind variant of the + /// DepNode. Condition (2) might not be fulfilled if a DepNode + /// refers to something from the previous compilation session that + /// has been removed. + fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option<DefId>; + + /// Used in testing + fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result<Self, ()>; + + /// Used in testing + fn has_label_string(label: &str) -> bool; +} + +impl DepNodeExt for DepNode { + /// Construct a DepNode from the given DepKind and DefPathHash. This + /// method will assert that the given DepKind actually requires a + /// single DefId/DefPathHash parameter. + fn from_def_path_hash(def_path_hash: DefPathHash, kind: DepKind) -> DepNode { + debug_assert!(kind.can_reconstruct_query_key() && kind.has_params); + DepNode { kind, hash: def_path_hash.0.into() } + } + + /// Extracts the DefId corresponding to this DepNode. This will work + /// if two conditions are met: + /// + /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and + /// 2. the item that the DefPath refers to exists in the current tcx. + /// + /// Condition (1) is determined by the DepKind variant of the + /// DepNode. Condition (2) might not be fulfilled if a DepNode + /// refers to something from the previous compilation session that + /// has been removed. + fn extract_def_id(&self, tcx: TyCtxt<'tcx>) -> Option<DefId> { + if self.kind.can_reconstruct_query_key() { + tcx.queries + .on_disk_cache + .as_ref()? + .def_path_hash_to_def_id(tcx, DefPathHash(self.hash.into())) + } else { + None + } + } + + /// Used in testing + fn from_label_string(label: &str, def_path_hash: DefPathHash) -> Result<DepNode, ()> { + let kind = dep_kind_from_label_string(label)?; + + if !kind.can_reconstruct_query_key() { + return Err(()); + } + + if kind.has_params { + Ok(DepNode::from_def_path_hash(def_path_hash, kind)) + } else { + Ok(DepNode::new_no_params(kind)) + } + } + + /// Used in testing + fn has_label_string(label: &str) -> bool { + dep_kind_from_label_string(label).is_ok() + } +} + +impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for () { + #[inline(always)] + fn can_reconstruct_query_key() -> bool { + true + } + + fn to_fingerprint(&self, _: TyCtxt<'tcx>) -> Fingerprint { + Fingerprint::ZERO + } + + fn recover(_: TyCtxt<'tcx>, _: &DepNode) -> Option<Self> { + Some(()) + } +} + impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for DefId { - #[inline] + #[inline(always)] fn can_reconstruct_query_key() -> bool { true } @@ -342,7 +519,7 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for DefId { } impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for LocalDefId { - #[inline] + #[inline(always)] fn can_reconstruct_query_key() -> bool { true } @@ -361,7 +538,7 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for LocalDefId { } impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for CrateNum { - #[inline] + #[inline(always)] fn can_reconstruct_query_key() -> bool { true } @@ -381,7 +558,7 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for CrateNum { } impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for (DefId, DefId) { - #[inline] + #[inline(always)] fn can_reconstruct_query_key() -> bool { false } @@ -406,7 +583,7 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for (DefId, DefId) { } impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for HirId { - #[inline] + #[inline(always)] fn can_reconstruct_query_key() -> bool { false } diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 728bfef9f46..22e9cc1cd3e 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -1,5 +1,4 @@ use crate::ich::StableHashingContext; -use crate::ty::query::try_load_from_on_disk_cache; use crate::ty::{self, TyCtxt}; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::Lock; @@ -9,13 +8,12 @@ use rustc_hir::def_id::LocalDefId; mod dep_node; -pub(crate) use rustc_query_system::dep_graph::DepNodeParams; pub use rustc_query_system::dep_graph::{ debug, hash_result, DepContext, DepNodeColor, DepNodeIndex, SerializedDepNodeIndex, WorkProduct, WorkProductId, }; -pub use dep_node::{label_strs, DepConstructor, DepKind, DepNode, DepNodeExt}; +pub use dep_node::{dep_constructor, label_strs, DepKind, DepNode, DepNodeExt}; pub type DepGraph = rustc_query_system::dep_graph::DepGraph<DepKind>; pub type TaskDeps = rustc_query_system::dep_graph::TaskDeps<DepKind>; @@ -26,18 +24,25 @@ pub type SerializedDepGraph = rustc_query_system::dep_graph::SerializedDepGraph< impl rustc_query_system::dep_graph::DepKind for DepKind { const NULL: Self = DepKind::Null; + #[inline(always)] + fn can_reconstruct_query_key(&self) -> bool { + DepKind::can_reconstruct_query_key(self) + } + + #[inline(always)] fn is_eval_always(&self) -> bool { - DepKind::is_eval_always(self) + self.is_eval_always } + #[inline(always)] fn has_params(&self) -> bool { - DepKind::has_params(self) + self.has_params } fn debug_node(node: &DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", node.kind)?; - if !node.kind.has_params() && !node.kind.is_anon() { + if !node.kind.has_params && !node.kind.is_anon { return Ok(()); } @@ -81,10 +86,6 @@ impl rustc_query_system::dep_graph::DepKind for DepKind { op(icx.task_deps) }) } - - fn can_reconstruct_query_key(&self) -> bool { - DepKind::can_reconstruct_query_key(self) - } } impl<'tcx> DepContext for TyCtxt<'tcx> { @@ -153,7 +154,26 @@ impl<'tcx> DepContext for TyCtxt<'tcx> { } debug!("try_force_from_dep_node({:?}) --- trying to force", dep_node); - ty::query::force_from_dep_node(*self, dep_node) + + // We must avoid ever having to call `force_from_dep_node()` for a + // `DepNode::codegen_unit`: + // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we + // would always end up having to evaluate the first caller of the + // `codegen_unit` query that *is* reconstructible. This might very well be + // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just + // to re-trigger calling the `codegen_unit` query with the right key. At + // that point we would already have re-done all the work we are trying to + // avoid doing in the first place. + // The solution is simple: Just explicitly call the `codegen_unit` query for + // each CGU, right after partitioning. This way `try_mark_green` will always + // hit the cache instead of having to go through `force_from_dep_node`. + // This assertion makes sure, we actually keep applying the solution above. + debug_assert!( + dep_node.kind != DepKind::codegen_unit, + "calling force_from_dep_node() on DepKind::codegen_unit" + ); + + (dep_node.kind.force_from_dep_node)(*self, dep_node) } fn has_errors_or_delayed_span_bugs(&self) -> bool { @@ -166,7 +186,7 @@ impl<'tcx> DepContext for TyCtxt<'tcx> { // Interactions with on_disk_cache fn try_load_from_on_disk_cache(&self, dep_node: &DepNode) { - try_load_from_on_disk_cache(*self, dep_node) + (dep_node.kind.try_load_from_on_disk_cache)(*self, dep_node) } fn load_diagnostics(&self, prev_dep_node_index: SerializedDepNodeIndex) -> Vec<Diagnostic> { diff --git a/compiler/rustc_middle/src/hir/map/collector.rs b/compiler/rustc_middle/src/hir/map/collector.rs index 82cfca4f171..872fcb0f581 100644 --- a/compiler/rustc_middle/src/hir/map/collector.rs +++ b/compiler/rustc_middle/src/hir/map/collector.rs @@ -529,13 +529,22 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { } fn visit_macro_def(&mut self, macro_def: &'hir MacroDef<'hir>) { - self.with_dep_node_owner(macro_def.hir_id.owner, macro_def, |this, hash| { - this.insert_with_hash( - macro_def.span, - macro_def.hir_id, - Node::MacroDef(macro_def), - hash, - ); + // Exported macros are visited directly from the crate root, + // so they do not have `parent_node` set. + // Find the correct enclosing module from their DefKey. + let def_key = self.definitions.def_key(macro_def.hir_id.owner); + let parent = def_key.parent.map_or(hir::CRATE_HIR_ID, |local_def_index| { + self.definitions.local_def_id_to_hir_id(LocalDefId { local_def_index }) + }); + self.with_parent(parent, |this| { + this.with_dep_node_owner(macro_def.hir_id.owner, macro_def, |this, hash| { + this.insert_with_hash( + macro_def.span, + macro_def.hir_id, + Node::MacroDef(macro_def), + hash, + ); + }) }); } diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 09d5b102103..eb6aded9cb3 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -31,7 +31,7 @@ pub struct Entry<'hir> { impl<'hir> Entry<'hir> { fn parent_node(self) -> Option<HirId> { match self.node { - Node::Crate(_) | Node::MacroDef(_) => None, + Node::Crate(_) => None, _ => Some(self.parent), } } @@ -710,15 +710,10 @@ impl<'hir> Map<'hir> { let mut scope = id; loop { scope = self.get_enclosing_scope(scope).unwrap_or(CRATE_HIR_ID); - if scope == CRATE_HIR_ID { - return CRATE_HIR_ID; - } - match self.get(scope) { - Node::Block(_) => {} - _ => break, + if scope == CRATE_HIR_ID || !matches!(self.get(scope), Node::Block(_)) { + return scope; } } - scope } pub fn get_parent_did(&self, id: HirId) -> LocalDefId { diff --git a/compiler/rustc_middle/src/ich/hcx.rs b/compiler/rustc_middle/src/ich/hcx.rs index 084fe4cfa16..addcb7a14e3 100644 --- a/compiler/rustc_middle/src/ich/hcx.rs +++ b/compiler/rustc_middle/src/ich/hcx.rs @@ -12,7 +12,7 @@ use rustc_hir::definitions::{DefPathHash, Definitions}; use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, CachingSourceMapView, SourceFile}; +use rustc_span::{BytePos, CachingSourceMapView, SourceFile, SpanData}; use rustc_span::def_id::{CrateNum, CRATE_DEF_INDEX}; use smallvec::SmallVec; @@ -248,6 +248,13 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { ) -> Option<(Lrc<SourceFile>, usize, BytePos)> { self.source_map().byte_pos_to_line_and_col(byte) } + + fn span_data_to_lines_and_cols( + &mut self, + span: &SpanData, + ) -> Option<(Lrc<SourceFile>, usize, BytePos, usize, BytePos)> { + self.source_map().span_data_to_lines_and_cols(span) + } } pub fn hash_stable_trait_impls<'a>( diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index a69555fd1a8..fab2f2c97e4 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1745,18 +1745,14 @@ impl<'tcx> Place<'tcx> { /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or /// a single deref of a local. - // - // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? + #[inline(always)] pub fn local_or_deref_local(&self) -> Option<Local> { - match self.as_ref() { - PlaceRef { local, projection: [] } - | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), - _ => None, - } + self.as_ref().local_or_deref_local() } /// If this place represents a local variable like `_X` with no /// projections, return `Some(_X)`. + #[inline(always)] pub fn as_local(&self) -> Option<Local> { self.as_ref().as_local() } @@ -1770,6 +1766,7 @@ impl<'tcx> Place<'tcx> { /// As a concrete example, given the place a.b.c, this would yield: /// - (a, .b) /// - (a.b, .c) + /// /// Given a place without projections, the iterator is empty. pub fn iter_projections( self, @@ -1790,8 +1787,6 @@ impl From<Local> for Place<'_> { impl<'tcx> PlaceRef<'tcx> { /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or /// a single deref of a local. - // - // FIXME: can we safely swap the semantics of `fn base_local` below in here instead? pub fn local_or_deref_local(&self) -> Option<Local> { match *self { PlaceRef { local, projection: [] } @@ -1808,6 +1803,14 @@ impl<'tcx> PlaceRef<'tcx> { _ => None, } } + + pub fn last_projection(&self) -> Option<(PlaceRef<'tcx>, PlaceElem<'tcx>)> { + if let &[ref proj_base @ .., elem] = self.projection { + Some((PlaceRef { local: self.local, projection: proj_base }, elem)) + } else { + None + } + } } impl Debug for Place<'_> { diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 1e70f760504..698c2521596 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -1,7 +1,6 @@ -use crate::dep_graph::{DepConstructor, DepNode, WorkProduct, WorkProductId}; +use crate::dep_graph::{dep_constructor, DepNode, WorkProduct, WorkProductId}; use crate::ich::{NodeIdHashingMode, StableHashingContext}; use crate::ty::{subst::InternalSubsts, Instance, InstanceDef, SymbolName, TyCtxt}; -use rustc_attr::InlineAttr; use rustc_data_structures::base_n; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; @@ -79,14 +78,6 @@ impl<'tcx> MonoItem<'tcx> { } pub fn instantiation_mode(&self, tcx: TyCtxt<'tcx>) -> InstantiationMode { - let generate_cgu_internal_copies = tcx - .sess - .opts - .debugging_opts - .inline_in_all_cgus - .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No) - && !tcx.sess.link_dead_code(); - match *self { MonoItem::Fn(ref instance) => { let entry_def_id = tcx.entry_fn(LOCAL_CRATE).map(|(id, _)| id); @@ -99,21 +90,26 @@ impl<'tcx> MonoItem<'tcx> { return InstantiationMode::GloballyShared { may_conflict: false }; } + let generate_cgu_internal_copies = tcx + .sess + .opts + .debugging_opts + .inline_in_all_cgus + .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No) + && !tcx.sess.link_dead_code(); + // At this point we don't have explicit linkage and we're an - // inlined function. If we're inlining into all CGUs then we'll - // be creating a local copy per CGU. + // inlined function. If we should generate local copies for each CGU, + // then return `LocalCopy`, otherwise we'll just generate one copy + // and share it with all CGUs in this crate. if generate_cgu_internal_copies { - return InstantiationMode::LocalCopy; - } - - // Finally, if this is `#[inline(always)]` we're sure to respect - // that with an inline copy per CGU, but otherwise we'll be - // creating one copy of this `#[inline]` function which may - // conflict with upstream crates as it could be an exported - // symbol. - match tcx.codegen_fn_attrs(instance.def_id()).inline { - InlineAttr::Always => InstantiationMode::LocalCopy, - _ => InstantiationMode::GloballyShared { may_conflict: true }, + InstantiationMode::LocalCopy + } else { + // Finally, if we've reached this point, then we should optimize for + // compilation speed. In that regard, we will ignore any `#[inline]` + // annotations on the function and simply codegen it as usual. This could + // conflict with upstream crates as it could be an exported symbol. + InstantiationMode::GloballyShared { may_conflict: true } } } MonoItem::Static(..) | MonoItem::GlobalAsm(..) => { @@ -362,7 +358,7 @@ impl<'tcx> CodegenUnit<'tcx> { } pub fn codegen_dep_node(&self, tcx: TyCtxt<'tcx>) -> DepNode { - DepConstructor::CompileCodegenUnit(tcx, self.name()) + dep_constructor::CompileCodegenUnit(tcx, self.name()) } } diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 65703d04c70..3adcdbe591f 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -245,8 +245,8 @@ pub fn suggest_constraining_type_param( } } - match ¶m_spans[..] { - &[¶m_span] => suggest_restrict(param_span.shrink_to_hi()), + match param_spans[..] { + [¶m_span] => suggest_restrict(param_span.shrink_to_hi()), _ => { err.span_suggestion_verbose( generics.where_clause.tail_span_for_suggestion(), diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 1fe1400fabe..863423b91a6 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -801,6 +801,15 @@ impl GenericParamDefKind { GenericParamDefKind::Const => "constant", } } + pub fn to_ord(&self, tcx: TyCtxt<'_>) -> ast::ParamKindOrd { + match self { + GenericParamDefKind::Lifetime => ast::ParamKindOrd::Lifetime, + GenericParamDefKind::Type { .. } => ast::ParamKindOrd::Type, + GenericParamDefKind::Const => { + ast::ParamKindOrd::Const { unordered: tcx.features().const_generics } + } + } + } } #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] @@ -2888,19 +2897,11 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn opt_associated_item(self, def_id: DefId) -> Option<&'tcx AssocItem> { - let is_associated_item = if let Some(def_id) = def_id.as_local() { - matches!( - self.hir().get(self.hir().local_def_id_to_hir_id(def_id)), - Node::TraitItem(_) | Node::ImplItem(_) - ) + if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = self.def_kind(def_id) { + Some(self.associated_item(def_id)) } else { - matches!( - self.def_kind(def_id), - DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy - ) - }; - - is_associated_item.then(|| self.associated_item(def_id)) + None + } } pub fn field_index(self, hir_id: hir::HirId, typeck_results: &TypeckResults<'_>) -> usize { diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs index b269dd09b72..acfa58e511e 100644 --- a/compiler/rustc_middle/src/ty/query/mod.rs +++ b/compiler/rustc_middle/src/ty/query/mod.rs @@ -1,4 +1,4 @@ -use crate::dep_graph::{self, DepKind, DepNode, DepNodeParams}; +use crate::dep_graph; use crate::hir::exports::Export; use crate::hir::map; use crate::infer::canonical::{self, Canonical}; @@ -103,138 +103,6 @@ pub use self::profiling_support::{IntoSelfProfilingString, QueryKeyStringBuilder rustc_query_append! { [define_queries!][<'tcx>] } -/// The red/green evaluation system will try to mark a specific DepNode in the -/// dependency graph as green by recursively trying to mark the dependencies of -/// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode` -/// where we don't know if it is red or green and we therefore actually have -/// to recompute its value in order to find out. Since the only piece of -/// information that we have at that point is the `DepNode` we are trying to -/// re-evaluate, we need some way to re-run a query from just that. This is what -/// `force_from_dep_node()` implements. -/// -/// In the general case, a `DepNode` consists of a `DepKind` and an opaque -/// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint -/// is usually constructed by computing a stable hash of the query-key that the -/// `DepNode` corresponds to. Consequently, it is not in general possible to go -/// back from hash to query-key (since hash functions are not reversible). For -/// this reason `force_from_dep_node()` is expected to fail from time to time -/// because we just cannot find out, from the `DepNode` alone, what the -/// corresponding query-key is and therefore cannot re-run the query. -/// -/// The system deals with this case letting `try_mark_green` fail which forces -/// the root query to be re-evaluated. -/// -/// Now, if `force_from_dep_node()` would always fail, it would be pretty useless. -/// Fortunately, we can use some contextual information that will allow us to -/// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we -/// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a -/// valid `DefPathHash`. Since we also always build a huge table that maps every -/// `DefPathHash` in the current codebase to the corresponding `DefId`, we have -/// everything we need to re-run the query. -/// -/// Take the `mir_promoted` query as an example. Like many other queries, it -/// just has a single parameter: the `DefId` of the item it will compute the -/// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode` -/// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode` -/// is actually a `DefPathHash`, and can therefore just look up the corresponding -/// `DefId` in `tcx.def_path_hash_to_def_id`. -/// -/// When you implement a new query, it will likely have a corresponding new -/// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As -/// a rule of thumb, if your query takes a `DefId` or `LocalDefId` as sole parameter, -/// then `force_from_dep_node()` should not fail for it. Otherwise, you can just -/// add it to the "We don't have enough information to reconstruct..." group in -/// the match below. -pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool { - // We must avoid ever having to call `force_from_dep_node()` for a - // `DepNode::codegen_unit`: - // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we - // would always end up having to evaluate the first caller of the - // `codegen_unit` query that *is* reconstructible. This might very well be - // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just - // to re-trigger calling the `codegen_unit` query with the right key. At - // that point we would already have re-done all the work we are trying to - // avoid doing in the first place. - // The solution is simple: Just explicitly call the `codegen_unit` query for - // each CGU, right after partitioning. This way `try_mark_green` will always - // hit the cache instead of having to go through `force_from_dep_node`. - // This assertion makes sure, we actually keep applying the solution above. - debug_assert!( - dep_node.kind != DepKind::codegen_unit, - "calling force_from_dep_node() on DepKind::codegen_unit" - ); - - if !dep_node.kind.can_reconstruct_query_key() { - return false; - } - - macro_rules! force_from_dep_node { - ($($(#[$attr:meta])* [$($modifiers:tt)*] $name:ident($K:ty),)*) => { - match dep_node.kind { - // These are inputs that are expected to be pre-allocated and that - // should therefore always be red or green already. - DepKind::CrateMetadata | - - // These are anonymous nodes. - DepKind::TraitSelect | - - // We don't have enough information to reconstruct the query key of - // these. - DepKind::CompileCodegenUnit | - - // Forcing this makes no sense. - DepKind::Null => { - bug!("force_from_dep_node: encountered {:?}", dep_node) - } - - $(DepKind::$name => { - debug_assert!(<$K as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key()); - - if let Some(key) = <$K as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node) { - force_query::<queries::$name<'_>, _>( - tcx, - key, - DUMMY_SP, - *dep_node - ); - return true; - } - })* - } - } - } - - rustc_dep_node_append! { [force_from_dep_node!][] } - - false -} - -pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) { - macro_rules! try_load_from_on_disk_cache { - ($($name:ident,)*) => { - match dep_node.kind { - $(DepKind::$name => { - if <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() { - debug_assert!(tcx.dep_graph - .node_color(dep_node) - .map(|c| c.is_green()) - .unwrap_or(false)); - - let key = <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash)); - if queries::$name::cache_on_disk(tcx, &key, None) { - let _ = tcx.$name(key); - } - } - })* - - _ => (), - } - } - } - - rustc_cached_queries!(try_load_from_on_disk_cache!); -} - mod sealed { use super::{DefId, LocalDefId}; diff --git a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs index 8a1165bbd64..eb4f1b958be 100644 --- a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs @@ -8,6 +8,7 @@ use rustc_data_structures::fingerprint::{Fingerprint, FingerprintDecoder, Finger use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, OnceCell}; use rustc_data_structures::thin_vec::ThinVec; +use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Diagnostic; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, LOCAL_CRATE}; use rustc_hir::definitions::DefPathHash; @@ -87,7 +88,7 @@ pub struct OnDiskCache<'sess> { // compilation session. This is used as an initial 'guess' when // we try to map a `DefPathHash` to its `DefId` in the current compilation // session. - foreign_def_path_hashes: FxHashMap<DefPathHash, RawDefId>, + foreign_def_path_hashes: UnhashMap<DefPathHash, RawDefId>, // The *next* compilation sessison's `foreign_def_path_hashes` - at // the end of our current compilation session, this will get written @@ -95,19 +96,19 @@ pub struct OnDiskCache<'sess> { // will become `foreign_def_path_hashes` of the next compilation session. // This stores any `DefPathHash` that we may need to map to a `DefId` // during the next compilation session. - latest_foreign_def_path_hashes: Lock<FxHashMap<DefPathHash, RawDefId>>, + latest_foreign_def_path_hashes: Lock<UnhashMap<DefPathHash, RawDefId>>, // Maps `DefPathHashes` to their corresponding `LocalDefId`s for all // local items in the current compilation session. This is only populated // when we are in incremental mode and have loaded a pre-existing cache // from disk, since this map is only used when deserializing a `DefPathHash` // from the incremental cache. - local_def_path_hash_to_def_id: FxHashMap<DefPathHash, LocalDefId>, + local_def_path_hash_to_def_id: UnhashMap<DefPathHash, LocalDefId>, // Caches all lookups of `DefPathHashes`, both for local and foreign // definitions. A definition from the previous compilation session // may no longer exist in the current compilation session, so // we use `Option<DefId>` so that we can cache a lookup failure. - def_path_hash_to_def_id_cache: Lock<FxHashMap<DefPathHash, Option<DefId>>>, + def_path_hash_to_def_id_cache: Lock<UnhashMap<DefPathHash, Option<DefId>>>, } // This type is used only for serialization and deserialization. @@ -123,7 +124,7 @@ struct Footer { syntax_contexts: FxHashMap<u32, AbsoluteBytePos>, // See `OnDiskCache.expn_data` expn_data: FxHashMap<u32, AbsoluteBytePos>, - foreign_def_path_hashes: FxHashMap<DefPathHash, RawDefId>, + foreign_def_path_hashes: UnhashMap<DefPathHash, RawDefId>, } type EncodedQueryResultIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>; @@ -160,8 +161,8 @@ crate struct RawDefId { pub index: u32, } -fn make_local_def_path_hash_map(definitions: &Definitions) -> FxHashMap<DefPathHash, LocalDefId> { - FxHashMap::from_iter( +fn make_local_def_path_hash_map(definitions: &Definitions) -> UnhashMap<DefPathHash, LocalDefId> { + UnhashMap::from_iter( definitions .def_path_table() .all_def_path_hashes_and_def_ids(LOCAL_CRATE) @@ -807,6 +808,15 @@ impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> { crate::implement_ty_decoder!(CacheDecoder<'a, 'tcx>); +// This ensures that the `Decodable<opaque::Decoder>::decode` specialization for `Vec<u8>` is used +// when a `CacheDecoder` is passed to `Decodable::decode`. Unfortunately, we have to manually opt +// into specializations this way, given how `CacheDecoder` and the decoding traits currently work. +impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for Vec<u8> { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Result<Self, String> { + Decodable::decode(&mut d.opaque) + } +} + impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for SyntaxContext { fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Result<Self, String> { let syntax_contexts = decoder.syntax_contexts; @@ -964,7 +974,7 @@ struct CacheEncoder<'a, 'tcx, E: OpaqueEncoder> { source_map: CachingSourceMapView<'tcx>, file_to_file_index: FxHashMap<*const SourceFile, SourceFileIndex>, hygiene_context: &'a HygieneEncodeContext, - latest_foreign_def_path_hashes: FxHashMap<DefPathHash, RawDefId>, + latest_foreign_def_path_hashes: UnhashMap<DefPathHash, RawDefId>, } impl<'a, 'tcx, E> CacheEncoder<'a, 'tcx, E> @@ -1149,6 +1159,16 @@ where } } +// This ensures that the `Encodable<opaque::Encoder>::encode` specialization for byte slices +// is used when a `CacheEncoder` having an `opaque::Encoder` is passed to `Encodable::encode`. +// Unfortunately, we have to manually opt into specializations this way, given how `CacheEncoder` +// and the encoding traits currently work. +impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx, opaque::Encoder>> for [u8] { + fn encode(&self, e: &mut CacheEncoder<'a, 'tcx, opaque::Encoder>) -> opaque::EncodeResult { + self.encode(e.encoder) + } +} + // An integer that will always encode to 8 bytes. struct IntEncodedWithFixedSize(u64); 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 1474c7abfad..db02ee67910 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -151,95 +151,88 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let move_msg = if move_spans.for_closure() { " into closure" } else { "" }; + let loop_message = if location == move_out.source || move_site.traversed_back_edge { + ", in previous iteration of loop" + } else { + "" + }; + if location == move_out.source { - err.span_label( - span, - format!( - "value {}moved{} here, in previous iteration of loop", - partially_str, move_msg - ), - ); is_loop_move = true; - } else if move_site.traversed_back_edge { - err.span_label( - move_span, - format!( - "value {}moved{} here, in previous iteration of loop", - partially_str, move_msg - ), - ); - } else { - if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = - move_spans - { - let place_name = self - .describe_place(moved_place.as_ref()) - .map(|n| format!("`{}`", n)) - .unwrap_or_else(|| "value".to_owned()); - match kind { - FnSelfUseKind::FnOnceCall => { + } + + if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans { + let place_name = self + .describe_place(moved_place.as_ref()) + .map(|n| format!("`{}`", n)) + .unwrap_or_else(|| "value".to_owned()); + match kind { + FnSelfUseKind::FnOnceCall => { + err.span_label( + fn_call_span, + &format!( + "{} {}moved due to this call{}", + place_name, partially_str, loop_message + ), + ); + err.span_note( + var_span, + "this value implements `FnOnce`, which causes it to be moved when called", + ); + } + FnSelfUseKind::Operator { self_arg } => { + err.span_label( + fn_call_span, + &format!( + "{} {}moved due to usage in operator{}", + place_name, partially_str, loop_message + ), + ); + if self.fn_self_span_reported.insert(fn_span) { + err.span_note( + self_arg.span, + "calling this operator moves the left-hand side", + ); + } + } + FnSelfUseKind::Normal { self_arg, implicit_into_iter } => { + if implicit_into_iter { err.span_label( fn_call_span, &format!( - "{} {}moved due to this call", - place_name, partially_str + "{} {}moved due to this implicit call to `.into_iter()`{}", + place_name, partially_str, loop_message ), ); - err.span_note( - var_span, - "this value implements `FnOnce`, which causes it to be moved when called", - ); - } - FnSelfUseKind::Operator { self_arg } => { + } else { err.span_label( fn_call_span, &format!( - "{} {}moved due to usage in operator", - place_name, partially_str + "{} {}moved due to this method call{}", + place_name, partially_str, loop_message ), ); - if self.fn_self_span_reported.insert(fn_span) { - err.span_note( - self_arg.span, - "calling this operator moves the left-hand side", - ); - } } - FnSelfUseKind::Normal { self_arg, implicit_into_iter } => { - if implicit_into_iter { - err.span_label( - fn_call_span, - &format!( - "{} {}moved due to this implicit call to `.into_iter()`", - place_name, partially_str - ), - ); - } else { - err.span_label( - fn_call_span, - &format!( - "{} {}moved due to this method call", - place_name, partially_str - ), - ); - } - // Avoid pointing to the same function in multiple different - // error messages - if self.fn_self_span_reported.insert(self_arg.span) { - err.span_note( - self_arg.span, - &format!("this function consumes the receiver `self` by taking ownership of it, which moves {}", place_name) - ); - } + // Avoid pointing to the same function in multiple different + // error messages + if self.fn_self_span_reported.insert(self_arg.span) { + err.span_note( + self_arg.span, + &format!("this function takes ownership of the receiver `self`, which moves {}", place_name) + ); } - // Deref::deref takes &self, which cannot cause a move - FnSelfUseKind::DerefCoercion { .. } => unreachable!(), } - } else { - err.span_label( - move_span, - format!("value {}moved{} here", partially_str, move_msg), - ); + // Deref::deref takes &self, which cannot cause a move + FnSelfUseKind::DerefCoercion { .. } => unreachable!(), + } + } else { + err.span_label( + move_span, + format!("value {}moved{} here{}", partially_str, move_msg, loop_message), + ); + // If the move error occurs due to a loop, don't show + // another message for the same span + if loop_message.is_empty() { move_spans.var_span_label( &mut err, format!( @@ -250,6 +243,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } } + if let UseSpans::PatUse(span) = move_spans { err.span_suggestion_verbose( span.shrink_to_lo(), @@ -293,9 +287,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } - let ty = - Place::ty_from(used_place.local, used_place.projection, self.body, self.infcx.tcx) - .ty; + let ty = PlaceRef::ty(&used_place, self.body, self.infcx.tcx).ty; let needs_note = match ty.kind() { ty::Closure(id, _) => { let tables = self.infcx.tcx.typeck(id.expect_local()); @@ -732,8 +724,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> (String, String, String, String) { // Define a small closure that we can use to check if the type of a place // is a union. - let union_ty = |place_base, place_projection| { - let ty = Place::ty_from(place_base, place_projection, self.body, self.infcx.tcx).ty; + let union_ty = |place_base| { + let ty = PlaceRef::ty(&place_base, self.body, self.infcx.tcx).ty; ty.ty_adt_def().filter(|adt| adt.is_union()).map(|_| ty) }; @@ -751,15 +743,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // field access to a union. If we find that, then we will keep the place of the // union being accessed and the field that was being accessed so we can check the // second borrowed place for the same union and a access to a different field. - let Place { local, projection } = first_borrowed_place; - - let mut cursor = projection.as_ref(); - while let [proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in first_borrowed_place.iter_projections().rev() { match elem { - ProjectionElem::Field(field, _) if union_ty(local, proj_base).is_some() => { - return Some((PlaceRef { local, projection: proj_base }, field)); + ProjectionElem::Field(field, _) if union_ty(place_base).is_some() => { + return Some((place_base, field)); } _ => {} } @@ -769,23 +756,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .and_then(|(target_base, target_field)| { // With the place of a union and a field access into it, we traverse the second // borrowed place and look for a access to a different field of the same union. - let Place { local, ref projection } = second_borrowed_place; - - let mut cursor = &projection[..]; - while let [proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in second_borrowed_place.iter_projections().rev() { if let ProjectionElem::Field(field, _) = elem { - if let Some(union_ty) = union_ty(local, proj_base) { - if field != target_field - && local == target_base.local - && proj_base == target_base.projection - { + if let Some(union_ty) = union_ty(place_base) { + if field != target_field && place_base == target_base { return Some(( - self.describe_any_place(PlaceRef { - local, - projection: proj_base, - }), + self.describe_any_place(place_base), self.describe_any_place(first_borrowed_place.as_ref()), self.describe_any_place(second_borrowed_place.as_ref()), union_ty.to_string(), diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs index b1cebbd1f38..350e0d045fa 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs @@ -302,7 +302,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .find_map(|p| self.is_upvar_field_projection(p)); let deref_base = match deref_target_place.projection.as_ref() { - &[ref proj_base @ .., ProjectionElem::Deref] => { + [proj_base @ .., ProjectionElem::Deref] => { PlaceRef { local: deref_target_place.local, projection: &proj_base } } _ => bug!("deref_target_place is not a deref projection"), diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index 44044d55532..006e05072a7 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -1740,20 +1740,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.check_if_full_path_is_moved(location, desired_action, place_span, flow_state); - if let [base_proj @ .., ProjectionElem::Subslice { from, to, from_end: false }] = - place_span.0.projection + if let Some((place_base, ProjectionElem::Subslice { from, to, from_end: false })) = + place_span.0.last_projection() { - let place_ty = - Place::ty_from(place_span.0.local, base_proj, self.body(), self.infcx.tcx); + let place_ty = PlaceRef::ty(&place_base, self.body(), self.infcx.tcx); if let ty::Array(..) = place_ty.ty.kind() { - let array_place = PlaceRef { local: place_span.0.local, projection: base_proj }; self.check_if_subslice_element_is_moved( location, desired_action, - (array_place, place_span.1), + (place_base, place_span.1), maybe_uninits, - *from, - *to, + from, + to, ); return; } @@ -1825,10 +1823,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { debug!("check_if_assigned_path_is_moved place: {:?}", place); // None case => assigning to `x` does not require `x` be initialized. - let mut cursor = &*place.projection.as_ref(); - while let [proj_base @ .., elem] = cursor { - cursor = proj_base; - + for (place_base, elem) in place.iter_projections().rev() { match elem { ProjectionElem::Index(_/*operand*/) | ProjectionElem::ConstantIndex { .. } | @@ -1843,10 +1838,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ProjectionElem::Deref => { self.check_if_full_path_is_moved( location, InitializationRequiringAction::Use, - (PlaceRef { - local: place.local, - projection: proj_base, - }, span), flow_state); + (place_base, span), flow_state); // (base initialized; no need to // recur further) break; @@ -1862,15 +1854,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // assigning to `P.f` requires `P` itself // be already initialized let tcx = self.infcx.tcx; - let base_ty = Place::ty_from(place.local, proj_base, self.body(), tcx).ty; + let base_ty = PlaceRef::ty(&place_base, self.body(), tcx).ty; match base_ty.kind() { ty::Adt(def, _) if def.has_dtor(tcx) => { self.check_if_path_or_subpath_is_moved( location, InitializationRequiringAction::Assignment, - (PlaceRef { - local: place.local, - projection: proj_base, - }, span), flow_state); + (place_base, span), flow_state); // (base initialized; no need to // recur further) @@ -1880,10 +1869,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Once `let s; s.x = V; read(s.x);`, // is allowed, remove this match arm. ty::Adt(..) | ty::Tuple(..) => { - check_parent_of_field(self, location, PlaceRef { - local: place.local, - projection: proj_base, - }, span, flow_state); + check_parent_of_field(self, location, place_base, span, flow_state); // rust-lang/rust#21232, #54499, #54986: during period where we reject // partial initialization, do not complain about unnecessary `mut` on @@ -1965,9 +1951,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // no move out from an earlier location) then this is an attempt at initialization // of the union - we should error in that case. let tcx = this.infcx.tcx; - if let ty::Adt(def, _) = - Place::ty_from(base.local, base.projection, this.body(), tcx).ty.kind() - { + if let ty::Adt(def, _) = PlaceRef::ty(&base, this.body(), tcx).ty.kind() { if def.is_union() { if this.move_data.path_map[mpi].iter().any(|moi| { this.move_data.moves[*moi].source.is_predecessor_of(location, this.body) @@ -2162,9 +2146,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { place: PlaceRef<'tcx>, is_local_mutation_allowed: LocalMutationIsAllowed, ) -> Result<RootPlace<'tcx>, PlaceRef<'tcx>> { - match place { - PlaceRef { local, projection: [] } => { - let local = &self.body.local_decls[local]; + match place.last_projection() { + None => { + let local = &self.body.local_decls[place.local]; match local.mutability { Mutability::Not => match is_local_mutation_allowed { LocalMutationIsAllowed::Yes => Ok(RootPlace { @@ -2186,11 +2170,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }), } } - PlaceRef { local: _, projection: [proj_base @ .., elem] } => { + Some((place_base, elem)) => { match elem { ProjectionElem::Deref => { - let base_ty = - Place::ty_from(place.local, proj_base, self.body(), self.infcx.tcx).ty; + let base_ty = PlaceRef::ty(&place_base, self.body(), self.infcx.tcx).ty; // Check the kind of deref to decide match base_ty.kind() { @@ -2208,10 +2191,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { _ => LocalMutationIsAllowed::Yes, }; - self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - mode, - ) + self.is_mutable(place_base, mode) } } } @@ -2229,10 +2209,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } // `Box<T>` owns its content, so mutable if its location is mutable - _ if base_ty.is_box() => self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - is_local_mutation_allowed, - ), + _ if base_ty.is_box() => { + self.is_mutable(place_base, is_local_mutation_allowed) + } // Deref should only be for reference, pointers or boxes _ => bug!("Deref of unexpected type: {:?}", base_ty), } @@ -2286,10 +2265,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // }); // } // ``` - let _ = self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - is_local_mutation_allowed, - )?; + let _ = + self.is_mutable(place_base, is_local_mutation_allowed)?; Ok(RootPlace { place_local: place.local, place_projection: place.projection, @@ -2298,10 +2275,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } } else { - self.is_mutable( - PlaceRef { local: place.local, projection: proj_base }, - is_local_mutation_allowed, - ) + self.is_mutable(place_base, is_local_mutation_allowed) } } } diff --git a/compiler/rustc_mir/src/borrow_check/prefixes.rs b/compiler/rustc_mir/src/borrow_check/prefixes.rs index 6c5d42296f7..cf04c55eb68 100644 --- a/compiler/rustc_mir/src/borrow_check/prefixes.rs +++ b/compiler/rustc_mir/src/borrow_check/prefixes.rs @@ -10,7 +10,7 @@ use super::MirBorrowckCtxt; use rustc_hir as hir; -use rustc_middle::mir::{Body, Place, PlaceRef, ProjectionElem}; +use rustc_middle::mir::{Body, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, TyCtxt}; pub trait IsPrefixOf<'tcx> { @@ -67,24 +67,23 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { // downcasts here, but may return a base of a downcast). 'cursor: loop { - match &cursor { - PlaceRef { local: _, projection: [] } => { + match cursor.last_projection() { + None => { self.next = None; return Some(cursor); } - PlaceRef { local: _, projection: [proj_base @ .., elem] } => { + Some((cursor_base, elem)) => { match elem { ProjectionElem::Field(_ /*field*/, _ /*ty*/) => { // FIXME: add union handling - self.next = - Some(PlaceRef { local: cursor.local, projection: proj_base }); + self.next = Some(cursor_base); return Some(cursor); } ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => { - cursor = PlaceRef { local: cursor.local, projection: proj_base }; + cursor = cursor_base; continue 'cursor; } ProjectionElem::Deref => { @@ -92,7 +91,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { } } - assert_eq!(*elem, ProjectionElem::Deref); + assert_eq!(elem, ProjectionElem::Deref); match self.kind { PrefixSet::Shallow => { @@ -105,8 +104,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { PrefixSet::All => { // All prefixes: just blindly enqueue the base // of the projection. - self.next = - Some(PlaceRef { local: cursor.local, projection: proj_base }); + self.next = Some(cursor_base); return Some(cursor); } PrefixSet::Supporting => { @@ -119,7 +117,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { // derefs, except we stop at the deref of a shared // reference. - let ty = Place::ty_from(cursor.local, proj_base, self.body, self.tcx).ty; + let ty = PlaceRef::ty(&cursor_base, self.body, self.tcx).ty; match ty.kind() { ty::RawPtr(_) | ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Not) => { // don't continue traversing over derefs of raw pointers or shared @@ -129,14 +127,12 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { } ty::Ref(_ /*rgn*/, _ /*ty*/, hir::Mutability::Mut) => { - self.next = - Some(PlaceRef { local: cursor.local, projection: proj_base }); + self.next = Some(cursor_base); return Some(cursor); } ty::Adt(..) if ty.is_box() => { - self.next = - Some(PlaceRef { local: cursor.local, projection: proj_base }); + self.next = Some(cursor_base); return Some(cursor); } diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index 42cd050abc5..5aad7523c89 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -1855,8 +1855,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.assert_iscleanup(body, block_data, unwind, true); } } - TerminatorKind::InlineAsm { ref destination, .. } => { - if let &Some(target) = destination { + TerminatorKind::InlineAsm { destination, .. } => { + if let Some(target) = destination { self.assert_iscleanup(body, block_data, target, is_cleanup); } } diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs index 72912dd76ff..02a9ec4df16 100644 --- a/compiler/rustc_mir/src/const_eval/machine.rs +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -13,6 +13,7 @@ use rustc_middle::mir::AssertMessage; use rustc_session::Limit; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::{Align, Size}; +use rustc_target::spec::abi::Abi; use crate::interpret::{ self, compile_time_machine, AllocId, Allocation, Frame, ImmTy, InterpCx, InterpResult, Memory, @@ -203,6 +204,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, fn find_mir_or_eval_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, + _abi: Abi, args: &[OpTy<'tcx>], _ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>, _unwind: Option<mir::BasicBlock>, // unwinding is not supported in consts diff --git a/compiler/rustc_mir/src/interpret/machine.rs b/compiler/rustc_mir/src/interpret/machine.rs index f50cc6c16ea..a1a825b3268 100644 --- a/compiler/rustc_mir/src/interpret/machine.rs +++ b/compiler/rustc_mir/src/interpret/machine.rs @@ -10,6 +10,7 @@ use rustc_middle::mir; use rustc_middle::ty::{self, Ty}; use rustc_span::def_id::DefId; use rustc_target::abi::Size; +use rustc_target::spec::abi::Abi; use super::{ AllocId, Allocation, AllocationExtra, CheckInAllocMsg, Frame, ImmTy, InterpCx, InterpResult, @@ -144,6 +145,7 @@ pub trait Machine<'mir, 'tcx>: Sized { fn find_mir_or_eval_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, + abi: Abi, args: &[OpTy<'tcx, Self::PointerTag>], ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>, unwind: Option<mir::BasicBlock>, @@ -154,6 +156,7 @@ pub trait Machine<'mir, 'tcx>: Sized { fn call_extra_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, fn_val: Self::ExtraFnVal, + abi: Abi, args: &[OpTy<'tcx, Self::PointerTag>], ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>, unwind: Option<mir::BasicBlock>, @@ -405,6 +408,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { fn call_extra_fn( _ecx: &mut InterpCx<$mir, $tcx, Self>, fn_val: !, + _abi: Abi, _args: &[OpTy<$tcx>], _ret: Option<(PlaceTy<$tcx>, mir::BasicBlock)>, _unwind: Option<mir::BasicBlock>, diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs index a2931325a28..575667f9a95 100644 --- a/compiler/rustc_mir/src/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -219,7 +219,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let instance = match fn_val { FnVal::Instance(instance) => instance, FnVal::Other(extra) => { - return M::call_extra_fn(self, extra, args, ret, unwind); + return M::call_extra_fn(self, extra, caller_abi, args, ret, unwind); } }; @@ -264,10 +264,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | ty::InstanceDef::CloneShim(..) | ty::InstanceDef::Item(_) => { // We need MIR for this fn - let body = match M::find_mir_or_eval_fn(self, instance, args, ret, unwind)? { - Some(body) => body, - None => return Ok(()), - }; + let body = + match M::find_mir_or_eval_fn(self, instance, caller_abi, args, ret, unwind)? { + Some(body) => body, + None => return Ok(()), + }; self.push_stack_frame( instance, diff --git a/compiler/rustc_mir/src/transform/check_consts/ops.rs b/compiler/rustc_mir/src/transform/check_consts/ops.rs index d2e65abfbc7..9e90a7519cf 100644 --- a/compiler/rustc_mir/src/transform/check_consts/ops.rs +++ b/compiler/rustc_mir/src/transform/check_consts/ops.rs @@ -209,16 +209,61 @@ impl NonConstOp for LiveDrop { } #[derive(Debug)] +/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to +/// the final value of the constant. +pub struct TransientCellBorrow; +impl NonConstOp for TransientCellBorrow { + fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { + Status::Unstable(sym::const_refs_to_cell) + } + fn importance(&self) -> DiagnosticImportance { + // The cases that cannot possibly work will already emit a `CellBorrow`, so we should + // not additionally emit a feature gate error if activating the feature gate won't work. + DiagnosticImportance::Secondary + } + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + feature_err( + &ccx.tcx.sess.parse_sess, + sym::const_refs_to_cell, + span, + "cannot borrow here, since the borrowed element may contain interior mutability", + ) + } +} + +#[derive(Debug)] +/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to +/// the final value of the constant, and thus we cannot allow this (for now). We may allow +/// it in the future for static items. pub struct CellBorrow; impl NonConstOp for CellBorrow { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { - struct_span_err!( + let mut err = struct_span_err!( ccx.tcx.sess, span, E0492, - "cannot borrow a constant which may contain \ - interior mutability, create a static instead" - ) + "{}s cannot refer to interior mutable data", + ccx.const_kind(), + ); + err.span_label( + span, + format!("this borrow of an interior mutable value may end up in the final value"), + ); + if let hir::ConstContext::Static(_) = ccx.const_kind() { + err.help( + "to fix this, the value can be extracted to a separate \ + `static` item and then referenced", + ); + } + if ccx.tcx.sess.teach(&err.get_code().unwrap()) { + err.note( + "A constant containing interior mutable data behind a reference can allow you + to modify that data. This would make multiple uses of a constant to be able to + see different values and allow circumventing the `Send` and `Sync` requirements + for shared mutable data, which is unsound.", + ); + } + err } } diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index 90688ebbd0a..d1c07d1051d 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -3,6 +3,7 @@ use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorReported}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, HirId, LangItem}; +use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; @@ -188,6 +189,9 @@ pub struct Validator<'mir, 'tcx> { /// The span of the current statement. span: Span, + /// A set that stores for each local whether it has a `StorageDead` for it somewhere. + local_has_storage_dead: Option<BitSet<Local>>, + error_emitted: Option<ErrorReported>, secondary_errors: Vec<Diagnostic>, } @@ -206,6 +210,7 @@ impl Validator<'mir, 'tcx> { span: ccx.body.span, ccx, qualifs: Default::default(), + local_has_storage_dead: None, error_emitted: None, secondary_errors: Vec::new(), } @@ -282,6 +287,27 @@ impl Validator<'mir, 'tcx> { } } + fn local_has_storage_dead(&mut self, local: Local) -> bool { + let ccx = self.ccx; + self.local_has_storage_dead + .get_or_insert_with(|| { + struct StorageDeads { + locals: BitSet<Local>, + } + impl Visitor<'tcx> for StorageDeads { + fn visit_statement(&mut self, stmt: &Statement<'tcx>, _: Location) { + if let StatementKind::StorageDead(l) = stmt.kind { + self.locals.insert(l); + } + } + } + let mut v = StorageDeads { locals: BitSet::new_empty(ccx.body.local_decls.len()) }; + v.visit_body(ccx.body); + v.locals + }) + .contains(local) + } + pub fn qualifs_in_return_place(&mut self) -> ConstQualifs { self.qualifs.in_return_place(self.ccx, self.error_emitted) } @@ -556,7 +582,29 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { ); if borrowed_place_has_mut_interior { - self.check_op(ops::CellBorrow); + match self.const_kind() { + // In a const fn all borrows are transient or point to the places given via + // references in the arguments (so we already checked them with + // TransientCellBorrow/CellBorrow as appropriate). + // The borrow checker guarantees that no new non-transient borrows are created. + // NOTE: Once we have heap allocations during CTFE we need to figure out + // how to prevent `const fn` to create long-lived allocations that point + // to (interior) mutable memory. + hir::ConstContext::ConstFn => self.check_op(ops::TransientCellBorrow), + _ => { + // Locals with StorageDead are definitely not part of the final constant value, and + // it is thus inherently safe to permit such locals to have their + // address taken as we can't end up with a reference to them in the + // final value. + // Note: This is only sound if every local that has a `StorageDead` has a + // `StorageDead` in every control flow path leading to a `return` terminator. + if self.local_has_storage_dead(place.local) { + self.check_op(ops::TransientCellBorrow); + } else { + self.check_op(ops::CellBorrow); + } + } + } } } diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs index 1d949e020ed..2d6d0adf3bc 100644 --- a/compiler/rustc_mir/src/transform/const_prop.rs +++ b/compiler/rustc_mir/src/transform/const_prop.rs @@ -25,6 +25,7 @@ use rustc_middle::ty::{ use rustc_session::lint; use rustc_span::{def_id::DefId, Span}; use rustc_target::abi::{HasDataLayout, LayoutOf, Size, TargetDataLayout}; +use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use crate::const_eval::ConstEvalErr; @@ -187,6 +188,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> fn find_mir_or_eval_fn( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _instance: ty::Instance<'tcx>, + _abi: Abi, _args: &[OpTy<'tcx>], _ret: Option<(PlaceTy<'tcx>, BasicBlock)>, _unwind: Option<BasicBlock>, diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 6e7575c1d71..52350c5d078 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -41,6 +41,15 @@ impl<'tcx> MirPass<'tcx> for Inline { return; } + if tcx.sess.opts.debugging_opts.instrument_coverage { + // Since `Inline` happens after `InstrumentCoverage`, the function-specific coverage + // counters can be invalidated, such as by merging coverage counter statements from + // a pre-inlined function into a different function. This kind of change is invalid, + // so inlining must be skipped. Note: This check is performed here so inlining can + // be disabled without preventing other optimizations (regardless of `mir_opt_level`). + return; + } + if inline(tcx, body) { debug!("running simplify cfg on {:?}", body.source); CfgSimplifier::new(body).simplify(); @@ -742,11 +751,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } fn visit_span(&mut self, span: &mut Span) { + let mut expn_data = + ExpnData::default(ExpnKind::Inlined, *span, self.tcx.sess.edition(), None); + expn_data.def_site = self.body_span; // Make sure that all spans track the fact that they were inlined. - *span = self.callsite_span.fresh_expansion(ExpnData { - def_site: self.body_span, - ..ExpnData::default(ExpnKind::Inlined, *span, self.tcx.sess.edition(), None) - }); + *span = self.callsite_span.fresh_expansion(expn_data); } fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index db817b378f9..a70c1a28176 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -503,6 +503,11 @@ fn non_exhaustive_match<'p, 'tcx>( )); } } + if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() { + if cx.tcx.is_ty_uninhabited_from(cx.module, sub_ty, cx.param_env) { + err.note("references are always considered inhabited"); + } + } err.emit(); } diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 9abffbacfc3..62fd6936d21 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -236,7 +236,6 @@ pub fn parse_in<'a, T>( pub fn nt_to_tokenstream( nt: &Nonterminal, sess: &ParseSess, - span: Span, synthesize_tokens: CanSynthesizeMissingTokens, ) -> TokenStream { // A `Nonterminal` is often a parsed AST item. At this point we now @@ -256,11 +255,18 @@ pub fn nt_to_tokenstream( |tokens: Option<&LazyTokenStream>| tokens.as_ref().map(|t| t.create_token_stream()); let tokens = match *nt { - Nonterminal::NtItem(ref item) => { - prepend_attrs(sess, &item.attrs, nt, span, item.tokens.as_ref()) - } + Nonterminal::NtItem(ref item) => prepend_attrs(sess, &item.attrs, nt, item.tokens.as_ref()), Nonterminal::NtBlock(ref block) => convert_tokens(block.tokens.as_ref()), - Nonterminal::NtStmt(ref stmt) => prepend_attrs(sess, stmt.attrs(), nt, span, stmt.tokens()), + 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))) + } else { + do_prepend(stmt.tokens()) + } + } Nonterminal::NtPat(ref pat) => convert_tokens(pat.tokens.as_ref()), Nonterminal::NtTy(ref ty) => convert_tokens(ty.tokens.as_ref()), Nonterminal::NtIdent(ident, is_raw) => { @@ -277,31 +283,29 @@ pub fn nt_to_tokenstream( if expr.tokens.is_none() { debug!("missing tokens for expr {:?}", expr); } - prepend_attrs(sess, &expr.attrs, nt, span, expr.tokens.as_ref()) + prepend_attrs(sess, &expr.attrs, nt, expr.tokens.as_ref()) } }; if let Some(tokens) = tokens { return tokens; } else if matches!(synthesize_tokens, CanSynthesizeMissingTokens::Yes) { - return fake_token_stream(sess, nt, span); + return fake_token_stream(sess, nt); } else { - let pretty = rustc_ast_pretty::pprust::nonterminal_to_string_no_extra_parens(&nt); - panic!("Missing tokens at {:?} for nt {:?}", span, pretty); + panic!("Missing tokens for nt {:?}", pprust::nonterminal_to_string(nt)); } } -pub fn fake_token_stream(sess: &ParseSess, nt: &Nonterminal, span: Span) -> 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(span)) + parse_stream_from_source_str(filename, source, sess, Some(nt.span())) } fn prepend_attrs( sess: &ParseSess, attrs: &[ast::Attribute], nt: &Nonterminal, - span: Span, tokens: Option<&tokenstream::LazyTokenStream>, ) -> Option<tokenstream::TokenStream> { if attrs.is_empty() { @@ -312,7 +316,7 @@ fn prepend_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, span)); + return Some(fake_token_stream(sess, nt)); } builder.push(attr.tokens()); } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d11db74a3bd..f4332e4548a 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1599,10 +1599,6 @@ impl<'a> Parser<'a> { } else { Async::No }; - if let Async::Yes { span, .. } = asyncness { - // Feature-gate `async ||` closures. - self.sess.gated_spans.gate(sym::async_closure, span); - } let capture_clause = self.parse_capture_clause()?; let decl = self.parse_fn_block_decl()?; @@ -1619,6 +1615,11 @@ impl<'a> Parser<'a> { } }; + if let Async::Yes { span, .. } = asyncness { + // Feature-gate `async ||` closures. + self.sess.gated_spans.gate(sym::async_closure, span); + } + Ok(self.mk_expr( lo.to(body.span), ExprKind::Closure(capture_clause, asyncness, movability, decl, body, lo.to(decl_hi)), diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index e49b1a54e9b..4fcc9edb7d9 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -220,7 +220,22 @@ impl<'a> Parser<'a> { let info = if self.eat_keyword(kw::Use) { // USE ITEM let tree = self.parse_use_tree()?; - self.expect_semi()?; + + // If wildcard or glob-like brace syntax doesn't have `;`, + // the user may not know `*` or `{}` should be the last. + if let Err(mut e) = self.expect_semi() { + match tree.kind { + UseTreeKind::Glob => { + e.note("the wildcard token must be last on the path").emit(); + } + UseTreeKind::Nested(..) => { + e.note("glob-like brace syntax must be last on the path").emit(); + } + _ => (), + } + return Err(e); + } + (Ident::invalid(), ItemKind::Use(P(tree))) } else if self.check_fn_front_matter() { // FUNCTION ITEM diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 073e62c41d3..45964b1c988 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -983,8 +983,8 @@ impl<'a> Parser<'a> { _ => self.sess.gated_spans.gate(sym::extended_key_value_attributes, span), } - let token = token::Interpolated(Lrc::new(token::NtExpr(expr))); - MacArgs::Eq(eq_span, TokenTree::token(token, span).into()) + let token_kind = token::Interpolated(Lrc::new(token::NtExpr(expr))); + MacArgs::Eq(eq_span, Token::new(token_kind, span)) } else { MacArgs::Empty } @@ -1239,7 +1239,15 @@ impl<'a> Parser<'a> { f: impl FnOnce(&mut Self) -> PResult<'a, R>, ) -> PResult<'a, (R, Option<LazyTokenStream>)> { let start_token = (self.token.clone(), self.token_spacing); - let cursor_snapshot = self.token_cursor.clone(); + let cursor_snapshot = TokenCursor { + frame: self.token_cursor.frame.clone(), + // We only ever capture tokens within our current frame, + // so we can just use an empty frame stack + stack: vec![], + desugar_doc_comments: self.token_cursor.desugar_doc_comments, + num_next_calls: self.token_cursor.num_next_calls, + append_unglued_token: self.token_cursor.append_unglued_token.clone(), + }; let ret = f(self)?; diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index f4bb9610940..21372725a68 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -2,7 +2,7 @@ use crate::parse_in; -use rustc_ast::tokenstream::DelimSpan; +use rustc_ast::tokenstream::{DelimSpan, TokenTree}; use rustc_ast::{self as ast, Attribute, MacArgs, MacDelimiter, MetaItem, MetaItemKind}; use rustc_errors::{Applicability, PResult}; use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP}; @@ -45,7 +45,8 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta kind: match &item.args { MacArgs::Empty => MetaItemKind::Word, MacArgs::Eq(_, t) => { - let v = parse_in(sess, t.clone(), "name value", |p| p.parse_unsuffixed_lit())?; + let t = TokenTree::Token(t.clone()).into(); + let v = parse_in(sess, t, "name value", |p| p.parse_unsuffixed_lit())?; MetaItemKind::NameValue(v) } MacArgs::Delimited(dspan, delim, t) => { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index aeaa862f5fd..a6a61ffc5da 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -310,7 +310,7 @@ impl CheckAttrVisitor<'tcx> { .sess .struct_span_err( meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - &format!("{:?} character isn't allowed in `#[doc(alias = \"...\")]`", c,), + &format!("{:?} character isn't allowed in `#[doc(alias = \"...\")]`", c), ) .emit(); return false; @@ -358,6 +358,17 @@ impl CheckAttrVisitor<'tcx> { .emit(); return false; } + let item_name = self.tcx.hir().name(hir_id); + if &*item_name.as_str() == doc_alias { + self.tcx + .sess + .struct_span_err( + meta.span(), + &format!("`#[doc(alias = \"...\")]` is the same as the item's name"), + ) + .emit(); + return false; + } true } diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs index 711e8e87c6c..ee90e9c54f6 100644 --- a/compiler/rustc_passes/src/intrinsicck.rs +++ b/compiler/rustc_passes/src/intrinsicck.rs @@ -78,7 +78,7 @@ impl ExprVisitor<'tcx> { return; } - // Special-case transmutting from `typeof(function)` and + // Special-case transmuting from `typeof(function)` and // `Option<typeof(function)>` to present a clearer error. let from = unpack_option_like(self.tcx, from); if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) { diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 5b50ef8627b..788f1df328c 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -149,7 +149,7 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, hir_id: HirId, body: &'tcx hir::Body<'tcx>, fn_span: Span) { let mut this = CheckInlineAssembly { tcx, items: Vec::new() }; this.visit_body(body); - if let &[(ItemKind::Asm, _)] = &this.items[..] { + if let [(ItemKind::Asm, _)] = this.items[..] { // Ok. } else { tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, fn_span, |lint| { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 1bcfdf0faf6..c2db2c82fa1 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -832,10 +832,15 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> { } fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) { + // Non-opaque macros cannot make other items more accessible than they already are. if attr::find_transparency(&self.tcx.sess, &md.attrs, md.ast.macro_rules).0 != Transparency::Opaque { - self.update(md.hir_id, Some(AccessLevel::Public)); + // `#[macro_export]`-ed `macro_rules!` are `Public` since they + // ignore their containing path to always appear at the crate root. + if md.ast.macro_rules { + self.update(md.hir_id, Some(AccessLevel::Public)); + } return; } diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index ff52fdab19c..64aba870502 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -153,12 +153,6 @@ where } } -impl<Ctxt: DepContext> DepNodeParams<Ctxt> for () { - fn to_fingerprint(&self, _: Ctxt) -> Fingerprint { - Fingerprint::ZERO - } -} - /// A "work product" corresponds to a `.o` (or other) file that we /// save in between runs. These IDs do not have a `DefId` but rather /// some independent path or string that persists between runs without diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index da0b5aad6c8..b1c901633a7 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -61,7 +61,7 @@ pub trait DepContext: Copy { } /// Describe the different families of dependency nodes. -pub trait DepKind: Copy + fmt::Debug + Eq + Ord + Hash { +pub trait DepKind: Copy + fmt::Debug + Eq + Hash { const NULL: Self; /// Return whether this kind always require evaluation. diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 426f5bb41d6..d17af6120c7 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -566,7 +566,6 @@ fn incremental_verify_ich<CTX, K, V>( assert!(new_hash == old_hash, "found unstable fingerprints for {:?}", dep_node,); } -#[inline(always)] fn force_query_with_job<C, CTX>( tcx: CTX, key: C::Key, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index c5f783e84a9..e96fc185b7e 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -96,7 +96,7 @@ impl<'a> Resolver<'a> { /// Walks up the tree of definitions starting at `def_id`, /// stopping at the first `DefKind::Mod` encountered - fn nearest_mod_parent(&mut self, def_id: DefId) -> Module<'a> { + fn nearest_parent_mod(&mut self, def_id: DefId) -> Module<'a> { let def_key = self.cstore().def_key(def_id); let mut parent_id = DefId { @@ -137,7 +137,7 @@ impl<'a> Resolver<'a> { .get_opt_name() .expect("given a DefId that wasn't a module"); - let parent = Some(self.nearest_mod_parent(def_id)); + let parent = Some(self.nearest_parent_mod(def_id)); (name, parent) }; @@ -179,7 +179,7 @@ impl<'a> Resolver<'a> { // so this hopefully won't be a problem. // // See https://github.com/rust-lang/rust/pull/77984#issuecomment-712445508 - self.nearest_mod_parent(def_id) + self.nearest_parent_mod(def_id) } } @@ -266,7 +266,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } else { // If it's not in an enum, its visibility is restricted to the `mod` item // that it's defined in. - Ok(ty::Visibility::Restricted(self.parent_scope.module.normal_ancestor_id)) + Ok(ty::Visibility::Restricted(self.parent_scope.module.nearest_parent_mod)) } } ast::VisibilityKind::Restricted { ref path, id, .. } => { @@ -803,7 +803,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let module = self.r.new_module( parent, module_kind, - parent.normal_ancestor_id, + parent.nearest_parent_mod, expansion, item.span, ); @@ -878,7 +878,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let module = self.r.new_module( parent, module_kind, - parent.normal_ancestor_id, + parent.nearest_parent_mod, expansion, item.span, ); @@ -921,7 +921,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let module = self.r.new_module( parent, ModuleKind::Block(block.id), - parent.normal_ancestor_id, + parent.nearest_parent_mod, expansion, block.span, ); @@ -1298,26 +1298,31 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { method!(visit_ty: ast::Ty, ast::TyKind::MacCall, walk_ty); fn visit_item(&mut self, item: &'b Item) { - let macro_use = match item.kind { + let orig_module_scope = self.parent_scope.module; + self.parent_scope.macro_rules = match item.kind { ItemKind::MacroDef(..) => { - self.parent_scope.macro_rules = self.define_macro(item); - return; + let macro_rules_scope = self.define_macro(item); + visit::walk_item(self, item); + macro_rules_scope } ItemKind::MacCall(..) => { - self.parent_scope.macro_rules = self.visit_invoc_in_module(item.id); - return; + let macro_rules_scope = self.visit_invoc_in_module(item.id); + visit::walk_item(self, item); + macro_rules_scope + } + _ => { + let orig_macro_rules_scope = self.parent_scope.macro_rules; + self.build_reduced_graph_for_item(item); + visit::walk_item(self, item); + match item.kind { + ItemKind::Mod(..) if self.contains_macro_use(&item.attrs) => { + self.parent_scope.macro_rules + } + _ => orig_macro_rules_scope, + } } - ItemKind::Mod(..) => self.contains_macro_use(&item.attrs), - _ => false, }; - let orig_current_module = self.parent_scope.module; - let orig_current_macro_rules_scope = self.parent_scope.macro_rules; - self.build_reduced_graph_for_item(item); - visit::walk_item(self, item); - self.parent_scope.module = orig_current_module; - if !macro_use { - self.parent_scope.macro_rules = orig_current_macro_rules_scope; - } + self.parent_scope.module = orig_module_scope; } fn visit_stmt(&mut self, stmt: &'b ast::Stmt) { diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 48bce884394..727d6ab53d8 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -91,7 +91,10 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { DefPathData::ValueNs(i.ident.name) } ItemKind::MacroDef(..) => DefPathData::MacroNs(i.ident.name), - ItemKind::MacCall(..) => return self.visit_macro_invoc(i.id), + ItemKind::MacCall(..) => { + visit::walk_item(self, i); + return self.visit_macro_invoc(i.id); + } ItemKind::GlobalAsm(..) => DefPathData::Misc, ItemKind::Use(..) => { return visit::walk_item(self, i); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 809de9beff6..4f8047ac2df 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -398,14 +398,30 @@ impl<'a> Resolver<'a> { err.help("use the `|| { ... }` closure form instead"); err } - ResolutionError::AttemptToUseNonConstantValueInConstant => { + ResolutionError::AttemptToUseNonConstantValueInConstant(ident, sugg, current) => { let mut err = struct_span_err!( self.session, span, E0435, "attempt to use a non-constant value in a constant" ); - err.span_label(span, "non-constant value"); + // let foo =... + // ^^^ given this Span + // ------- get this Span to have an applicable suggestion + let sp = + self.session.source_map().span_extend_to_prev_str(ident.span, current, true); + if sp.lo().0 == 0 { + err.span_label(ident.span, &format!("this would need to be a `{}`", sugg)); + } else { + let sp = sp.with_lo(BytePos(sp.lo().0 - current.len() as u32)); + err.span_suggestion( + sp, + &format!("consider using `{}` instead of `{}`", sugg, current), + format!("{} {}", sugg, ident), + Applicability::MaybeIncorrect, + ); + err.span_label(span, "non-constant value"); + } err } ResolutionError::BindingShadowsSomethingUnacceptable(what_binding, name, binding) => { @@ -595,7 +611,8 @@ impl<'a> Resolver<'a> { filter_fn: &impl Fn(Res) -> bool, ) -> Option<TypoSuggestion> { let mut suggestions = Vec::new(); - self.visit_scopes(scope_set, parent_scope, ident, |this, scope, use_prelude, _| { + let ctxt = ident.span.ctxt(); + self.visit_scopes(scope_set, parent_scope, ctxt, |this, scope, use_prelude, _| { match scope { Scope::DeriveHelpers(expn_id) => { let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index fbe99a31150..8544e1d8ee5 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -92,6 +92,12 @@ crate enum HasGenericParams { No, } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +crate enum ConstantItemKind { + Const, + Static, +} + /// The rib kind restricts certain accesses, /// e.g. to a `Res::Local` of an outer item. #[derive(Copy, Clone, Debug)] @@ -119,7 +125,7 @@ crate enum RibKind<'a> { /// /// The `bool` indicates if this constant may reference generic parameters /// and is used to only allow generic parameters to be used in trivial constant expressions. - ConstantItemRibKind(bool), + ConstantItemRibKind(bool, Option<(Ident, ConstantItemKind)>), /// We passed through a module. ModuleRibKind(Module<'a>), @@ -145,7 +151,7 @@ impl RibKind<'_> { NormalRibKind | ClosureOrAsyncRibKind | FnItemRibKind - | ConstantItemRibKind(_) + | ConstantItemRibKind(..) | ModuleRibKind(_) | MacroDefinition(_) | ConstParamTyRibKind => false, @@ -262,52 +268,60 @@ impl<'a> PathSource<'a> { crate fn is_expected(self, res: Res) -> bool { match self { - PathSource::Type => matches!(res, Res::Def( + PathSource::Type => matches!( + res, + Res::Def( DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Trait - | DefKind::TraitAlias - | DefKind::TyAlias - | DefKind::AssocTy - | DefKind::TyParam - | DefKind::OpaqueTy - | DefKind::ForeignTy, + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::TyAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::OpaqueTy + | DefKind::ForeignTy, _, - ) - | Res::PrimTy(..) - | Res::SelfTy(..)), + ) | Res::PrimTy(..) + | Res::SelfTy(..) + ), PathSource::Trait(AliasPossibility::No) => matches!(res, Res::Def(DefKind::Trait, _)), PathSource::Trait(AliasPossibility::Maybe) => { matches!(res, Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) } - PathSource::Expr(..) => matches!(res, Res::Def( + PathSource::Expr(..) => matches!( + res, + Res::Def( DefKind::Ctor(_, CtorKind::Const | CtorKind::Fn) - | DefKind::Const - | DefKind::Static - | DefKind::Fn - | DefKind::AssocFn - | DefKind::AssocConst - | DefKind::ConstParam, + | DefKind::Const + | DefKind::Static + | DefKind::Fn + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::ConstParam, _, - ) - | Res::Local(..) - | Res::SelfCtor(..)), - PathSource::Pat => matches!(res, Res::Def( + ) | Res::Local(..) + | Res::SelfCtor(..) + ), + PathSource::Pat => matches!( + res, + Res::Def( DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst, _, - ) - | Res::SelfCtor(..)), + ) | Res::SelfCtor(..) + ), PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(), - PathSource::Struct => matches!(res, Res::Def( + PathSource::Struct => matches!( + res, + Res::Def( DefKind::Struct - | DefKind::Union - | DefKind::Variant - | DefKind::TyAlias - | DefKind::AssocTy, + | DefKind::Union + | DefKind::Variant + | DefKind::TyAlias + | DefKind::AssocTy, _, - ) - | Res::SelfTy(..)), + ) | Res::SelfTy(..) + ), PathSource::TraitItem(ns) => match res { Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) if ns == ValueNS => true, Res::Def(DefKind::AssocTy, _) if ns == TypeNS => true, @@ -634,7 +648,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // Note that we might not be inside of an repeat expression here, // but considering that `IsRepeatExpr` is only relevant for // non-trivial constants this is doesn't matter. - self.with_constant_rib(IsRepeatExpr::No, true, |this| { + self.with_constant_rib(IsRepeatExpr::No, true, None, |this| { this.smart_resolve_path( ty.id, qself.as_ref(), @@ -843,7 +857,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { | ClosureOrAsyncRibKind | FnItemRibKind | ItemRibKind(..) - | ConstantItemRibKind(_) + | ConstantItemRibKind(..) | ModuleRibKind(..) | ForwardTyParamBanRibKind | ConstParamTyRibKind => { @@ -970,6 +984,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.with_constant_rib( IsRepeatExpr::No, true, + None, |this| this.visit_expr(expr), ); } @@ -1012,11 +1027,19 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.with_item_rib(HasGenericParams::No, |this| { this.visit_ty(ty); if let Some(expr) = expr { + let constant_item_kind = match item.kind { + ItemKind::Const(..) => ConstantItemKind::Const, + ItemKind::Static(..) => ConstantItemKind::Static, + _ => unreachable!(), + }; // We already forbid generic params because of the above item rib, // so it doesn't matter whether this is a trivial constant. - this.with_constant_rib(IsRepeatExpr::No, true, |this| { - this.visit_expr(expr) - }); + this.with_constant_rib( + IsRepeatExpr::No, + true, + Some((item.ident, constant_item_kind)), + |this| this.visit_expr(expr), + ); } }); } @@ -1118,15 +1141,16 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &mut self, is_repeat: IsRepeatExpr, is_trivial: bool, + item: Option<(Ident, ConstantItemKind)>, f: impl FnOnce(&mut Self), ) { debug!("with_constant_rib: is_repeat={:?} is_trivial={}", is_repeat, is_trivial); - self.with_rib(ValueNS, ConstantItemRibKind(is_trivial), |this| { + self.with_rib(ValueNS, ConstantItemRibKind(is_trivial, item), |this| { this.with_rib( TypeNS, - ConstantItemRibKind(is_repeat == IsRepeatExpr::Yes || is_trivial), + ConstantItemRibKind(is_repeat == IsRepeatExpr::Yes || is_trivial, item), |this| { - this.with_label_rib(ConstantItemRibKind(is_trivial), f); + this.with_label_rib(ConstantItemRibKind(is_trivial, item), f); }, ) }); @@ -1266,6 +1290,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { this.with_constant_rib( IsRepeatExpr::No, true, + None, |this| { visit::walk_assoc_item( this, @@ -1775,7 +1800,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if this.should_report_errs() { let (err, candidates) = this.smart_resolve_report_errors(path, span, source, res); - let def_id = this.parent_scope.module.normal_ancestor_id; + let def_id = this.parent_scope.module.nearest_parent_mod; let instead = res.is_some(); let suggestion = if res.is_none() { this.report_missing_type_error(path) } else { None }; @@ -1843,7 +1868,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { drop(parent_err); - let def_id = this.parent_scope.module.normal_ancestor_id; + let def_id = this.parent_scope.module.nearest_parent_mod; if this.should_report_errs() { this.r.use_injections.push(UseError { @@ -2200,6 +2225,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.with_constant_rib( is_repeat, constant.value.is_potential_trivial_const_param(), + None, |this| { visit::walk_anon_const(this, constant); }, @@ -2397,8 +2423,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &mut found_traits, &self.parent_scope, ); - search_module = - unwrap_or!(self.r.hygienic_lexical_parent(search_module, &mut ident.span), break); + let mut span_data = ident.span.data(); + search_module = unwrap_or!( + self.r.hygienic_lexical_parent(search_module, &mut span_data.ctxt), + break + ); + ident.span = span_data.span(); } if let Some(prelude) = self.r.prelude { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 0de732b2cf9..fb364053e24 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -33,7 +33,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::ptr_key::PtrKey; use rustc_data_structures::sync::Lrc; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; -use rustc_expand::base::SyntaxExtension; +use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_hir::def::Namespace::*; use rustc_hir::def::{self, CtorOf, DefKind, NonMacroAttrKind, PartialRes}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; @@ -50,6 +50,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer}; use rustc_session::Session; +use rustc_span::edition::Edition; use rustc_span::hygiene::{ExpnId, ExpnKind, MacroKind, SyntaxContext, Transparency}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -64,7 +65,7 @@ use tracing::debug; use diagnostics::{extend_span_to_previous_binding, find_span_of_binding_until_next_binding}; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use imports::{Import, ImportKind, ImportResolver, NameResolution}; -use late::{HasGenericParams, PathSource, Rib, RibKind::*}; +use late::{ConstantItemKind, HasGenericParams, PathSource, Rib, RibKind::*}; use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; type Res = def::Res<NodeId>; @@ -210,7 +211,11 @@ enum ResolutionError<'a> { /// Error E0434: can't capture dynamic environment in a fn item. CannotCaptureDynamicEnvironmentInFnItem, /// Error E0435: attempt to use a non-constant value in a constant. - AttemptToUseNonConstantValueInConstant, + AttemptToUseNonConstantValueInConstant( + Ident, + /* suggestion */ &'static str, + /* current */ &'static str, + ), /// Error E0530: `X` bindings cannot shadow `Y`s. BindingShadowsSomethingUnacceptable(&'static str, Symbol, &'a NameBinding<'a>), /// Error E0128: type parameters with a default cannot use forward-declared identifiers. @@ -422,7 +427,9 @@ enum ModuleKind { /// /// This could be: /// - /// * A normal module ‒ either `mod from_file;` or `mod from_block { }`. + /// * A normal module – either `mod from_file;` or `mod from_block { }` – + /// or the crate root (which is conceptually a top-level module). + /// Note that the crate root's [name][Self::name] will be [`kw::Empty`]. /// * A trait or an enum (it implicitly contains associated types, methods and variant /// constructors). Def(DefKind, DefId, Symbol), @@ -456,28 +463,42 @@ struct BindingKey { type Resolutions<'a> = RefCell<FxIndexMap<BindingKey, &'a RefCell<NameResolution<'a>>>>; /// One node in the tree of modules. +/// +/// Note that a "module" in resolve is broader than a `mod` that you declare in Rust code. It may be one of these: +/// +/// * `mod` +/// * crate root (aka, top-level anonymous module) +/// * `enum` +/// * `trait` +/// * curly-braced block with statements +/// +/// You can use [`ModuleData::kind`] to determine the kind of module this is. pub struct ModuleData<'a> { + /// The direct parent module (it may not be a `mod`, however). parent: Option<Module<'a>>, + /// What kind of module this is, because this may not be a `mod`. kind: ModuleKind, - // The def id of the closest normal module (`mod`) ancestor (including this module). - normal_ancestor_id: DefId, + /// The [`DefId`] of the nearest `mod` item ancestor (which may be this module). + /// This may be the crate root. + nearest_parent_mod: DefId, - // Mapping between names and their (possibly in-progress) resolutions in this module. - // Resolutions in modules from other crates are not populated until accessed. + /// Mapping between names and their (possibly in-progress) resolutions in this module. + /// Resolutions in modules from other crates are not populated until accessed. lazy_resolutions: Resolutions<'a>, - // True if this is a module from other crate that needs to be populated on access. + /// True if this is a module from other crate that needs to be populated on access. populate_on_access: Cell<bool>, - // Macro invocations that can expand into items in this module. + /// Macro invocations that can expand into items in this module. unexpanded_invocations: RefCell<FxHashSet<ExpnId>>, + /// Whether `#[no_implicit_prelude]` is active. no_implicit_prelude: bool, glob_importers: RefCell<Vec<&'a Import<'a>>>, globs: RefCell<Vec<&'a Import<'a>>>, - // Used to memoize the traits in this module for faster searches through all traits in scope. + /// Used to memoize the traits in this module for faster searches through all traits in scope. traits: RefCell<Option<Box<[(Ident, &'a NameBinding<'a>)]>>>, /// Span of the module itself. Used for error reporting. @@ -492,16 +513,16 @@ impl<'a> ModuleData<'a> { fn new( parent: Option<Module<'a>>, kind: ModuleKind, - normal_ancestor_id: DefId, + nearest_parent_mod: DefId, expansion: ExpnId, span: Span, ) -> Self { ModuleData { parent, kind, - normal_ancestor_id, + nearest_parent_mod, lazy_resolutions: Default::default(), - populate_on_access: Cell::new(!normal_ancestor_id.is_local()), + populate_on_access: Cell::new(!nearest_parent_mod.is_local()), unexpanded_invocations: Default::default(), no_implicit_prelude: false, glob_importers: RefCell::new(Vec::new()), @@ -743,10 +764,13 @@ impl<'a> NameBinding<'a> { } fn is_variant(&self) -> bool { - matches!(self.kind, NameBindingKind::Res( + matches!( + self.kind, + NameBindingKind::Res( Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), _), _, - )) + ) + ) } fn is_extern_crate(&self) -> bool { @@ -850,7 +874,7 @@ pub struct ExternPreludeEntry<'a> { /// Used for better errors for E0773 enum BuiltinMacroState { - NotYetSeen(SyntaxExtension), + NotYetSeen(SyntaxExtensionKind), AlreadySeen(Span), } @@ -1427,7 +1451,7 @@ impl<'a> Resolver<'a> { } fn is_builtin_macro(&mut self, res: Res) -> bool { - self.get_macro(res).map_or(false, |ext| ext.is_builtin) + self.get_macro(res).map_or(false, |ext| ext.builtin_name.is_some()) } fn macro_def(&self, mut ctxt: SyntaxContext) -> DefId { @@ -1519,11 +1543,11 @@ impl<'a> Resolver<'a> { &self, parent: Module<'a>, kind: ModuleKind, - normal_ancestor_id: DefId, + nearest_parent_mod: DefId, expn_id: ExpnId, span: Span, ) -> Module<'a> { - let module = ModuleData::new(Some(parent), kind, normal_ancestor_id, expn_id, span); + let module = ModuleData::new(Some(parent), kind, nearest_parent_mod, expn_id, span); self.arenas.alloc_module(module) } @@ -1610,8 +1634,13 @@ impl<'a> Resolver<'a> { &mut self, scope_set: ScopeSet, parent_scope: &ParentScope<'a>, - ident: Ident, - mut visitor: impl FnMut(&mut Self, Scope<'a>, /*use_prelude*/ bool, Ident) -> Option<T>, + ctxt: SyntaxContext, + mut visitor: impl FnMut( + &mut Self, + Scope<'a>, + /*use_prelude*/ bool, + SyntaxContext, + ) -> Option<T>, ) -> Option<T> { // General principles: // 1. Not controlled (user-defined) names should have higher priority than controlled names @@ -1654,7 +1683,7 @@ impl<'a> Resolver<'a> { // 4c. Standard library prelude (de-facto closed, controlled). // 6. Language prelude: builtin attributes (closed, controlled). - let rust_2015 = ident.span.rust_2015(); + let rust_2015 = ctxt.edition() == Edition::Edition2015; let (ns, macro_kind, is_absolute_path) = match scope_set { ScopeSet::All(ns, _) => (ns, None, false), ScopeSet::AbsolutePath(ns) => (ns, None, true), @@ -1667,7 +1696,7 @@ impl<'a> Resolver<'a> { TypeNS | ValueNS => Scope::Module(module), MacroNS => Scope::DeriveHelpers(parent_scope.expansion), }; - let mut ident = ident.normalize_to_macros_2_0(); + let mut ctxt = ctxt.normalize_to_macros_2_0(); let mut use_prelude = !module.no_implicit_prelude; loop { @@ -1703,7 +1732,7 @@ impl<'a> Resolver<'a> { }; if visit { - if let break_result @ Some(..) = visitor(self, scope, use_prelude, ident) { + if let break_result @ Some(..) = visitor(self, scope, use_prelude, ctxt) { return break_result; } } @@ -1733,17 +1762,17 @@ impl<'a> Resolver<'a> { }, Scope::CrateRoot => match ns { TypeNS => { - ident.span.adjust(ExpnId::root()); + ctxt.adjust(ExpnId::root()); Scope::ExternPrelude } ValueNS | MacroNS => break, }, Scope::Module(module) => { use_prelude = !module.no_implicit_prelude; - match self.hygienic_lexical_parent(module, &mut ident.span) { + match self.hygienic_lexical_parent(module, &mut ctxt) { Some(parent_module) => Scope::Module(parent_module), None => { - ident.span.adjust(ExpnId::root()); + ctxt.adjust(ExpnId::root()); match ns { TypeNS => Scope::ExternPrelude, ValueNS => Scope::StdLibPrelude, @@ -1821,14 +1850,16 @@ impl<'a> Resolver<'a> { // Use the rib kind to determine whether we are resolving parameters // (macro 2.0 hygiene) or local variables (`macro_rules` hygiene). let rib_ident = if ribs[i].kind.contains_params() { normalized_ident } else { ident }; - if let Some(res) = ribs[i].bindings.get(&rib_ident).cloned() { + if let Some((original_rib_ident_def, res)) = ribs[i].bindings.get_key_value(&rib_ident) + { // The ident resolves to a type parameter or local variable. return Some(LexicalScopeBinding::Res(self.validate_res_from_ribs( i, rib_ident, - res, + *res, record_used, path_span, + *original_rib_ident_def, ribs, ))); } @@ -1866,16 +1897,18 @@ impl<'a> Resolver<'a> { ident = normalized_ident; let mut poisoned = None; loop { + let mut span_data = ident.span.data(); let opt_module = if let Some(node_id) = record_used_id { self.hygienic_lexical_parent_with_compatibility_fallback( module, - &mut ident.span, + &mut span_data.ctxt, node_id, &mut poisoned, ) } else { - self.hygienic_lexical_parent(module, &mut ident.span) + self.hygienic_lexical_parent(module, &mut span_data.ctxt) }; + ident.span = span_data.span(); module = unwrap_or!(opt_module, break); let adjusted_parent_scope = &ParentScope { module, ..*parent_scope }; let result = self.resolve_ident_in_module_unadjusted( @@ -1949,10 +1982,10 @@ impl<'a> Resolver<'a> { fn hygienic_lexical_parent( &mut self, module: Module<'a>, - span: &mut Span, + ctxt: &mut SyntaxContext, ) -> Option<Module<'a>> { - if !module.expansion.outer_expn_is_descendant_of(span.ctxt()) { - return Some(self.macro_def_scope(span.remove_mark())); + if !module.expansion.outer_expn_is_descendant_of(*ctxt) { + return Some(self.macro_def_scope(ctxt.remove_mark())); } if let ModuleKind::Block(..) = module.kind { @@ -1965,11 +1998,11 @@ impl<'a> Resolver<'a> { fn hygienic_lexical_parent_with_compatibility_fallback( &mut self, module: Module<'a>, - span: &mut Span, + ctxt: &mut SyntaxContext, node_id: NodeId, poisoned: &mut Option<NodeId>, ) -> Option<Module<'a>> { - if let module @ Some(..) = self.hygienic_lexical_parent(module, span) { + if let module @ Some(..) = self.hygienic_lexical_parent(module, ctxt) { return module; } @@ -1992,9 +2025,9 @@ impl<'a> Resolver<'a> { // The macro is a proc macro derive if let Some(def_id) = module.expansion.expn_data().macro_def_id { let ext = self.get_macro_by_def_id(def_id); - if !ext.is_builtin + if ext.builtin_name.is_none() && ext.macro_kind() == MacroKind::Derive - && parent.expansion.outer_expn_is_descendant_of(span.ctxt()) + && parent.expansion.outer_expn_is_descendant_of(*ctxt) { *poisoned = Some(node_id); return module.parent; @@ -2116,7 +2149,7 @@ impl<'a> Resolver<'a> { return self.graph_root; } }; - let module = self.get_module(DefId { index: CRATE_DEF_INDEX, ..module.normal_ancestor_id }); + let module = self.get_module(DefId { index: CRATE_DEF_INDEX, ..module.nearest_parent_mod }); debug!( "resolve_crate_root({:?}): got module {:?} ({:?}) (ident.span = {:?})", ident, @@ -2128,10 +2161,10 @@ impl<'a> Resolver<'a> { } fn resolve_self(&mut self, ctxt: &mut SyntaxContext, module: Module<'a>) -> Module<'a> { - let mut module = self.get_module(module.normal_ancestor_id); + let mut module = self.get_module(module.nearest_parent_mod); while module.span.ctxt().normalize_to_macros_2_0() != *ctxt { let parent = module.parent.unwrap_or_else(|| self.macro_def_scope(ctxt.remove_mark())); - module = self.get_module(parent.normal_ancestor_id); + module = self.get_module(parent.nearest_parent_mod); } module } @@ -2540,6 +2573,7 @@ impl<'a> Resolver<'a> { mut res: Res, record_used: bool, span: Span, + original_rib_ident_def: Ident, all_ribs: &[Rib<'a>], ) -> Res { const CG_BUG_STR: &str = "min_const_generics resolve check didn't stop compilation"; @@ -2586,10 +2620,32 @@ impl<'a> Resolver<'a> { res_err = Some(CannotCaptureDynamicEnvironmentInFnItem); } } - ConstantItemRibKind(_) => { + ConstantItemRibKind(_, item) => { // Still doesn't deal with upvars if record_used { - self.report_error(span, AttemptToUseNonConstantValueInConstant); + let (span, resolution_error) = + if let Some((ident, constant_item_kind)) = item { + let kind_str = match constant_item_kind { + ConstantItemKind::Const => "const", + ConstantItemKind::Static => "static", + }; + ( + span, + AttemptToUseNonConstantValueInConstant( + ident, "let", kind_str, + ), + ) + } else { + ( + rib_ident.span, + AttemptToUseNonConstantValueInConstant( + original_rib_ident_def, + "const", + "let", + ), + ) + }; + self.report_error(span, resolution_error); } return Res::Err; } @@ -2625,7 +2681,7 @@ impl<'a> Resolver<'a> { in_ty_param_default = true; continue; } - ConstantItemRibKind(trivial) => { + ConstantItemRibKind(trivial, _) => { let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. if !(trivial @@ -2718,7 +2774,7 @@ impl<'a> Resolver<'a> { in_ty_param_default = true; continue; } - ConstantItemRibKind(trivial) => { + ConstantItemRibKind(trivial, _) => { let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. if !(trivial @@ -2793,7 +2849,7 @@ impl<'a> Resolver<'a> { } fn is_accessible_from(&self, vis: ty::Visibility, module: Module<'a>) -> bool { - vis.is_accessible_from(module.normal_ancestor_id, self) + vis.is_accessible_from(module.nearest_parent_mod, self) } fn set_binding_parent_module(&mut self, binding: &'a NameBinding<'a>, module: Module<'a>) { @@ -2817,7 +2873,7 @@ impl<'a> Resolver<'a> { self.binding_parent_modules.get(&PtrKey(modularized)), ) { (Some(macro_rules), Some(modularized)) => { - macro_rules.normal_ancestor_id == modularized.normal_ancestor_id + macro_rules.nearest_parent_mod == modularized.nearest_parent_mod && modularized.is_ancestor_of(macro_rules) } _ => false, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 5ad7c83ca36..5c74094ecd3 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -14,7 +14,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::ptr_key::PtrKey; use rustc_data_structures::sync::Lrc; use rustc_errors::struct_span_err; -use rustc_expand::base::{Indeterminate, InvocationRes, ResolverExpand, SyntaxExtension}; +use rustc_expand::base::{Indeterminate, InvocationRes, ResolverExpand}; +use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::compile_declarative_macro; use rustc_expand::expand::{AstFragment, Invocation, InvocationKind}; use rustc_feature::is_builtin_attr_name; @@ -176,10 +177,11 @@ impl<'a> ResolverExpand for Resolver<'a> { parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion); } - fn register_builtin_macro(&mut self, ident: Ident, ext: SyntaxExtension) { - if self.builtin_macros.insert(ident.name, BuiltinMacroState::NotYetSeen(ext)).is_some() { + fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind) { + if self.builtin_macros.insert(name, BuiltinMacroState::NotYetSeen(ext)).is_some() { self.session - .span_err(ident.span, &format!("built-in macro `{}` was already defined", ident)); + .diagnostic() + .bug(&format!("built-in macro `{}` was already registered", name)); } } @@ -285,7 +287,7 @@ impl<'a> ResolverExpand for Resolver<'a> { helper_attrs.extend( ext.helper_attrs.iter().map(|name| Ident::new(*name, span)), ); - if ext.is_derive_copy { + if ext.builtin_name == Some(sym::Copy) { self.containers_deriving_copy.insert(invoc_id); } ext @@ -328,7 +330,7 @@ impl<'a> ResolverExpand for Resolver<'a> { if after_derive { self.session.span_err(span, "macro attributes must be placed before `#[derive]`"); } - let normal_module_def_id = self.macro_def_scope(invoc_id).normal_ancestor_id; + let normal_module_def_id = self.macro_def_scope(invoc_id).nearest_parent_mod; self.definitions.add_parent_module_of_macro_def(invoc_id, normal_module_def_id); } @@ -618,8 +620,9 @@ impl<'a> Resolver<'a> { let break_result = self.visit_scopes( scope_set, parent_scope, - orig_ident, - |this, scope, use_prelude, ident| { + orig_ident.span.ctxt(), + |this, scope, use_prelude, ctxt| { + let ident = Ident::new(orig_ident.name, orig_ident.span.with_ctxt(ctxt)); let ok = |res, span, arenas| { Ok(( (res, ty::Visibility::Public, span, ExpnId::root()).to_name_binding(arenas), @@ -1089,14 +1092,14 @@ impl<'a> Resolver<'a> { edition, ); - if result.is_builtin { + if let Some(builtin_name) = result.builtin_name { // The macro was marked with `#[rustc_builtin_macro]`. - if let Some(builtin_macro) = self.builtin_macros.get_mut(&item.ident.name) { + if let Some(builtin_macro) = self.builtin_macros.get_mut(&builtin_name) { // The macro is a built-in, replace its expander function // while still taking everything else from the source code. // If we already loaded this builtin macro, give a better error message than 'no such builtin macro'. match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) { - BuiltinMacroState::NotYetSeen(ext) => result.kind = ext.kind, + BuiltinMacroState::NotYetSeen(ext) => result.kind = ext, BuiltinMacroState::AlreadySeen(span) => { struct_span_err!( self.session, diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index 987badcedde..617a28ed519 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -582,7 +582,7 @@ impl<'tcx> DumpVisitor<'tcx> { } ref v => { let mut value = format!("{}::{}", enum_data.name, name); - if let &hir::VariantData::Tuple(ref fields, _) = v { + if let hir::VariantData::Tuple(fields, _) = v { value.push('('); value.push_str( &fields @@ -653,7 +653,7 @@ impl<'tcx> DumpVisitor<'tcx> { let map = &self.tcx.hir(); self.nest_typeck_results(map.local_def_id(item.hir_id), |v| { v.visit_ty(&typ); - if let &Some(ref trait_ref) = trait_ref { + if let Some(trait_ref) = trait_ref { v.process_path(trait_ref.hir_ref_id, &hir::QPath::Resolved(None, &trait_ref.path)); } v.process_generic_params(generics, "", item.hir_id); @@ -1082,7 +1082,7 @@ impl<'tcx> DumpVisitor<'tcx> { ); } - if let &Some(ref default_ty) = default_ty { + if let Some(default_ty) = default_ty { self.visit_ty(default_ty) } } diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs index e7d1c9d3bbe..ab3da270fe6 100644 --- a/compiler/rustc_save_analysis/src/sig.rs +++ b/compiler/rustc_save_analysis/src/sig.rs @@ -619,7 +619,6 @@ impl<'hir> Sig for hir::Generics<'hir> { param_text.push_str(&ty_to_string(&ty)); if let Some(ref _default) = default { // FIXME(const_generics_defaults): push the `default` value here - todo!(); } } if !param.bounds.is_empty() { diff --git a/compiler/rustc_serialize/src/collection_impls.rs b/compiler/rustc_serialize/src/collection_impls.rs index 3d274cb0150..ae6d27e037b 100644 --- a/compiler/rustc_serialize/src/collection_impls.rs +++ b/compiler/rustc_serialize/src/collection_impls.rs @@ -11,12 +11,8 @@ use smallvec::{Array, SmallVec}; impl<S: Encoder, A: Array<Item: Encodable<S>>> Encodable<S> for SmallVec<A> { fn encode(&self, s: &mut S) -> Result<(), S::Error> { - s.emit_seq(self.len(), |s| { - for (i, e) in self.iter().enumerate() { - s.emit_seq_elt(i, |s| e.encode(s))?; - } - Ok(()) - }) + let slice: &[A::Item] = self; + slice.encode(s) } } @@ -292,46 +288,28 @@ where impl<E: Encoder, T: Encodable<E>> Encodable<E> for Rc<[T]> { fn encode(&self, s: &mut E) -> Result<(), E::Error> { - s.emit_seq(self.len(), |s| { - for (index, e) in self.iter().enumerate() { - s.emit_seq_elt(index, |s| e.encode(s))?; - } - Ok(()) - }) + let slice: &[T] = self; + slice.encode(s) } } impl<D: Decoder, T: Decodable<D>> Decodable<D> for Rc<[T]> { fn decode(d: &mut D) -> Result<Rc<[T]>, D::Error> { - d.read_seq(|d, len| { - let mut vec = Vec::with_capacity(len); - for index in 0..len { - vec.push(d.read_seq_elt(index, |d| Decodable::decode(d))?); - } - Ok(vec.into()) - }) + let vec: Vec<T> = Decodable::decode(d)?; + Ok(vec.into()) } } impl<E: Encoder, T: Encodable<E>> Encodable<E> for Arc<[T]> { fn encode(&self, s: &mut E) -> Result<(), E::Error> { - s.emit_seq(self.len(), |s| { - for (index, e) in self.iter().enumerate() { - s.emit_seq_elt(index, |s| e.encode(s))?; - } - Ok(()) - }) + let slice: &[T] = self; + slice.encode(s) } } impl<D: Decoder, T: Decodable<D>> Decodable<D> for Arc<[T]> { fn decode(d: &mut D) -> Result<Arc<[T]>, D::Error> { - d.read_seq(|d, len| { - let mut vec = Vec::with_capacity(len); - for index in 0..len { - vec.push(d.read_seq_elt(index, |d| Decodable::decode(d))?); - } - Ok(vec.into()) - }) + let vec: Vec<T> = Decodable::decode(d)?; + Ok(vec.into()) } } diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index ac1cdc6ad45..f58ed14d997 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -14,6 +14,8 @@ Core encoding and decoding interfaces. #![feature(nll)] #![feature(associated_type_bounds)] #![cfg_attr(bootstrap, feature(min_const_generics))] +#![feature(min_specialization)] +#![feature(vec_spare_capacity)] #![cfg_attr(test, feature(test))] #![allow(rustc::internal)] diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index 8b79c93e760..673742df7f0 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -1,6 +1,8 @@ use crate::leb128::{self, read_signed_leb128, write_signed_leb128}; use crate::serialize; use std::borrow::Cow; +use std::mem::MaybeUninit; +use std::ptr; // ----------------------------------------------------------------------------- // Encoder @@ -179,11 +181,19 @@ impl<'a> Decoder<'a> { } #[inline] - pub fn read_raw_bytes(&mut self, s: &mut [u8]) -> Result<(), String> { + pub fn read_raw_bytes(&mut self, s: &mut [MaybeUninit<u8>]) -> Result<(), String> { let start = self.position; let end = start + s.len(); + assert!(end <= self.data.len()); - s.copy_from_slice(&self.data[start..end]); + // SAFETY: Both `src` and `dst` point to at least `s.len()` elements: + // `src` points to at least `s.len()` elements by above assert, and + // `dst` points to `s.len()` elements by derivation from `s`. + unsafe { + let src = self.data.as_ptr().add(start); + let dst = s.as_mut_ptr() as *mut u8; + ptr::copy_nonoverlapping(src, dst, s.len()); + } self.position = end; @@ -316,3 +326,36 @@ impl<'a> serialize::Decoder for Decoder<'a> { err.to_string() } } + +// Specializations for contiguous byte sequences follow. The default implementations for slices +// encode and decode each element individually. This isn't necessary for `u8` slices when using +// opaque encoders and decoders, because each `u8` is unchanged by encoding and decoding. +// Therefore, we can use more efficient implementations that process the entire sequence at once. + +// Specialize encoding byte slices. This specialization also applies to encoding `Vec<u8>`s, etc., +// since the default implementations call `encode` on their slices internally. +impl serialize::Encodable<Encoder> for [u8] { + fn encode(&self, e: &mut Encoder) -> EncodeResult { + serialize::Encoder::emit_usize(e, self.len())?; + e.emit_raw_bytes(self); + Ok(()) + } +} + +// Specialize decoding `Vec<u8>`. This specialization also applies to decoding `Box<[u8]>`s, etc., +// since the default implementations call `decode` to produce a `Vec<u8>` internally. +impl<'a> serialize::Decodable<Decoder<'a>> for Vec<u8> { + fn decode(d: &mut Decoder<'a>) -> Result<Self, String> { + let len = serialize::Decoder::read_usize(d)?; + + let mut v = Vec::with_capacity(len); + let buf = &mut v.spare_capacity_mut()[..len]; + d.read_raw_bytes(buf)?; + + unsafe { + v.set_len(len); + } + + Ok(v) + } +} diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index aa305f3c7fc..47aad5b88c6 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -527,7 +527,7 @@ impl<D: Decoder, T: Decodable<D>> Decodable<D> for Rc<T> { } impl<S: Encoder, T: Encodable<S>> Encodable<S> for [T] { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { + default fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_seq(self.len(), |s| { for (i, e) in self.iter().enumerate() { s.emit_seq_elt(i, |s| e.encode(s))? @@ -545,7 +545,7 @@ impl<S: Encoder, T: Encodable<S>> Encodable<S> for Vec<T> { } impl<D: Decoder, T: Decodable<D>> Decodable<D> for Vec<T> { - fn decode(d: &mut D) -> Result<Vec<T>, D::Error> { + default fn decode(d: &mut D) -> Result<Vec<T>, D::Error> { d.read_seq(|d, len| { let mut v = Vec::with_capacity(len); for i in 0..len { @@ -591,13 +591,8 @@ where [T]: ToOwned<Owned = Vec<T>>, { fn decode(d: &mut D) -> Result<Cow<'static, [T]>, D::Error> { - d.read_seq(|d, len| { - let mut v = Vec::with_capacity(len); - for i in 0..len { - v.push(d.read_seq_elt(i, |d| Decodable::decode(d))?); - } - Ok(Cow::Owned(v)) - }) + let v: Vec<T> = Decodable::decode(d)?; + Ok(Cow::Owned(v)) } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 62859f4bef4..0cafdec1495 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -819,7 +819,7 @@ pub fn default_configuration(sess: &Session) -> CrateConfig { } } ret.insert((sym::target_arch, Some(Symbol::intern(arch)))); - ret.insert((sym::target_endian, Some(Symbol::intern(end)))); + ret.insert((sym::target_endian, Some(Symbol::intern(end.as_str())))); ret.insert((sym::target_pointer_width, Some(Symbol::intern(&wordsz)))); ret.insert((sym::target_env, Some(Symbol::intern(env)))); ret.insert((sym::target_vendor, Some(Symbol::intern(vendor)))); @@ -1829,11 +1829,17 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { } if debugging_opts.mir_opt_level > 1 { + // Functions inlined during MIR transform can, at best, make it impossible to + // effectively cover inlined functions, and, at worst, break coverage map generation + // during LLVM codegen. For example, function counter IDs are only unique within a + // function. Inlining after these counters are injected can produce duplicate counters, + // resulting in an invalid coverage map (and ICE); so this option combination is not + // allowed. early_warn( error_format, &format!( - "`-Z mir-opt-level={}` (any level > 1) enables function inlining, which \ - limits the effectiveness of `-Z instrument-coverage`.", + "`-Z mir-opt-level={}` (or any level > 1) enables function inlining, which \ + is incompatible with `-Z instrument-coverage`. Inlining will be disabled.", debugging_opts.mir_opt_level, ), ); @@ -2166,6 +2172,7 @@ crate mod dep_tracking { SymbolManglingVersion, TrimmedDefPaths, }; use crate::lint; + use crate::options::WasiExecModel; use crate::utils::NativeLibKind; use rustc_feature::UnstableFeatures; use rustc_span::edition::Edition; @@ -2221,6 +2228,7 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(Option<RelocModel>); impl_dep_tracking_hash_via_hash!(Option<CodeModel>); impl_dep_tracking_hash_via_hash!(Option<TlsModel>); + impl_dep_tracking_hash_via_hash!(Option<WasiExecModel>); impl_dep_tracking_hash_via_hash!(Option<PanicStrategy>); impl_dep_tracking_hash_via_hash!(Option<RelroLevel>); impl_dep_tracking_hash_via_hash!(Option<lint::Level>); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 81f79f4b0e0..30af65e49a0 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -279,6 +279,7 @@ macro_rules! options { pub const parse_tls_model: &str = "one of supported TLS models (`rustc --print tls-models`)"; pub const parse_target_feature: &str = parse_string; + pub const parse_wasi_exec_model: &str = "either `command` or `reactor`"; } #[allow(dead_code)] @@ -722,6 +723,15 @@ macro_rules! options { None => false, } } + + fn parse_wasi_exec_model(slot: &mut Option<WasiExecModel>, v: Option<&str>) -> bool { + match v { + Some("command") => *slot = Some(WasiExecModel::Command), + Some("reactor") => *slot = Some(WasiExecModel::Reactor), + _ => return false, + } + true + } } ) } @@ -1166,9 +1176,17 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "in general, enable more debug printouts (default: no)"), verify_llvm_ir: bool = (false, parse_bool, [TRACKED], "verify LLVM IR (default: no)"), + wasi_exec_model: Option<WasiExecModel> = (None, parse_wasi_exec_model, [TRACKED], + "whether to build a wasi command or reactor"), // This list is in alphabetical order. // // If you add a new option, please update: - // - src/librustc_interface/tests.rs + // - compiler/rustc_interface/src/tests.rs +} + +#[derive(Clone, Hash)] +pub enum WasiExecModel { + Command, + Reactor, } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3a420f5f9de..1f9a1af0f68 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -796,6 +796,14 @@ impl Session { self.opts.debugging_opts.tls_model.unwrap_or(self.target.tls_model) } + pub fn is_wasi_reactor(&self) -> bool { + self.target.options.os == "wasi" + && matches!( + self.opts.debugging_opts.wasi_exec_model, + Some(config::WasiExecModel::Reactor) + ) + } + pub fn must_not_eliminate_frame_pointers(&self) -> bool { // "mcount" function relies on stack pointer. // See <https://sourceware.org/binutils/docs/gprof/Implementation.html>. @@ -1522,6 +1530,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } const ASAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-apple-darwin", "aarch64-fuchsia", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", @@ -1529,11 +1538,16 @@ fn validate_commandline_args_with_session_available(sess: &Session) { "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu", ]; - const LSAN_SUPPORTED_TARGETS: &[&str] = - &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; + const LSAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu", + ]; const MSAN_SUPPORTED_TARGETS: &[&str] = &["aarch64-unknown-linux-gnu", "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu"]; const TSAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-freebsd", diff --git a/compiler/rustc_span/src/caching_source_map_view.rs b/compiler/rustc_span/src/caching_source_map_view.rs index 15dd00fb483..8e21b9ff44a 100644 --- a/compiler/rustc_span/src/caching_source_map_view.rs +++ b/compiler/rustc_span/src/caching_source_map_view.rs @@ -1,5 +1,5 @@ use crate::source_map::SourceMap; -use crate::{BytePos, SourceFile}; +use crate::{BytePos, SourceFile, SpanData}; use rustc_data_structures::sync::Lrc; use std::ops::Range; @@ -24,6 +24,32 @@ struct CacheEntry { file_index: usize, } +impl CacheEntry { + #[inline] + fn update( + &mut self, + new_file_and_idx: Option<(Lrc<SourceFile>, usize)>, + pos: BytePos, + time_stamp: usize, + ) { + if let Some((file, file_idx)) = new_file_and_idx { + self.file = file; + self.file_index = file_idx; + } + + let line_index = self.file.lookup_line(pos).unwrap(); + let line_bounds = self.file.line_bounds(line_index); + self.line_number = line_index + 1; + self.line = line_bounds; + self.touch(time_stamp); + } + + #[inline] + fn touch(&mut self, time_stamp: usize) { + self.time_stamp = time_stamp; + } +} + #[derive(Clone)] pub struct CachingSourceMapView<'sm> { source_map: &'sm SourceMap, @@ -57,59 +83,202 @@ impl<'sm> CachingSourceMapView<'sm> { self.time_stamp += 1; // Check if the position is in one of the cached lines - for cache_entry in self.line_cache.iter_mut() { - if cache_entry.line.contains(&pos) { - cache_entry.time_stamp = self.time_stamp; + let cache_idx = self.cache_entry_index(pos); + if cache_idx != -1 { + let cache_entry = &mut self.line_cache[cache_idx as usize]; + cache_entry.touch(self.time_stamp); - return Some(( - cache_entry.file.clone(), - cache_entry.line_number, - pos - cache_entry.line.start, - )); - } + return Some(( + cache_entry.file.clone(), + cache_entry.line_number, + pos - cache_entry.line.start, + )); } // No cache hit ... - let mut oldest = 0; - for index in 1..self.line_cache.len() { - if self.line_cache[index].time_stamp < self.line_cache[oldest].time_stamp { - oldest = index; - } - } + let oldest = self.oldest_cache_entry_index(); + + // If the entry doesn't point to the correct file, get the new file and index. + let new_file_and_idx = if !file_contains(&self.line_cache[oldest].file, pos) { + Some(self.file_for_position(pos)?) + } else { + None + }; let cache_entry = &mut self.line_cache[oldest]; + cache_entry.update(new_file_and_idx, pos, self.time_stamp); - // If the entry doesn't point to the correct file, fix it up - if !file_contains(&cache_entry.file, pos) { - let file_valid; - if self.source_map.files().len() > 0 { - let file_index = self.source_map.lookup_source_file_idx(pos); - let file = self.source_map.files()[file_index].clone(); - - if file_contains(&file, pos) { - cache_entry.file = file; - cache_entry.file_index = file_index; - file_valid = true; - } else { - file_valid = false; + Some((cache_entry.file.clone(), cache_entry.line_number, pos - cache_entry.line.start)) + } + + pub fn span_data_to_lines_and_cols( + &mut self, + span_data: &SpanData, + ) -> Option<(Lrc<SourceFile>, usize, BytePos, usize, BytePos)> { + self.time_stamp += 1; + + // Check if lo and hi are in the cached lines. + let lo_cache_idx = self.cache_entry_index(span_data.lo); + let hi_cache_idx = self.cache_entry_index(span_data.hi); + + if lo_cache_idx != -1 && hi_cache_idx != -1 { + // Cache hit for span lo and hi. Check if they belong to the same file. + let result = { + let lo = &self.line_cache[lo_cache_idx as usize]; + let hi = &self.line_cache[hi_cache_idx as usize]; + + if lo.file_index != hi.file_index { + return None; } - } else { - file_valid = false; + + ( + lo.file.clone(), + lo.line_number, + span_data.lo - lo.line.start, + hi.line_number, + span_data.hi - hi.line.start, + ) + }; + + self.line_cache[lo_cache_idx as usize].touch(self.time_stamp); + self.line_cache[hi_cache_idx as usize].touch(self.time_stamp); + + return Some(result); + } + + // No cache hit or cache hit for only one of span lo and hi. + let oldest = if lo_cache_idx != -1 || hi_cache_idx != -1 { + let avoid_idx = if lo_cache_idx != -1 { lo_cache_idx } else { hi_cache_idx }; + self.oldest_cache_entry_index_avoid(avoid_idx as usize) + } else { + self.oldest_cache_entry_index() + }; + + // If the entry doesn't point to the correct file, get the new file and index. + // Return early if the file containing beginning of span doesn't contain end of span. + let new_file_and_idx = if !file_contains(&self.line_cache[oldest].file, span_data.lo) { + let new_file_and_idx = self.file_for_position(span_data.lo)?; + if !file_contains(&new_file_and_idx.0, span_data.hi) { + return None; } - if !file_valid { + Some(new_file_and_idx) + } else { + let file = &self.line_cache[oldest].file; + if !file_contains(&file, span_data.hi) { return None; } + + None + }; + + // Update the cache entries. + let (lo_idx, hi_idx) = match (lo_cache_idx, hi_cache_idx) { + // Oldest cache entry is for span_data.lo line. + (-1, -1) => { + let lo = &mut self.line_cache[oldest]; + lo.update(new_file_and_idx, span_data.lo, self.time_stamp); + + if !lo.line.contains(&span_data.hi) { + let new_file_and_idx = Some((lo.file.clone(), lo.file_index)); + let next_oldest = self.oldest_cache_entry_index_avoid(oldest); + let hi = &mut self.line_cache[next_oldest]; + hi.update(new_file_and_idx, span_data.hi, self.time_stamp); + (oldest, next_oldest) + } else { + (oldest, oldest) + } + } + // Oldest cache entry is for span_data.lo line. + (-1, _) => { + let lo = &mut self.line_cache[oldest]; + lo.update(new_file_and_idx, span_data.lo, self.time_stamp); + let hi = &mut self.line_cache[hi_cache_idx as usize]; + hi.touch(self.time_stamp); + (oldest, hi_cache_idx as usize) + } + // Oldest cache entry is for span_data.hi line. + (_, -1) => { + let hi = &mut self.line_cache[oldest]; + hi.update(new_file_and_idx, span_data.hi, self.time_stamp); + let lo = &mut self.line_cache[lo_cache_idx as usize]; + lo.touch(self.time_stamp); + (lo_cache_idx as usize, oldest) + } + _ => { + panic!(); + } + }; + + let lo = &self.line_cache[lo_idx]; + let hi = &self.line_cache[hi_idx]; + + // Span lo and hi may equal line end when last line doesn't + // end in newline, hence the inclusive upper bounds below. + debug_assert!(span_data.lo >= lo.line.start); + debug_assert!(span_data.lo <= lo.line.end); + debug_assert!(span_data.hi >= hi.line.start); + debug_assert!(span_data.hi <= hi.line.end); + debug_assert!(lo.file.contains(span_data.lo)); + debug_assert!(lo.file.contains(span_data.hi)); + debug_assert_eq!(lo.file_index, hi.file_index); + + Some(( + lo.file.clone(), + lo.line_number, + span_data.lo - lo.line.start, + hi.line_number, + span_data.hi - hi.line.start, + )) + } + + fn cache_entry_index(&self, pos: BytePos) -> isize { + for (idx, cache_entry) in self.line_cache.iter().enumerate() { + if cache_entry.line.contains(&pos) { + return idx as isize; + } + } + + -1 + } + + fn oldest_cache_entry_index(&self) -> usize { + let mut oldest = 0; + + for idx in 1..self.line_cache.len() { + if self.line_cache[idx].time_stamp < self.line_cache[oldest].time_stamp { + oldest = idx; + } } - let line_index = cache_entry.file.lookup_line(pos).unwrap(); - let line_bounds = cache_entry.file.line_bounds(line_index); + oldest + } - cache_entry.line_number = line_index + 1; - cache_entry.line = line_bounds; - cache_entry.time_stamp = self.time_stamp; + fn oldest_cache_entry_index_avoid(&self, avoid_idx: usize) -> usize { + let mut oldest = if avoid_idx != 0 { 0 } else { 1 }; - Some((cache_entry.file.clone(), cache_entry.line_number, pos - cache_entry.line.start)) + for idx in 0..self.line_cache.len() { + if idx != avoid_idx + && self.line_cache[idx].time_stamp < self.line_cache[oldest].time_stamp + { + oldest = idx; + } + } + + oldest + } + + fn file_for_position(&self, pos: BytePos) -> Option<(Lrc<SourceFile>, usize)> { + if !self.source_map.files().is_empty() { + let file_idx = self.source_map.lookup_source_file_idx(pos); + let file = &self.source_map.files()[file_idx]; + + if file_contains(file, pos) { + return Some((file.clone(), file_idx)); + } + } + + None } } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index fdc0d225bb8..1fb5642912f 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -622,6 +622,10 @@ impl SyntaxContext { pub fn dollar_crate_name(self) -> Symbol { HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name) } + + pub fn edition(self) -> Edition { + self.outer_expn_data().edition + } } impl fmt::Debug for SyntaxContext { @@ -650,6 +654,20 @@ impl Span { self.with_ctxt(data.apply_mark(SyntaxContext::root(), expn_id, transparency)) }) } + + /// Reuses the span but adds information like the kind of the desugaring and features that are + /// allowed inside this span. + pub fn mark_with_reason( + self, + allow_internal_unstable: Option<Lrc<[Symbol]>>, + reason: DesugaringKind, + edition: Edition, + ) -> Span { + self.fresh_expansion(ExpnData { + allow_internal_unstable, + ..ExpnData::default(ExpnKind::Desugaring(reason), self, edition, None) + }) + } } /// A subset of properties from both macro definition and macro call available through global data. @@ -699,7 +717,7 @@ pub struct ExpnData { /// created locally - when our serialized metadata is decoded, /// foreign `ExpnId`s will have their `ExpnData` looked up /// from the crate specified by `Crate - pub krate: CrateNum, + krate: CrateNum, /// The raw that this `ExpnData` had in its original crate. /// An `ExpnData` can be created before being assigned an `ExpnId`, /// so this might be `None` until `set_expn_data` is called @@ -707,13 +725,39 @@ pub struct ExpnData { // two `ExpnData`s that differ only in their `orig_id` should // be considered equivalent. #[stable_hasher(ignore)] - pub orig_id: Option<u32>, + orig_id: Option<u32>, } // This would require special handling of `orig_id` and `parent` impl !PartialEq for ExpnData {} impl ExpnData { + pub fn new( + kind: ExpnKind, + parent: ExpnId, + call_site: Span, + def_site: Span, + allow_internal_unstable: Option<Lrc<[Symbol]>>, + allow_internal_unsafe: bool, + local_inner_macros: bool, + edition: Edition, + macro_def_id: Option<DefId>, + ) -> ExpnData { + ExpnData { + kind, + parent, + call_site, + def_site, + allow_internal_unstable, + allow_internal_unsafe, + local_inner_macros, + edition, + macro_def_id, + krate: LOCAL_CRATE, + orig_id: None, + } + } + /// Constructs expansion data with default properties. pub fn default( kind: ExpnKind, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 99f01062545..f0f9f940446 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -301,6 +301,10 @@ pub struct SpanData { impl SpanData { #[inline] + pub fn span(&self) -> Span { + Span::new(self.lo, self.hi, self.ctxt) + } + #[inline] pub fn with_lo(&self, lo: BytePos) -> Span { Span::new(lo, self.hi, self.ctxt) } @@ -468,7 +472,7 @@ impl Span { /// Edition of the crate from which this span came. pub fn edition(self) -> edition::Edition { - self.ctxt().outer_expn_data().edition + self.ctxt().edition() } #[inline] @@ -1867,6 +1871,10 @@ pub trait HashStableContext { &mut self, byte: BytePos, ) -> Option<(Lrc<SourceFile>, usize, BytePos)>; + fn span_data_to_lines_and_cols( + &mut self, + span: &SpanData, + ) -> Option<(Lrc<SourceFile>, usize, BytePos, usize, BytePos)>; } impl<CTX> HashStable<CTX> for Span @@ -1900,22 +1908,8 @@ where // position that belongs to it, as opposed to hashing the first // position past it. let span = self.data(); - let (file_lo, line_lo, col_lo) = match ctx.byte_pos_to_line_and_col(span.lo) { - Some(pos) => pos, - None => { - Hash::hash(&TAG_INVALID_SPAN, hasher); - span.ctxt.hash_stable(ctx, hasher); - return; - } - }; - - if !file_lo.contains(span.hi) { - Hash::hash(&TAG_INVALID_SPAN, hasher); - span.ctxt.hash_stable(ctx, hasher); - return; - } - - let (_, line_hi, col_hi) = match ctx.byte_pos_to_line_and_col(span.hi) { + let (file, line_lo, col_lo, line_hi, col_hi) = match ctx.span_data_to_lines_and_cols(&span) + { Some(pos) => pos, None => { Hash::hash(&TAG_INVALID_SPAN, hasher); @@ -1927,7 +1921,7 @@ where Hash::hash(&TAG_VALID_SPAN, hasher); // We truncate the stable ID hash and line and column numbers. The chances // of causing a collision this way should be minimal. - Hash::hash(&(file_lo.name_hash as u64), hasher); + Hash::hash(&(file.name_hash as u64), hasher); // Hash both the length and the end location (line/column) of a span. If we // hash only the length, for example, then two otherwise equal spans with diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index fefc0cb48dd..6635d44496c 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -671,7 +671,9 @@ impl SourceMap { let pat = pat.to_owned() + ws; if let Ok(prev_source) = self.span_to_prev_source(sp) { let prev_source = prev_source.rsplit(&pat).next().unwrap_or("").trim_start(); - if !prev_source.is_empty() && (!prev_source.contains('\n') || accept_newlines) { + if prev_source.is_empty() && sp.lo().0 != 0 { + return sp.with_lo(BytePos(sp.lo().0 - 1)); + } else if !prev_source.contains('\n') || accept_newlines { return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32)); } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 64b50a9b70a..b6cf584d875 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -381,6 +381,7 @@ symbols! { const_ptr, const_raw_ptr_deref, const_raw_ptr_to_usize_cast, + const_refs_to_cell, const_slice_ptr, const_trait_bound_opt_out, const_trait_impl, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 6356a7e7832..b0d5f340902 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -56,7 +56,15 @@ pub(super) fn mangle( let hash = get_symbol_hash(tcx, instance, instance_ty, instantiating_crate); let mut printer = SymbolPrinter { tcx, path: SymbolPath::new(), keep_within_component: false } - .print_def_path(def_id, &[]) + .print_def_path( + def_id, + if let ty::InstanceDef::DropGlue(_, _) = instance.def { + // Add the name of the dropped type to the symbol name + &*instance.substs + } else { + &[] + }, + ) .unwrap(); if let ty::InstanceDef::VtableShim(..) = instance.def { diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 5de9a8dfa7a..a9e595d11e7 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -605,10 +605,11 @@ impl<'a, Ty> FnAbi<'a, Ty> { "nvptx64" => nvptx64::compute_abi_info(self), "hexagon" => hexagon::compute_abi_info(self), "riscv32" | "riscv64" => riscv::compute_abi_info(cx, self), - "wasm32" if cx.target_spec().os != "emscripten" => { - wasm32_bindgen_compat::compute_abi_info(self) - } - "wasm32" | "asmjs" => wasm32::compute_abi_info(cx, self), + "wasm32" => match cx.target_spec().os.as_str() { + "emscripten" | "wasi" => wasm32::compute_abi_info(cx, self), + _ => wasm32_bindgen_compat::compute_abi_info(self), + }, + "asmjs" => wasm32::compute_abi_info(cx, self), a => return Err(format!("unrecognized arch \"{}\" in target specification", a)), } diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index a43080b09e9..93868bed9b9 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -4,11 +4,14 @@ pub use Primitive::*; use crate::spec::Target; use std::convert::{TryFrom, TryInto}; +use std::fmt; use std::num::NonZeroUsize; use std::ops::{Add, AddAssign, Deref, Mul, Range, RangeInclusive, Sub}; +use std::str::FromStr; use rustc_index::vec::{Idx, IndexVec}; use rustc_macros::HashStable_Generic; +use rustc_serialize::json::{Json, ToJson}; use rustc_span::Span; pub mod call; @@ -152,22 +155,19 @@ impl TargetDataLayout { } // Perform consistency checks against the Target information. - let endian_str = match dl.endian { - Endian::Little => "little", - Endian::Big => "big", - }; - if endian_str != target.endian { + if dl.endian != target.endian { return Err(format!( "inconsistent target specification: \"data-layout\" claims \ - architecture is {}-endian, while \"target-endian\" is `{}`", - endian_str, target.endian + architecture is {}-endian, while \"target-endian\" is `{}`", + dl.endian.as_str(), + target.endian.as_str(), )); } if dl.pointer_size.bits() != target.pointer_width.into() { return Err(format!( "inconsistent target specification: \"data-layout\" claims \ - pointers are {}-bit, while \"target-pointer-width\" is `{}`", + pointers are {}-bit, while \"target-pointer-width\" is `{}`", dl.pointer_size.bits(), target.pointer_width )); @@ -234,26 +234,75 @@ pub enum Endian { Big, } +impl Endian { + pub fn as_str(&self) -> &'static str { + match self { + Self::Little => "little", + Self::Big => "big", + } + } +} + +impl fmt::Debug for Endian { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl FromStr for Endian { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "little" => Ok(Self::Little), + "big" => Ok(Self::Big), + _ => Err(format!(r#"unknown endian: "{}""#, s)), + } + } +} + +impl ToJson for Endian { + fn to_json(&self) -> Json { + self.as_str().to_json() + } +} + /// Size of a type in bytes. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] #[derive(HashStable_Generic)] pub struct Size { + // The top 3 bits are ALWAYS zero. raw: u64, } impl Size { pub const ZERO: Size = Size { raw: 0 }; - #[inline] + /// Rounds `bits` up to the next-higher byte boundary, if `bits` is + /// is not aligned. pub fn from_bits(bits: impl TryInto<u64>) -> Size { let bits = bits.try_into().ok().unwrap(); + + #[cold] + fn overflow(bits: u64) -> ! { + panic!("Size::from_bits({}) has overflowed", bits); + } + + // This is the largest value of `bits` that does not cause overflow + // during rounding, and guarantees that the resulting number of bytes + // cannot cause overflow when multiplied by 8. + if bits > 0xffff_ffff_ffff_fff8 { + overflow(bits); + } + // Avoid potential overflow from `bits + 7`. - Size::from_bytes(bits / 8 + ((bits % 8) + 7) / 8) + Size { raw: bits / 8 + ((bits % 8) + 7) / 8 } } #[inline] pub fn from_bytes(bytes: impl TryInto<u64>) -> Size { - Size { raw: bytes.try_into().ok().unwrap() } + let bytes: u64 = bytes.try_into().ok().unwrap(); + Size { raw: bytes } } #[inline] @@ -268,9 +317,7 @@ impl Size { #[inline] pub fn bits(self) -> u64 { - self.bytes().checked_mul(8).unwrap_or_else(|| { - panic!("Size::bits: {} bytes in bits doesn't fit in u64", self.bytes()) - }) + self.raw << 3 } #[inline] diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs index c6586b79b87..255740cb9c0 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs @@ -1,5 +1,6 @@ // Targets the Big endian Cortex-R4/R5 processor (ARMv7-R) +use crate::abi::Endian; use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; use crate::spec::{Target, TargetOptions}; @@ -11,7 +12,7 @@ pub fn target() -> Target { arch: "arm".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs index e3d4397f612..eb82e4d17b0 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs @@ -1,5 +1,6 @@ // Targets the Cortex-R4F/R5F processor (ARMv7-R) +use crate::abi::Endian; use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; use crate::spec::{Target, TargetOptions}; @@ -11,7 +12,7 @@ pub fn target() -> Target { arch: "arm".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index 76c0bf419e8..32da16a2d8c 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -5,15 +5,16 @@ //! The `crtx` ones are generally distributed with libc and the `begin/end` ones with gcc. //! See <https://dev.gentoo.org/~vapier/crt.txt> for some more details. //! -//! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi | -//! |----------------------|------------------------|------------------------|------------------|-------------------|------| -//! | dynamic-nopic-exe | crt1, crti, crtbegin | crt1, crti, crtbegin | crtbegin_dynamic | crt2, crtbegin | crt1 | -//! | dynamic-pic-exe | Scrt1, crti, crtbeginS | Scrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | -//! | static-nopic-exe | crt1, crti, crtbeginT | crt1, crti, crtbegin | crtbegin_static | crt2, crtbegin | crt1 | -//! | static-pic-exe | rcrt1, crti, crtbeginS | rcrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | -//! | dynamic-dylib | crti, crtbeginS | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | -//! | static-dylib (gcc) | crti, crtbeginT | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | -//! | static-dylib (clang) | crti, crtbeginT | N/A | crtbegin_static | dllcrt2, crtbegin | - | +//! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi | +//! |----------------------|------------------------|------------------------|------------------|-------------------|--------------| +//! | dynamic-nopic-exe | crt1, crti, crtbegin | crt1, crti, crtbegin | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | dynamic-pic-exe | Scrt1, crti, crtbeginS | Scrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | static-nopic-exe | crt1, crti, crtbeginT | crt1, crti, crtbegin | crtbegin_static | crt2, crtbegin | crt1 | +//! | static-pic-exe | rcrt1, crti, crtbeginS | rcrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | dynamic-dylib | crti, crtbeginS | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | +//! | static-dylib (gcc) | crti, crtbeginT | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | +//! | static-dylib (clang) | crti, crtbeginT | N/A | crtbegin_static | dllcrt2, crtbegin | - | +//! | wasi-reactor-exe | N/A | N/A | N/A | N/A | crt1-reactor | //! //! | Post-link CRT objects | glibc | musl | bionic | mingw | wasi | //! |-----------------------|---------------|---------------|----------------|--------|------| @@ -105,6 +106,7 @@ pub(super) fn pre_wasi_fallback() -> CrtObjects { (LinkOutputKind::DynamicPicExe, &["crt1.o"]), (LinkOutputKind::StaticNoPicExe, &["crt1.o"]), (LinkOutputKind::StaticPicExe, &["crt1.o"]), + (LinkOutputKind::WasiReactorExe, &["crt1-reactor.o"]), ]) } diff --git a/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs index daa0d9da172..53398539ac2 100644 --- a/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -7,7 +8,7 @@ pub fn target() -> Target { data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), arch: "mips64".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, // NOTE(mips64r2) matches C toolchain cpu: "mips64r2".to_string(), features: "+mips64r2".to_string(), diff --git a/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs index db8d0c04e6f..329fbd22721 100644 --- a/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -11,6 +12,6 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), arch: "mips64".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs index a7ec1f19c9d..b41b28cbc87 100644 --- a/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -7,7 +8,7 @@ pub fn target() -> Target { data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, cpu: "mips32r2".to_string(), features: "+mips32r2,+fpxx,+nooddspreg".to_string(), max_atomic_width: Some(32), diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs index 1ebe577bc1c..3713af43d73 100644 --- a/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -11,6 +12,6 @@ pub fn target() -> Target { pointer_width: 32, data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs index 2123d5e1a0f..042ec9140fa 100644 --- a/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs +++ b/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -7,7 +8,7 @@ pub fn target() -> Target { data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, cpu: "mips32r2".to_string(), features: "+mips32r2,+soft-float".to_string(), max_atomic_width: Some(32), diff --git a/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs index 11b3734a105..a81c90fe0cd 100644 --- a/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -7,7 +8,7 @@ pub fn target() -> Target { data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, cpu: "mips32r6".to_string(), features: "+mips32r6".to_string(), max_atomic_width: Some(32), diff --git a/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs index 6282c9e1d54..3bf837fbb41 100644 --- a/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { @@ -7,7 +8,7 @@ pub fn target() -> Target { data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), arch: "mips64".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, // NOTE(mips64r6) matches C toolchain cpu: "mips64r6".to_string(), features: "+mips64r6".to_string(), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 8d72df6850f..d283c254886 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -34,6 +34,7 @@ //! the target's settings, though `target-feature` and `link-args` will *add* //! to the list specified by the target, rather than replace. +use crate::abi::Endian; use crate::spec::abi::{lookup as lookup_abi, Abi}; use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; use rustc_serialize::json::{Json, ToJson}; @@ -407,6 +408,8 @@ pub enum LinkOutputKind { DynamicDylib, /// Dynamic library with bundled libc ("statically linked"). StaticDylib, + /// WASI module with a lifetime past the _initialize entry point + WasiReactorExe, } impl LinkOutputKind { @@ -418,6 +421,7 @@ impl LinkOutputKind { LinkOutputKind::StaticPicExe => "static-pic-exe", LinkOutputKind::DynamicDylib => "dynamic-dylib", LinkOutputKind::StaticDylib => "static-dylib", + LinkOutputKind::WasiReactorExe => "wasi-reactor-exe", } } @@ -429,6 +433,7 @@ impl LinkOutputKind { "static-pic-exe" => LinkOutputKind::StaticPicExe, "dynamic-dylib" => LinkOutputKind::DynamicDylib, "static-dylib" => LinkOutputKind::StaticDylib, + "wasi-reactor-exe" => LinkOutputKind::WasiReactorExe, _ => return None, }) } @@ -705,8 +710,8 @@ pub struct TargetOptions { /// Whether the target is built-in or loaded from a custom target specification. pub is_builtin: bool, - /// String to use as the `target_endian` `cfg` variable. Defaults to "little". - pub endian: String, + /// Used as the `target_endian` `cfg` variable. Defaults to little endian. + pub endian: Endian, /// Width of c_int type. Defaults to "32". pub c_int_width: String, /// OS name to use for conditional compilation (`target_os`). Defaults to "none". @@ -1010,7 +1015,7 @@ impl Default for TargetOptions { fn default() -> TargetOptions { TargetOptions { is_builtin: false, - endian: "little".to_string(), + endian: Endian::Little, c_int_width: "32".to_string(), os: "none".to_string(), env: String::new(), @@ -1377,7 +1382,7 @@ impl Target { let kind = LinkOutputKind::from_str(&k).ok_or_else(|| { format!("{}: '{}' is not a valid value for CRT object kind. \ Use '(dynamic,static)-(nopic,pic)-exe' or \ - '(dynamic,static)-dylib'", name, k) + '(dynamic,static)-dylib' or 'wasi-reactor-exe'", name, k) })?; let v = v.as_array().ok_or_else(|| @@ -1439,8 +1444,10 @@ impl Target { } ); } + if let Some(s) = obj.find("target-endian").and_then(Json::as_string) { + base.endian = s.parse()?; + } key!(is_builtin, bool); - key!(endian = "target-endian"); key!(c_int_width = "target-c-int-width"); key!(os); key!(env); diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs index 626865aa242..3dddeb1129c 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -11,6 +12,6 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs index 03322818d33..751022c124b 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, RelroLevel, Target, TargetOptions}; pub fn target() -> Target { @@ -15,6 +16,6 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs index 231539756f3..546dfbab6c7 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -11,6 +12,6 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs index 1c83e3e64d4..bb55872109c 100644 --- a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -11,6 +12,6 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - options: TargetOptions { endian: "big".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs index 3a9271247b0..70dd0b2aee6 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -10,6 +11,6 @@ pub fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs index 105a0b21aaf..66118b74955 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -10,6 +11,6 @@ pub fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs index 49d32944789..679a3a2f6aa 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -10,6 +11,6 @@ pub fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + options: TargetOptions { endian: Endian::Big, mcount: "_mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs index 387d6cdc456..1245098329a 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -10,10 +11,6 @@ pub fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - options: TargetOptions { - endian: "big".to_string(), - mcount: "__mcount".to_string(), - ..base - }, + options: TargetOptions { endian: Endian::Big, mcount: "__mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs index 20ffa07b997..bb943a8825c 100644 --- a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -11,10 +12,6 @@ pub fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - options: TargetOptions { - endian: "big".to_string(), - features: "+secure-plt".to_string(), - ..base - }, + options: TargetOptions { endian: Endian::Big, features: "+secure-plt".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs index 0e713fccd23..4b4f118ba49 100644 --- a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs +++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -12,7 +13,7 @@ pub fn target() -> Target { data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), options: TargetOptions { - endian: "big".to_string(), + endian: Endian::Big, // feature msync would disable instruction 'fsync' which is not supported by fsl_p1p2 features: "+secure-plt,+msync".to_string(), ..base diff --git a/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs index d6e8e6ee220..4eeea9bedfb 100644 --- a/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs @@ -1,8 +1,9 @@ +use crate::abi::Endian; use crate::spec::Target; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); - base.endian = "big".to_string(); + base.endian = Endian::Big; // z10 is the oldest CPU supported by LLVM base.cpu = "z10".to_string(); // FIXME: The data_layout string below and the ABI implementation in diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs index e9b5520ac3d..e1aa48872b9 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs @@ -1,8 +1,9 @@ +use crate::abi::Endian; use crate::spec::Target; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); - base.endian = "big".to_string(); + base.endian = Endian::Big; base.cpu = "v9".to_string(); base.max_atomic_width = Some(64); diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs index c8e90f832d0..7d685c83100 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs @@ -1,3 +1,4 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { @@ -11,10 +12,6 @@ pub fn target() -> Target { pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), arch: "sparc64".to_string(), - options: TargetOptions { - endian: "big".to_string(), - mcount: "__mcount".to_string(), - ..base - }, + options: TargetOptions { endian: Endian::Big, mcount: "__mcount".to_string(), ..base }, } } diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs index 630ce6123f9..63b13fad4f7 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs @@ -1,8 +1,9 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::openbsd_base::opts(); - base.endian = "big".to_string(); + base.endian = Endian::Big; base.cpu = "v9".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); diff --git a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs index aae186b2293..9e8fbff81c5 100644 --- a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs @@ -1,8 +1,9 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::linux_gnu_base::opts(); - base.endian = "big".to_string(); + base.endian = Endian::Big; base.cpu = "v9".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mv8plus".to_string()); diff --git a/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs index 5f99e0b14f9..9ac56cae916 100644 --- a/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs +++ b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs @@ -1,8 +1,9 @@ +use crate::abi::Endian; use crate::spec::{LinkerFlavor, Target}; pub fn target() -> Target { let mut base = super::solaris_base::opts(); - base.endian = "big".to_string(); + base.endian = Endian::Big; base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); // llvm calls this "v9" base.cpu = "v9".to_string(); diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index fc6a9a7f209..d677103df1f 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -305,8 +305,8 @@ impl AutoTraitFinder<'tcx> { infcx.resolve_vars_if_possible(Obligation::new(dummy_cause.clone(), new_env, pred)); let result = select.select(&obligation); - match &result { - &Ok(Some(ref impl_source)) => { + match result { + Ok(Some(ref impl_source)) => { // If we see an explicit negative impl (e.g., `impl !Send for MyStruct`), // we immediately bail out, since it's impossible for us to continue. @@ -339,8 +339,8 @@ impl AutoTraitFinder<'tcx> { return None; } } - &Ok(None) => {} - &Err(SelectionError::Unimplemented) => { + Ok(None) => {} + Err(SelectionError::Unimplemented) => { if self.is_param_no_infer(pred.skip_binder().trait_ref.substs) { already_visited.remove(&pred); self.add_user_pred( @@ -863,7 +863,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionReplacer<'a, 'tcx> { fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { (match r { - &ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned(), + ty::ReVar(vid) => self.vid_to_region.get(vid).cloned(), _ => None, }) .unwrap_or_else(|| r.super_fold_with(self)) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 1d82e732907..a439bb892f8 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1368,7 +1368,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { code: &ObligationCauseCode<'tcx>, ) -> Option<(String, Option<Span>)> { match code { - &ObligationCauseCode::BuiltinDerivedObligation(ref data) => { + ObligationCauseCode::BuiltinDerivedObligation(data) => { let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_ref); match self.get_parent_trait_ref(&data.parent_code) { Some(t) => Some(t), diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index a8f81445b03..8ca540fc893 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -69,16 +69,16 @@ impl IntercrateAmbiguityCause { pub fn intercrate_ambiguity_hint(&self) -> String { match self { - &IntercrateAmbiguityCause::DownstreamCrate { ref trait_desc, ref self_desc } => { - let self_desc = if let &Some(ref ty) = self_desc { + IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } => { + let self_desc = if let Some(ty) = self_desc { format!(" for type `{}`", ty) } else { String::new() }; format!("downstream crates may implement trait `{}`{}", trait_desc, self_desc) } - &IntercrateAmbiguityCause::UpstreamCrateUpdate { ref trait_desc, ref self_desc } => { - let self_desc = if let &Some(ref ty) = self_desc { + IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } => { + let self_desc = if let Some(ty) = self_desc { format!(" for type `{}`", ty) } else { String::new() @@ -89,7 +89,7 @@ impl IntercrateAmbiguityCause { trait_desc, self_desc ) } - &IntercrateAmbiguityCause::ReservationImpl { ref message } => message.clone(), + IntercrateAmbiguityCause::ReservationImpl { message } => message.clone(), } } } diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index a3a2b8967c6..1100401ed12 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -11,7 +11,7 @@ use rustc_hir::GenericArg; use rustc_middle::ty::{ self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, Ty, TyCtxt, }; -use rustc_session::{lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS, Session}; +use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; use rustc_span::{symbol::kw, MultiSpan, Span}; use smallvec::SmallVec; @@ -20,62 +20,72 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Report an error that a generic argument did not match the generic parameter that was /// expected. fn generic_arg_mismatch_err( - sess: &Session, + tcx: TyCtxt<'_>, arg: &GenericArg<'_>, - kind: &'static str, + param: &GenericParamDef, possible_ordering_error: bool, help: Option<&str>, ) { + let sess = tcx.sess; let mut err = struct_span_err!( sess, arg.span(), E0747, "{} provided when a {} was expected", arg.descr(), - kind, + param.kind.descr(), ); - let unordered = sess.features_untracked().const_generics; - let kind_ord = match kind { - "lifetime" => ParamKindOrd::Lifetime, - "type" => ParamKindOrd::Type, - "constant" => ParamKindOrd::Const { unordered }, - // It's more concise to match on the string representation, though it means - // the match is non-exhaustive. - _ => bug!("invalid generic parameter kind {}", kind), - }; - - if let ParamKindOrd::Const { .. } = kind_ord { + if let GenericParamDefKind::Const { .. } = param.kind { if let GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. }) = arg { err.help("const arguments cannot yet be inferred with `_`"); } } - let arg_ord = match arg { - GenericArg::Lifetime(_) => ParamKindOrd::Lifetime, - GenericArg::Type(_) => ParamKindOrd::Type, - GenericArg::Const(_) => ParamKindOrd::Const { unordered }, - }; - - if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. })) - && matches!(kind_ord, ParamKindOrd::Const { .. }) - { - let suggestions = vec![ - (arg.span().shrink_to_lo(), String::from("{ ")), - (arg.span().shrink_to_hi(), String::from(" }")), - ]; - err.multipart_suggestion( - "if this generic argument was intended as a const parameter, \ + // Specific suggestion set for diagnostics + match (arg, ¶m.kind) { + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. }), + GenericParamDefKind::Const { .. }, + ) => { + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + err.multipart_suggestion( + "if this generic argument was intended as a const parameter, \ try surrounding it with braces:", - suggestions, - Applicability::MaybeIncorrect, - ); + suggestions, + Applicability::MaybeIncorrect, + ); + } + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), + GenericParamDefKind::Const { .. }, + ) if tcx.type_of(param.def_id) == tcx.types.usize => { + let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id)); + if let Ok(snippet) = snippet { + err.span_suggestion( + arg.span(), + "array type provided where a `usize` was expected, try", + format!("{{ {} }}", snippet), + Applicability::MaybeIncorrect, + ); + } + } + _ => {} } + let kind_ord = param.kind.to_ord(tcx); + let arg_ord = arg.to_ord(&tcx.features()); + // This note is only true when generic parameters are strictly ordered by their kind. if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { - let (first, last) = - if kind_ord < arg_ord { (kind, arg.descr()) } else { (arg.descr(), kind) }; + let (first, last) = if kind_ord < arg_ord { + (param.kind.descr(), arg.descr()) + } else { + (arg.descr(), param.kind.descr()) + }; err.note(&format!("{} arguments must be provided before {} arguments", first, last)); if let Some(help) = help { err.help(help); @@ -203,7 +213,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // We expected a lifetime argument, but got a type or const // argument. That means we're inferring the lifetimes. substs.push(ctx.inferred_kind(None, param, infer_args)); - force_infer_lt = Some(arg); + force_infer_lt = Some((arg, param)); params.next(); } (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { @@ -213,7 +223,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // ignore it. args.next(); } - (_, kind, _) => { + (_, _, _) => { // We expected one kind of parameter, but the user provided // another. This is an error. However, if we already know that // the arguments don't match up with the parameters, we won't issue @@ -256,9 +266,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { param_types_present.dedup(); Self::generic_arg_mismatch_err( - tcx.sess, + tcx, arg, - kind.descr(), + param, !args_iter.clone().is_sorted_by_key(|arg| match arg { GenericArg::Lifetime(_) => ParamKindOrd::Lifetime, GenericArg::Type(_) => ParamKindOrd::Type, @@ -315,9 +325,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { { let kind = arg.descr(); assert_eq!(kind, "lifetime"); - let provided = + let (provided_arg, param) = force_infer_lt.expect("lifetimes ought to have been inferred"); - Self::generic_arg_mismatch_err(tcx.sess, provided, kind, false, None); + Self::generic_arg_mismatch_err(tcx, provided_arg, param, false, None); } break; diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 9a2210e4f0e..878993d512c 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -770,7 +770,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Try to find an unbound in bounds. let mut unbound = None; for ab in ast_bounds { - if let &hir::GenericBound::Trait(ref ptr, hir::TraitBoundModifier::Maybe) = ab { + if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab { if unbound.is_none() { unbound = Some(&ptr.trait_ref); } else { diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 22e287320d8..116b079e742 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -290,16 +290,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::FnPtr(sig) => (sig, None), ref t => { let mut unit_variant = None; - if let &ty::Adt(adt_def, ..) = t { + if let ty::Adt(adt_def, ..) = t { if adt_def.is_enum() { - if let hir::ExprKind::Call(ref expr, _) = call_expr.kind { + if let hir::ExprKind::Call(expr, _) = call_expr.kind { unit_variant = self.tcx.sess.source_map().span_to_snippet(expr.span).ok(); } } } - if let hir::ExprKind::Call(ref callee, _) = call_expr.kind { + if let hir::ExprKind::Call(callee, _) = call_expr.kind { let mut err = type_error_struct!( self.tcx.sess, callee.span, diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 9c60e8933d4..9bb4c9b3719 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -103,13 +103,17 @@ pub(super) fn check_fn<'a, 'tcx>( Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(header, ..), .. }) => Some(header), + Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(header, ..), + .. + }) => Some(header), // Closures are RustCall, but they tuple their arguments, so shouldn't be checked Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => None, node => bug!("Item being checked wasn't a function/closure: {:?}", node), }; if let Some(header) = item { - tcx.sess.span_err(header.span, "A function with the \"rust-call\" ABI must take a single non-self argument that is a tuple") + tcx.sess.span_err(header.span, "functions with the \"rust-call\" ABI must take a single non-self argument that is a tuple") } }; diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 0f5f0ab0260..67ec739d861 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -926,11 +926,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } }; - if is_capturing_closure(&prev_ty.kind()) || is_capturing_closure(&new_ty.kind()) { + if is_capturing_closure(prev_ty.kind()) || is_capturing_closure(new_ty.kind()) { (None, None) } else { - match (&prev_ty.kind(), &new_ty.kind()) { - (&ty::FnDef(..), &ty::FnDef(..)) => { + match (prev_ty.kind(), new_ty.kind()) { + (ty::FnDef(..), ty::FnDef(..)) => { // Don't reify if the function types have a LUB, i.e., they // are the same function and their parameters have a LUB. match self @@ -943,21 +943,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - (&ty::Closure(_, substs), &ty::FnDef(..)) => { + (ty::Closure(_, substs), ty::FnDef(..)) => { let b_sig = new_ty.fn_sig(self.tcx); let a_sig = self .tcx .signature_unclosure(substs.as_closure().sig(), b_sig.unsafety()); (Some(a_sig), Some(b_sig)) } - (&ty::FnDef(..), &ty::Closure(_, substs)) => { + (ty::FnDef(..), ty::Closure(_, substs)) => { let a_sig = prev_ty.fn_sig(self.tcx); let b_sig = self .tcx .signature_unclosure(substs.as_closure().sig(), a_sig.unsafety()); (Some(a_sig), Some(b_sig)) } - (&ty::Closure(_, substs_a), &ty::Closure(_, substs_b)) => ( + (ty::Closure(_, substs_a), ty::Closure(_, substs_b)) => ( Some(self.tcx.signature_unclosure( substs_a.as_closure().sig(), hir::Unsafety::Normal, diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 8197d02ec59..d76a80d5a39 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1125,7 +1125,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields, base_expr.is_none(), ); - if let &Some(ref base_expr) = base_expr { + if let Some(base_expr) = base_expr { // If check_expr_struct_fields hit an error, do not attempt to populate // the fields with the base_expr. This could cause us to hit errors later // when certain fields are assumed to exist that in fact do not. @@ -1182,8 +1182,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // re-link the regions that EIfEO can erase. self.demand_eqtype(span, adt_ty_hint, adt_ty); - let (substs, adt_kind, kind_name) = match &adt_ty.kind() { - &ty::Adt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()), + let (substs, adt_kind, kind_name) = match adt_ty.kind() { + ty::Adt(adt, substs) => (substs, adt.adt_kind(), adt.variant_descr()), _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields"), }; @@ -1381,19 +1381,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty, ); match variant.ctor_kind { - CtorKind::Fn => { - err.span_label(variant.ident.span, format!("`{adt}` defined here", adt = ty)); - err.span_label(field.ident.span, "field does not exist"); - err.span_label( - ty_span, - format!( - "`{adt}` is a tuple {kind_name}, \ - use the appropriate syntax: `{adt}(/* fields */)`", - adt = ty, - kind_name = kind_name - ), - ); - } + CtorKind::Fn => match ty.kind() { + ty::Adt(adt, ..) if adt.is_enum() => { + err.span_label( + variant.ident.span, + format!( + "`{adt}::{variant}` defined here", + adt = ty, + variant = variant.ident, + ), + ); + err.span_label(field.ident.span, "field does not exist"); + err.span_label( + ty_span, + format!( + "`{adt}::{variant}` is a tuple {kind_name}, \ + use the appropriate syntax: `{adt}::{variant}(/* fields */)`", + adt = ty, + variant = variant.ident, + kind_name = kind_name + ), + ); + } + _ => { + err.span_label(variant.ident.span, format!("`{adt}` defined here", adt = ty)); + err.span_label(field.ident.span, "field does not exist"); + err.span_label( + ty_span, + format!( + "`{adt}` is a tuple {kind_name}, \ + use the appropriate syntax: `{adt}(/* fields */)`", + adt = ty, + kind_name = kind_name + ), + ); + } + }, _ => { // prevent all specified fields from being suggested let skip_fields = skip_fields.iter().map(|ref x| x.ident.name); diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 673dec6c7f9..e4e6cf73c7e 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -63,8 +63,6 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { | sym::min_align_of | sym::needs_drop | sym::caller_location - | sym::size_of_val - | sym::min_align_of_val | sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 3bf41981ef6..5cfd78ebeac 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -333,7 +333,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::Path(ref qpath) => { // local binding - if let &QPath::Resolved(_, ref path) = &qpath { + if let QPath::Resolved(_, path) = qpath { if let hir::def::Res::Local(hir_id) = path.res { let span = tcx.hir().span(hir_id); let snippet = tcx.sess.source_map().span_to_snippet(span); diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index ab41ff372e2..d81d83f60bd 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -330,7 +330,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { ), ) .note("the only supported types are integers, `bool` and `char`") - .help("more complex types are supported with `#[feature(const_generics)]`") + .help("more complex types are supported with `#![feature(const_generics)]`") .emit() } }; diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 2ebb1a3be4e..ae97fa3f7e4 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1920,7 +1920,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP let where_clause = &ast_generics.where_clause; for predicate in where_clause.predicates { match predicate { - &hir::WherePredicate::BoundPredicate(ref bound_pred) => { + hir::WherePredicate::BoundPredicate(bound_pred) => { let ty = icx.to_ty(&bound_pred.bounded_ty); // Keep the type around in a dummy predicate, in case of no bounds. @@ -1949,7 +1949,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP for bound in bound_pred.bounds.iter() { match bound { - &hir::GenericBound::Trait(ref poly_trait_ref, modifier) => { + hir::GenericBound::Trait(poly_trait_ref, modifier) => { let constness = match modifier { hir::TraitBoundModifier::MaybeConst => hir::Constness::NotConst, hir::TraitBoundModifier::None => constness, @@ -1959,7 +1959,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP let mut bounds = Bounds::default(); let _ = AstConv::instantiate_poly_trait_ref( &icx, - poly_trait_ref, + &poly_trait_ref, constness, ty, &mut bounds, @@ -1981,7 +1981,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP predicates.extend(bounds.predicates(tcx, ty)); } - &hir::GenericBound::Outlives(ref lifetime) => { + hir::GenericBound::Outlives(lifetime) => { let region = AstConv::ast_region_to_region(&icx, lifetime, None); predicates.insert(( ty::Binder::bind(ty::PredicateAtom::TypeOutlives( @@ -1995,7 +1995,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } } - &hir::WherePredicate::RegionPredicate(ref region_pred) => { + hir::WherePredicate::RegionPredicate(region_pred) => { let r1 = AstConv::ast_region_to_region(&icx, ®ion_pred.lifetime, None); predicates.extend(region_pred.bounds.iter().map(|bound| { let (r2, span) = match bound { @@ -2011,7 +2011,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP })) } - &hir::WherePredicate::EqPredicate(..) => { + hir::WherePredicate::EqPredicate(..) => { // FIXME(#20041) } } diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index eff197d9988..d95b5b7f17f 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "0.1.10", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.39", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = "0.7" @@ -31,5 +31,5 @@ harness = false [features] compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-c = ["compiler_builtins/c"] -compiler-builtins-asm = ["compiler_builtins/asm"] +compiler-builtins-no-asm = ["compiler_builtins/no-asm"] compiler-builtins-mangled-names = ["compiler_builtins/mangled-names"] diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index f801c1ac75b..adf996fc782 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -103,6 +103,11 @@ where /// is desired, `to_mut` will obtain a mutable reference to an owned /// value, cloning if necessary. /// +/// If you need reference-counting pointers, note that +/// [`Rc::make_mut`][crate::rc::Rc::make_mut] and +/// [`Arc::make_mut`][crate::sync::Arc::make_mut] can provide clone-on-write +/// functionality as well. +/// /// # Examples /// /// ``` diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 8eb2caa60b1..33b812ec59f 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -126,9 +126,7 @@ //! //! [ucg#198]: https://github.com/rust-lang/unsafe-code-guidelines/issues/198 //! [dereferencing]: core::ops::Deref -//! [`Box<T>`]: Box //! [`Box::<T>::from_raw(value)`]: Box::from_raw -//! [`Box::<T>::into_raw`]: Box::into_raw //! [`Global`]: crate::alloc::Global //! [`Layout`]: crate::alloc::Layout //! [`Layout::for_value(&*value)`]: crate::alloc::Layout::for_value diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index b3641a7a0c6..bef6850f06f 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -1124,21 +1124,20 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle<NodeRef<marker::Mut<'a>, K, V, NodeType> /// by taking care of leaf data. fn split_leaf_data(&mut self, new_node: &mut LeafNode<K, V>) -> (K, V) { debug_assert!(self.idx < self.node.len()); - let new_len = self.node.len() - self.idx - 1; + let old_len = self.node.len(); + let new_len = old_len - self.idx - 1; new_node.len = new_len as u16; unsafe { let k = self.node.key_area_mut(self.idx).assume_init_read(); let v = self.node.val_area_mut(self.idx).assume_init_read(); - ptr::copy_nonoverlapping( - self.node.key_area_mut(self.idx + 1..).as_ptr(), - new_node.keys.as_mut_ptr(), - new_len, + move_to_slice( + self.node.key_area_mut(self.idx + 1..old_len), + &mut new_node.keys[..new_len], ); - ptr::copy_nonoverlapping( - self.node.val_area_mut(self.idx + 1..).as_ptr(), - new_node.vals.as_mut_ptr(), - new_len, + move_to_slice( + self.node.val_area_mut(self.idx + 1..old_len), + &mut new_node.vals[..new_len], ); *self.node.len_mut() = self.idx as u16; @@ -1190,20 +1189,20 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, /// - All the edges and key-value pairs to the right of this handle are put into /// a newly allocated node. pub fn split(mut self) -> SplitResult<'a, K, V, marker::Internal> { + let old_len = self.node.len(); unsafe { let mut new_node = Box::new(InternalNode::new()); let kv = self.split_leaf_data(&mut new_node.data); let new_len = usize::from(new_node.data.len); - ptr::copy_nonoverlapping( - self.node.edge_area_mut(self.idx + 1..).as_ptr(), - new_node.edges.as_mut_ptr(), - new_len + 1, + move_to_slice( + self.node.edge_area_mut(self.idx + 1..old_len + 1), + &mut new_node.edges[..new_len + 1], ); let height = self.node.height; let mut right = NodeRef::from_new_internal(new_node, height); - right.borrow_mut().correct_childrens_parent_links(0..=new_len); + right.borrow_mut().correct_childrens_parent_links(0..new_len + 1); SplitResult { left: self.node, kv, right } } @@ -1323,18 +1322,16 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { let parent_key = slice_remove(parent_node.key_area_mut(..old_parent_len), parent_idx); left_node.key_area_mut(old_left_len).write(parent_key); - ptr::copy_nonoverlapping( - right_node.key_area_mut(..).as_ptr(), - left_node.key_area_mut(old_left_len + 1..).as_mut_ptr(), - right_len, + move_to_slice( + right_node.key_area_mut(..right_len), + left_node.key_area_mut(old_left_len + 1..new_left_len), ); let parent_val = slice_remove(parent_node.val_area_mut(..old_parent_len), parent_idx); left_node.val_area_mut(old_left_len).write(parent_val); - ptr::copy_nonoverlapping( - right_node.val_area_mut(..).as_ptr(), - left_node.val_area_mut(old_left_len + 1..).as_mut_ptr(), - right_len, + move_to_slice( + right_node.val_area_mut(..right_len), + left_node.val_area_mut(old_left_len + 1..new_left_len), ); slice_remove(&mut parent_node.edge_area_mut(..old_parent_len + 1), parent_idx + 1); @@ -1346,10 +1343,9 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { // of the node of this edge, thus above zero, so they are internal. let mut left_node = left_node.reborrow_mut().cast_to_internal_unchecked(); let mut right_node = right_node.cast_to_internal_unchecked(); - ptr::copy_nonoverlapping( - right_node.edge_area_mut(..).as_ptr(), - left_node.edge_area_mut(old_left_len + 1..).as_mut_ptr(), - right_len + 1, + move_to_slice( + right_node.edge_area_mut(..right_len + 1), + left_node.edge_area_mut(old_left_len + 1..new_left_len + 1), ); left_node.correct_childrens_parent_links(old_left_len + 1..new_left_len + 1); @@ -1741,5 +1737,15 @@ unsafe fn slice_remove<T>(slice: &mut [MaybeUninit<T>], idx: usize) -> T { } } +/// Moves all values from a slice of initialized elements to a slice +/// of uninitialized elements, leaving behind `src` as all uninitialized. +/// Works like `dst.copy_from_slice(src)` but does not require `T` to be `Copy`. +fn move_to_slice<T>(src: &mut [MaybeUninit<T>], dst: &mut [MaybeUninit<T>]) { + assert!(src.len() == dst.len()); + unsafe { + ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), src.len()); + } +} + #[cfg(test)] mod tests; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index e6db66ac571..cfad111aa54 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -70,7 +70,6 @@ #![warn(missing_docs)] #![warn(missing_debug_implementations)] #![allow(explicit_outlives_requirements)] -#![allow(incomplete_features)] #![deny(unsafe_op_in_unsafe_fn)] #![feature(rustc_allow_const_fn_unstable)] #![cfg_attr(not(test), feature(generator_trait))] @@ -90,7 +89,6 @@ #![feature(coerce_unsized)] #![feature(const_btree_new)] #![feature(const_fn)] -#![feature(const_generics)] #![feature(const_in_array_repeat_expressions)] #![feature(cow_is_borrowed)] #![feature(const_cow_is_borrowed)] @@ -120,6 +118,7 @@ #![feature(raw_ref_op)] #![feature(rustc_attrs)] #![feature(receiver_trait)] +#![cfg_attr(bootstrap, feature(min_const_generics))] #![feature(min_specialization)] #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 3115cc3d002..8183a582d33 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -238,6 +238,7 @@ //! [downgrade]: Rc::downgrade //! [upgrade]: Weak::upgrade //! [mutability]: core::cell#introducing-mutability-inside-of-something-immutable +//! [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name #![stable(feature = "rust1", since = "1.0.0")] diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 578eca7d893..70e0c7dba5e 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -451,8 +451,6 @@ impl str { /// Converts a [`Box<str>`] into a [`String`] without copying or allocating. /// - /// [`Box<str>`]: Box - /// /// # Examples /// /// Basic usage: diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 2a83eb33fe3..1ca194c3361 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -990,6 +990,9 @@ impl<T, A: Allocator> Vec<T, A> { // such that no value will be dropped twice in case `drop_in_place` // were to panic once (if it panics twice, the program aborts). unsafe { + // Note: It's intentional that this is `>` and not `>=`. + // Changing it to `>=` has negative performance + // implications in some cases. See #78884 for more. if len > self.len { return; } diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index 57c6624b64f..c572c66ce32 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -4,6 +4,12 @@ use crate::mem; use crate::num::NonZeroUsize; use crate::ptr::NonNull; +// While this function is used in one place and its implementation +// could be inlined, the previous attempts to do so made rustc +// slower: +// +// * https://github.com/rust-lang/rust/pull/72189 +// * https://github.com/rust-lang/rust/pull/79827 const fn size_align<T>() -> (usize, usize) { (mem::size_of::<T>(), mem::align_of::<T>()) } diff --git a/library/core/src/any.rs b/library/core/src/any.rs index d1951fbbf10..eef8f2046d3 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -14,6 +14,29 @@ //! //! [`Box`]: ../../std/boxed/struct.Box.html //! +//! # Smart pointers and `dyn Any` +//! +//! One piece of behavior to keep in mind when using `Any` as a trait object, +//! especially with types like `Box<dyn Any>` or `Arc<dyn Any>`, is that simply +//! calling `.type_id()` on the value will produce the `TypeId` of the +//! *container*, not the underlying trait object. This can be avoided by +//! converting the smart pointer into a `&dyn Any` instead, which will return +//! the object's `TypeId`. For example: +//! +//! ``` +//! use std::any::{Any, TypeId}; +//! +//! let boxed: Box<dyn Any> = Box::new(3_i32); +//! +//! // You're more likely to want this: +//! let actual_id = (&*boxed).type_id(); +//! // ... than this: +//! let boxed_id = boxed.type_id(); +//! +//! assert_eq!(actual_id, TypeId::of::<i32>()); +//! assert_eq!(boxed_id, TypeId::of::<Box<dyn Any>>()); +//! ``` +//! //! # Examples //! //! Consider a situation where we want to log out a value passed to a function. diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 71548bec7aa..85b1a47f4a9 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -12,6 +12,7 @@ use crate::convert::{Infallible, TryFrom}; use crate::fmt; use crate::hash::{self, Hash}; use crate::marker::Unsize; +use crate::mem::MaybeUninit; use crate::ops::{Index, IndexMut}; use crate::slice::{Iter, IterMut}; @@ -429,7 +430,6 @@ impl<T, const N: usize> [T; N] { where F: FnMut(T) -> U, { - use crate::mem::MaybeUninit; struct Guard<T, const N: usize> { dst: *mut T, initialized: usize, @@ -481,8 +481,6 @@ impl<T, const N: usize> [T; N] { /// ``` #[unstable(feature = "array_zip", issue = "80094")] pub fn zip<U>(self, rhs: [U; N]) -> [(T, U); N] { - use crate::mem::MaybeUninit; - let mut dst = MaybeUninit::uninit_array::<N>(); for (i, (lhs, rhs)) in IntoIter::new(self).zip(IntoIter::new(rhs)).enumerate() { dst[i].write((lhs, rhs)); @@ -506,4 +504,75 @@ impl<T, const N: usize> [T; N] { pub fn as_mut_slice(&mut self) -> &mut [T] { self } + + /// Borrows each element and returns an array of references with the same + /// size as `self`. + /// + /// + /// # Example + /// + /// ``` + /// #![feature(array_methods)] + /// + /// let floats = [3.1, 2.7, -1.0]; + /// let float_refs: [&f64; 3] = floats.each_ref(); + /// assert_eq!(float_refs, [&3.1, &2.7, &-1.0]); + /// ``` + /// + /// This method is particularly useful if combined with other methods, like + /// [`map`](#method.map). This way, you can can avoid moving the original + /// array if its elements are not `Copy`. + /// + /// ``` + /// #![feature(array_methods, array_map)] + /// + /// let strings = ["Ferris".to_string(), "♥".to_string(), "Rust".to_string()]; + /// let is_ascii = strings.each_ref().map(|s| s.is_ascii()); + /// assert_eq!(is_ascii, [true, false, true]); + /// + /// // We can still access the original array: it has not been moved. + /// assert_eq!(strings.len(), 3); + /// ``` + #[unstable(feature = "array_methods", issue = "76118")] + pub fn each_ref(&self) -> [&T; N] { + // Unlike in `map`, we don't need a guard here, as dropping a reference + // is a noop. + let mut out = MaybeUninit::uninit_array::<N>(); + for (src, dst) in self.iter().zip(&mut out) { + dst.write(src); + } + + // SAFETY: All elements of `dst` are properly initialized and + // `MaybeUninit<T>` has the same layout as `T`, so this cast is valid. + unsafe { (&mut out as *mut _ as *mut [&T; N]).read() } + } + + /// Borrows each element mutably and returns an array of mutable references + /// with the same size as `self`. + /// + /// + /// # Example + /// + /// ``` + /// #![feature(array_methods)] + /// + /// let mut floats = [3.1, 2.7, -1.0]; + /// let float_refs: [&mut f64; 3] = floats.each_mut(); + /// *float_refs[0] = 0.0; + /// assert_eq!(float_refs, [&mut 0.0, &mut 2.7, &mut -1.0]); + /// assert_eq!(floats, [0.0, 2.7, -1.0]); + /// ``` + #[unstable(feature = "array_methods", issue = "76118")] + pub fn each_mut(&mut self) -> [&mut T; N] { + // Unlike in `map`, we don't need a guard here, as dropping a reference + // is a noop. + let mut out = MaybeUninit::uninit_array::<N>(); + for (src, dst) in self.iter_mut().zip(&mut out) { + dst.write(src); + } + + // SAFETY: All elements of `dst` are properly initialized and + // `MaybeUninit<T>` has the same layout as `T`, so this cast is valid. + unsafe { (&mut out as *mut _ as *mut [&mut T; N]).read() } + } } diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs index 6f5a6aa7c79..c9040cd0a16 100644 --- a/library/core/src/borrow.rs +++ b/library/core/src/borrow.rs @@ -40,7 +40,6 @@ /// provide a reference to related type `T`, it is often better to use /// [`AsRef<T>`] as more types can safely implement it. /// -/// [`BorrowMut<T>`]: BorrowMut /// [`Box<T>`]: ../../std/boxed/struct.Box.html /// [`Mutex<T>`]: ../../std/sync/struct.Mutex.html /// [`Rc<T>`]: ../../std/rc/struct.Rc.html @@ -183,8 +182,6 @@ pub trait Borrow<Borrowed: ?Sized> { /// As a companion to [`Borrow<T>`] this trait allows a type to borrow as /// an underlying type by providing a mutable reference. See [`Borrow<T>`] /// for more information on borrowing as another type. -/// -/// [`Borrow<T>`]: Borrow #[stable(feature = "rust1", since = "1.0.0")] pub trait BorrowMut<Borrowed: ?Sized>: Borrow<Borrowed> { /// Mutably borrows from an owned value. diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index de05a8c82e8..7a0ec32cc61 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -113,6 +113,48 @@ impl From<char> for u32 { } } +#[stable(feature = "more_char_conversions", since = "1.51.0")] +impl From<char> for u64 { + /// Converts a [`char`] into a [`u64`]. + /// + /// # Examples + /// + /// ``` + /// use std::mem; + /// + /// let c = '👤'; + /// let u = u64::from(c); + /// assert!(8 == mem::size_of_val(&u)) + /// ``` + #[inline] + fn from(c: char) -> Self { + // The char is casted to the value of the code point, then zero-extended to 64 bit. + // See [https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics] + c as u64 + } +} + +#[stable(feature = "more_char_conversions", since = "1.51.0")] +impl From<char> for u128 { + /// Converts a [`char`] into a [`u128`]. + /// + /// # Examples + /// + /// ``` + /// use std::mem; + /// + /// let c = '⚙'; + /// let u = u128::from(c); + /// assert!(16 == mem::size_of_val(&u)) + /// ``` + #[inline] + fn from(c: char) -> Self { + // The char is casted to the value of the code point, then zero-extended to 128 bit. + // See [https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics] + c as u128 + } +} + /// Maps a byte in 0x00..=0xFF to a `char` whose code point has the same value, in U+0000..=U+00FF. /// /// Unicode is designed such that this effectively decodes bytes diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 0c459a820c6..38c6bfb0ef3 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -29,7 +29,7 @@ use self::Ordering::*; /// /// This trait allows for partial equality, for types that do not have a full /// equivalence relation. For example, in floating point numbers `NaN != NaN`, -/// so floating point types implement `PartialEq` but not [`Eq`]. +/// so floating point types implement `PartialEq` but not [`trait@Eq`]. /// /// Formally, the equality must be (for all `a`, `b` and `c`): /// diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 3f7110b34cc..041f40f2cbc 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -135,8 +135,6 @@ pub const fn identity<T>(x: T) -> T { /// Since both [`String`] and [`&str`] implement `AsRef<str>` we can accept both as input argument. /// /// [`&str`]: primitive@str -/// [`Option<T>`]: Option -/// [`Result<T, E>`]: Result /// [`Borrow`]: crate::borrow::Borrow /// [`Eq`]: crate::cmp::Eq /// [`Ord`]: crate::cmp::Ord @@ -169,9 +167,6 @@ pub trait AsRef<T: ?Sized> { /// **Note: This trait must not fail**. If the conversion can fail, use a /// dedicated method which returns an [`Option<T>`] or a [`Result<T, E>`]. /// -/// [`Option<T>`]: Option -/// [`Result<T, E>`]: Result -/// /// # Generic Implementations /// /// - `AsMut` auto-dereferences if the inner type is a mutable reference @@ -270,8 +265,6 @@ pub trait AsMut<T: ?Sized> { /// is_hello(s); /// ``` /// -/// [`Option<T>`]: Option -/// [`Result<T, E>`]: Result /// [`String`]: ../../std/string/struct.String.html /// [`Vec`]: ../../std/vec/struct.Vec.html #[stable(feature = "rust1", since = "1.0.0")] @@ -359,8 +352,6 @@ pub trait Into<T>: Sized { /// } /// ``` /// -/// [`Option<T>`]: Option -/// [`Result<T, E>`]: Result /// [`String`]: ../../std/string/struct.String.html /// [`from`]: From::from /// [book]: ../../book/ch09-00-error-handling.html diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 979a5f8cf50..313729581ac 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -91,7 +91,7 @@ pub const unsafe fn unreachable_unchecked() -> ! { /// }; /// /// // Back on our current thread, we wait for the value to be set -/// while live.load(Ordering::Acquire) { +/// while !live.load(Ordering::Acquire) { /// // The spin loop is a hint to the CPU that we're waiting, but probably /// // not for very long /// hint::spin_loop(); diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 938dc214486..0130586b430 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -712,8 +712,8 @@ extern "rust-intrinsic" { /// [`std::process::abort`](../../std/process/fn.abort.html). pub fn abort() -> !; - /// Tells LLVM that this point in the code is not reachable, enabling - /// further optimizations. + /// Informs the optimizer that this point in the code is not reachable, + /// enabling further optimizations. /// /// N.B., this is very different from the `unreachable!()` macro: Unlike the /// macro, which panics when it is executed, it is *undefined behavior* to @@ -1133,7 +1133,7 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. pub fn volatile_copy_nonoverlapping_memory<T>(dst: *mut T, src: *const T, count: usize); /// Equivalent to the appropriate `llvm.memmove.p0i8.0i8.*` intrinsic, with - /// a size of `count` * `size_of::<T>()` and an alignment of + /// a size of `count * size_of::<T>()` and an alignment of /// `min_align_of::<T>()` /// /// The volatile parameter is set to `true`, so it will not be optimized out @@ -1142,7 +1142,7 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. pub fn volatile_copy_memory<T>(dst: *mut T, src: *const T, count: usize); /// Equivalent to the appropriate `llvm.memset.p0i8.*` intrinsic, with a - /// size of `count` * `size_of::<T>()` and an alignment of + /// size of `count * size_of::<T>()` and an alignment of /// `min_align_of::<T>()`. /// /// The volatile parameter is set to `true`, so it will not be optimized out @@ -1588,7 +1588,7 @@ extern "rust-intrinsic" { pub fn exact_div<T: Copy>(x: T, y: T) -> T; /// Performs an unchecked division, resulting in undefined behavior - /// where y = 0 or x = `T::MIN` and y = -1 + /// where `y == 0` or `x == T::MIN && y == -1` /// /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_div` method. For example, @@ -1596,7 +1596,7 @@ extern "rust-intrinsic" { #[rustc_const_unstable(feature = "const_int_unchecked_arith", issue = "none")] pub fn unchecked_div<T: Copy>(x: T, y: T) -> T; /// Returns the remainder of an unchecked division, resulting in - /// undefined behavior where y = 0 or x = `T::MIN` and y = -1 + /// undefined behavior when `y == 0` or `x == T::MIN && y == -1` /// /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_rem` method. For example, @@ -1605,7 +1605,7 @@ extern "rust-intrinsic" { pub fn unchecked_rem<T: Copy>(x: T, y: T) -> T; /// Performs an unchecked left shift, resulting in undefined behavior when - /// y < 0 or y >= N, where N is the width of T in bits. + /// `y < 0` or `y >= N`, where N is the width of T in bits. /// /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shl` method. For example, @@ -1613,7 +1613,7 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")] pub fn unchecked_shl<T: Copy>(x: T, y: T) -> T; /// Performs an unchecked right shift, resulting in undefined behavior when - /// y < 0 or y >= N, where N is the width of T in bits. + /// `y < 0` or `y >= N`, where N is the width of T in bits. /// /// Safe wrappers for this intrinsic are available on the integer /// primitives via the `checked_shr` method. For example, @@ -1680,14 +1680,14 @@ extern "rust-intrinsic" { #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] pub fn wrapping_mul<T: Copy>(a: T, b: T) -> T; - /// Computes `a + b`, while saturating at numeric bounds. + /// Computes `a + b`, saturating at numeric bounds. /// /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `saturating_add` method. For example, /// [`u32::saturating_add`] #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")] pub fn saturating_add<T: Copy>(a: T, b: T) -> T; - /// Computes `a - b`, while saturating at numeric bounds. + /// Computes `a - b`, saturating at numeric bounds. /// /// The stabilized versions of this intrinsic are available on the integer /// primitives via the `saturating_sub` method. For example, @@ -1696,14 +1696,14 @@ extern "rust-intrinsic" { pub fn saturating_sub<T: Copy>(a: T, b: T) -> T; /// Returns the value of the discriminant for the variant in 'v', - /// cast to a `u64`; if `T` has no discriminant, returns 0. + /// cast to a `u64`; if `T` has no discriminant, returns `0`. /// /// The stabilized version of this intrinsic is [`core::mem::discriminant`](crate::mem::discriminant). #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant; /// Returns the number of variants of the type `T` cast to a `usize`; - /// if `T` has no variants, returns 0. Uninhabited variants will be counted. + /// if `T` has no variants, returns `0`. Uninhabited variants will be counted. /// /// The to-be-stabilized version of this intrinsic is [`mem::variant_count`]. #[rustc_const_unstable(feature = "variant_count", issue = "73662")] @@ -1845,7 +1845,7 @@ pub(crate) fn is_nonoverlapping<T>(src: *const T, dst: *const T, count: usize) - /// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append #[doc(alias = "memcpy")] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "none")] +#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] #[inline] pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize) { extern "rust-intrinsic" { @@ -1929,11 +1929,10 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us /// ``` #[doc(alias = "memmove")] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "none")] +#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")] #[inline] pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) { extern "rust-intrinsic" { - #[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "none")] fn copy<T>(src: *const T, dst: *mut T, count: usize); } diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs index 8cd4c775231..5766fd3c887 100644 --- a/library/core/src/iter/adapters/zip.rs +++ b/library/core/src/iter/adapters/zip.rs @@ -397,7 +397,7 @@ impl<A: Debug + TrustedRandomAccess, B: Debug + TrustedRandomAccess> ZipFmt<A, B /// only be called at most `self.size() - idx - 1` times. /// 4. After `get_unchecked` is called, then only the following methods will be /// called on `self`: -/// * `std::clone::Clone::clone` +/// * `std::clone::Clone::clone()` /// * `std::iter::Iterator::size_hint()` /// * `std::iter::Iterator::next_back()` /// * `std::iter::Iterator::__iterator_get_unchecked()` diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index cd8ab11cb84..4321b2187e1 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -111,7 +111,7 @@ pub unsafe trait Step: Clone + PartialOrd + Sized { Step::forward(start, count) } - /// Returns the value that would be obtained by taking the *successor* + /// Returns the value that would be obtained by taking the *predecessor* /// of `self` `count` times. /// /// If this would overflow the range of values supported by `Self`, returns `None`. diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 633175702d8..0023de65d2b 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -805,8 +805,6 @@ pub trait Iterator { /// assert_eq!(iter.next(), Some(5)); /// assert_eq!(iter.next(), None); /// ``` - /// - /// [`Option<T>`]: Option #[inline] #[stable(feature = "rust1", since = "1.0.0")] fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F> diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 099b98b824d..df8d9ff371f 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -62,7 +62,6 @@ #![warn(missing_docs)] #![warn(missing_debug_implementations)] #![allow(explicit_outlives_requirements)] -#![allow(incomplete_features)] #![feature(rustc_allow_const_fn_unstable)] #![feature(allow_internal_unstable)] #![feature(arbitrary_self_types)] @@ -89,7 +88,6 @@ #![feature(const_impl_trait)] #![feature(const_fn_floating_point_arithmetic)] #![feature(const_fn_fn_ptr_basics)] -#![feature(const_generics)] #![feature(const_option)] #![feature(const_precise_live_drops)] #![feature(const_ptr_offset)] @@ -131,6 +129,7 @@ #![feature(repr_simd, platform_intrinsics)] #![feature(rustc_attrs)] #![feature(simd_ffi)] +#![cfg_attr(bootstrap, feature(min_const_generics))] #![feature(min_specialization)] #![feature(staged_api)] #![feature(std_internals)] diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 22e44a3c409..64cf5286eb1 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -4,6 +4,7 @@ //! types, initializing and manipulating memory. #![stable(feature = "rust1", since = "1.0.0")] +#![cfg_attr(bootstrap, allow(unused_unsafe))] use crate::clone; use crate::cmp; @@ -333,7 +334,8 @@ pub const fn size_of<T>() -> usize { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")] pub const fn size_of_val<T: ?Sized>(val: &T) -> usize { - intrinsics::size_of_val(val) + // SAFETY: `val` is a reference, so it's a valid raw pointer + unsafe { intrinsics::size_of_val(val) } } /// Returns the size of the pointed-to value in bytes. @@ -381,7 +383,8 @@ pub const fn size_of_val<T: ?Sized>(val: &T) -> usize { #[unstable(feature = "layout_for_ptr", issue = "69835")] #[rustc_const_unstable(feature = "const_size_of_val_raw", issue = "46571")] pub const unsafe fn size_of_val_raw<T: ?Sized>(val: *const T) -> usize { - intrinsics::size_of_val(val) + // SAFETY: the caller must provide a valid raw pointer + unsafe { intrinsics::size_of_val(val) } } /// Returns the [ABI]-required minimum alignment of a type. @@ -425,7 +428,8 @@ pub fn min_align_of<T>() -> usize { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_deprecated(reason = "use `align_of_val` instead", since = "1.2.0")] pub fn min_align_of_val<T: ?Sized>(val: &T) -> usize { - intrinsics::min_align_of_val(val) + // SAFETY: val is a reference, so it's a valid raw pointer + unsafe { intrinsics::min_align_of_val(val) } } /// Returns the [ABI]-required minimum alignment of a type. @@ -469,7 +473,8 @@ pub const fn align_of<T>() -> usize { #[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")] #[allow(deprecated)] pub const fn align_of_val<T: ?Sized>(val: &T) -> usize { - intrinsics::min_align_of_val(val) + // SAFETY: val is a reference, so it's a valid raw pointer + unsafe { intrinsics::min_align_of_val(val) } } /// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. @@ -513,7 +518,8 @@ pub const fn align_of_val<T: ?Sized>(val: &T) -> usize { #[unstable(feature = "layout_for_ptr", issue = "69835")] #[rustc_const_unstable(feature = "const_align_of_val_raw", issue = "46571")] pub const unsafe fn align_of_val_raw<T: ?Sized>(val: *const T) -> usize { - intrinsics::min_align_of_val(val) + // SAFETY: the caller must provide a valid raw pointer + unsafe { intrinsics::min_align_of_val(val) } } /// Returns `true` if dropping values of type `T` matters. @@ -650,7 +656,6 @@ pub unsafe fn zeroed<T>() -> T { /// (Notice that the rules around uninitialized integers are not finalized yet, but /// until they are, it is advisable to avoid them.) /// -/// [`MaybeUninit<T>`]: MaybeUninit /// [uninit]: MaybeUninit::uninit /// [assume_init]: MaybeUninit::assume_init /// [inv]: MaybeUninit#initialization-invariant diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 1afa30f5843..0051c9eede0 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -512,7 +512,6 @@ impl<T> Option<T> { /// result of a function call, it is recommended to use [`ok_or_else`], which is /// lazily evaluated. /// - /// [`Result<T, E>`]: Result /// [`Ok(v)`]: Ok /// [`Err(err)`]: Err /// [`Some(v)`]: Some @@ -539,7 +538,6 @@ impl<T> Option<T> { /// Transforms the `Option<T>` into a [`Result<T, E>`], mapping [`Some(v)`] to /// [`Ok(v)`] and [`None`] to [`Err(err())`]. /// - /// [`Result<T, E>`]: Result /// [`Ok(v)`]: Ok /// [`Err(err())`]: Err /// [`Some(v)`]: Some diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 0b9c733f7fe..b2de0e16a17 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -349,7 +349,6 @@ //! mutable reference even when you just have [`Pin`]`<&mut Self>` (such as in your own //! [`poll`] implementation). //! -//! [`Pin<P>`]: Pin //! [`Deref`]: crate::ops::Deref //! [`DerefMut`]: crate::ops::DerefMut //! [`mem::swap`]: crate::mem::swap @@ -364,7 +363,6 @@ //! [`RefCell<T>`]: crate::cell::RefCell //! [`drop`]: Drop::drop //! [`VecDeque<T>`]: ../../std/collections/struct.VecDeque.html -//! [`Option<T>`]: Option //! [`Some(v)`]: Some //! [`ptr::write`]: crate::ptr::write //! [`Future`]: crate::future::Future diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 0b4ca2b7214..d6d17625729 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -368,8 +368,6 @@ impl<T, E> Result<T, E> { /// Converts `self` into an [`Option<T>`], consuming `self`, /// and discarding the error, if any. /// - /// [`Option<T>`]: Option - /// /// # Examples /// /// Basic usage: @@ -395,8 +393,6 @@ impl<T, E> Result<T, E> { /// Converts `self` into an [`Option<E>`], consuming `self`, /// and discarding the success value, if any. /// - /// [`Option<E>`]: Option - /// /// # Examples /// /// Basic usage: @@ -1009,8 +1005,6 @@ impl<T: fmt::Debug, E> Result<T, E> { /// Panics if the value is an [`Ok`], with a custom panic message provided /// by the [`Ok`]'s value. /// - /// - /// /// # Examples /// /// ```{.should_panic} diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index a367b4737db..769b673d80a 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -51,7 +51,7 @@ fn size_from_ptr<T>(_: *const T) -> usize { /// Basic usage: /// /// ``` -/// // First, we declare a type which has `iter` method to get the `Iter` struct (&[usize here]): +/// // First, we declare a type which has `iter` method to get the `Iter` struct (`&[usize]` here): /// let slice = &[1, 2, 3]; /// /// // Then, we iterate over it: @@ -112,7 +112,7 @@ impl<'a, T> Iter<'a, T> { /// /// ``` /// // First, we declare a type which has the `iter` method to get the `Iter` - /// // struct (&[usize here]): + /// // struct (`&[usize]` here): /// let slice = &[1, 2, 3]; /// /// // Then, we get the iterator: @@ -167,7 +167,7 @@ impl<T> AsRef<[T]> for Iter<'_, T> { /// /// ``` /// // First, we declare a type which has `iter_mut` method to get the `IterMut` -/// // struct (&[usize here]): +/// // struct (`&[usize]` here): /// let mut slice = &mut [1, 2, 3]; /// /// // Then, we iterate over it and increment each element value: @@ -246,7 +246,7 @@ impl<'a, T> IterMut<'a, T> { /// /// ``` /// // First, we declare a type which has `iter_mut` method to get the `IterMut` - /// // struct (&[usize here]): + /// // struct (`&[usize]` here): /// let mut slice = &mut [1, 2, 3]; /// /// { diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 58bf74c8cf4..bd01271ec15 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1872,19 +1872,24 @@ impl<T> [T] { /// # Examples /// /// ``` - /// #![feature(slice_strip)] /// let v = &[10, 40, 30]; /// assert_eq!(v.strip_prefix(&[10]), Some(&[40, 30][..])); /// assert_eq!(v.strip_prefix(&[10, 40]), Some(&[30][..])); /// assert_eq!(v.strip_prefix(&[50]), None); /// assert_eq!(v.strip_prefix(&[10, 50]), None); + /// + /// let prefix : &str = "he"; + /// assert_eq!(b"hello".strip_prefix(prefix.as_bytes()), + /// Some(b"llo".as_ref())); /// ``` #[must_use = "returns the subslice without modifying the original"] - #[unstable(feature = "slice_strip", issue = "73413")] - pub fn strip_prefix(&self, prefix: &[T]) -> Option<&[T]> + #[stable(feature = "slice_strip", since = "1.50.0")] + pub fn strip_prefix<P: SlicePattern<Item = T> + ?Sized>(&self, prefix: &P) -> Option<&[T]> where T: PartialEq, { + // This function will need rewriting if and when SlicePattern becomes more sophisticated. + let prefix = prefix.as_slice(); let n = prefix.len(); if n <= self.len() { let (head, tail) = self.split_at(n); @@ -1905,7 +1910,6 @@ impl<T> [T] { /// # Examples /// /// ``` - /// #![feature(slice_strip)] /// let v = &[10, 40, 30]; /// assert_eq!(v.strip_suffix(&[30]), Some(&[10, 40][..])); /// assert_eq!(v.strip_suffix(&[40, 30]), Some(&[10][..])); @@ -1913,11 +1917,13 @@ impl<T> [T] { /// assert_eq!(v.strip_suffix(&[50, 30]), None); /// ``` #[must_use = "returns the subslice without modifying the original"] - #[unstable(feature = "slice_strip", issue = "73413")] - pub fn strip_suffix(&self, suffix: &[T]) -> Option<&[T]> + #[stable(feature = "slice_strip", since = "1.50.0")] + pub fn strip_suffix<P: SlicePattern<Item = T> + ?Sized>(&self, suffix: &P) -> Option<&[T]> where T: PartialEq, { + // This function will need rewriting if and when SlicePattern becomes more sophisticated. + let suffix = suffix.as_slice(); let (len, n) = (self.len(), suffix.len()); if n <= len { let (head, tail) = self.split_at(len - n); @@ -3310,3 +3316,35 @@ impl<T> Default for &mut [T] { &mut [] } } + +#[unstable(feature = "slice_pattern", reason = "stopgap trait for slice patterns", issue = "56345")] +/// Patterns in slices - currently, only used by `strip_prefix` and `strip_suffix`. At a future +/// point, we hope to generalise `core::str::Pattern` (which at the time of writing is limited to +/// `str`) to slices, and then this trait will be replaced or abolished. +pub trait SlicePattern { + /// The element type of the slice being matched on. + type Item; + + /// Currently, the consumers of `SlicePattern` need a slice. + fn as_slice(&self) -> &[Self::Item]; +} + +#[stable(feature = "slice_strip", since = "1.50.0")] +impl<T> SlicePattern for [T] { + type Item = T; + + #[inline] + fn as_slice(&self) -> &[Self::Item] { + self + } +} + +#[stable(feature = "slice_strip", since = "1.50.0")] +impl<T, const N: usize> SlicePattern for [T; N] { + type Item = T; + + #[inline] + fn as_slice(&self) -> &[Self::Item] { + self + } +} diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index ba495a1a9fb..ae7892291ed 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2175,7 +2175,7 @@ impl str { /// helps the inference algorithm understand specifically which type /// you're trying to parse into. /// - /// `parse` can parse any type that implements the [`FromStr`] trait. + /// `parse` can parse into any type that implements the [`FromStr`] trait. /// /// # Errors diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 71d00b5c087..1c738761e8a 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,7 +17,7 @@ panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } libc = { version = "0.2.79", default-features = false, features = ['rustc-dep-of-std'] } -compiler_builtins = { version = "0.1.35" } +compiler_builtins = { version = "0.1.39" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.9.0", default-features = false, features = ['rustc-dep-of-std'] } @@ -60,7 +60,7 @@ panic-unwind = ["panic_unwind"] profiler = ["profiler_builtins"] compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] -compiler-builtins-asm = ["alloc/compiler-builtins-asm"] +compiler-builtins-no-asm = ["alloc/compiler-builtins-no-asm"] compiler-builtins-mangled-names = ["alloc/compiler-builtins-mangled-names"] llvm-libunwind = ["unwind/llvm-libunwind"] system-llvm-libunwind = ["unwind/system-llvm-libunwind"] diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 8491ff40033..843ef09a584 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -166,8 +166,9 @@ impl System { match old_layout.size() { 0 => self.alloc_impl(new_layout, zeroed), - // SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size` - // as required by safety conditions. Other conditions must be upheld by the caller + // SAFETY: `new_size` is non-zero as `new_size` is greater than or equal to `old_size` + // as required by safety conditions and the `old_size == 0` case was handled in the + // previous match arm. Other conditions must be upheld by the caller old_size if old_layout.align() == new_layout.align() => unsafe { let new_size = new_layout.size(); diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 0044e59d697..ca83c409822 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -42,8 +42,6 @@ use crate::string; /// via [`Error::source()`]. This makes it possible for the high-level /// module to provide its own errors while also revealing some of the /// implementation for debugging via `source` chains. -/// -/// [`Result<T, E>`]: Result #[stable(feature = "rust1", since = "1.0.0")] pub trait Error: Debug + Display { /// The lower-level source of this error, if any. diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index dfbf6c3f244..7ad9e446c59 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -240,7 +240,6 @@ //! //! [`File`]: crate::fs::File //! [`TcpStream`]: crate::net::TcpStream -//! [`Vec<T>`]: Vec //! [`io::stdout`]: stdout //! [`io::Result`]: self::Result //! [`?` operator]: ../../book/appendix-02-operators.html @@ -1984,7 +1983,6 @@ pub trait BufRead: Read { /// also yielded an error. /// /// [`io::Result`]: self::Result - /// [`Vec<u8>`]: Vec /// [`read_until`]: BufRead::read_until /// /// # Examples diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 2c6f03fe224..15ef5d1619b 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -307,7 +307,6 @@ #![feature(slice_internals)] #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] -#![feature(slice_strip)] #![feature(staged_api)] #![feature(std_internals)] #![feature(stdsimd)] diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 8a75c1d6058..243761e3897 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1775,7 +1775,6 @@ impl Path { /// Any non-Unicode sequences are replaced with /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. /// - /// [`Cow<str>`]: Cow /// [U+FFFD]: super::char::REPLACEMENT_CHARACTER /// /// # Examples diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index 0fbd6b62f18..26302d0ecf2 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -41,17 +41,17 @@ pub use crate::result::Result::{self, Err, Ok}; pub use core::prelude::v1::{ asm, assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, format_args_nl, global_asm, include, include_bytes, include_str, line, llvm_asm, log_syntax, - module_path, option_env, stringify, trace_macros, + module_path, option_env, stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, + PartialEq, PartialOrd, }; -// FIXME: Attribute and derive macros are not documented because for them rustdoc generates +// FIXME: Attribute and internal derive macros are not documented because for them rustdoc generates // dead links which fail link checker testing. #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow(deprecated)] #[doc(hidden)] pub use core::prelude::v1::{ - bench, global_allocator, test, test_case, Clone, Copy, Debug, Default, Eq, Hash, Ord, - PartialEq, PartialOrd, RustcDecodable, RustcEncodable, + bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable, }; #[unstable( diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index ec12e9f09d8..876b2b8a3f6 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -11,8 +11,9 @@ /// `bool` implements various traits, such as [`BitAnd`], [`BitOr`], [`Not`], etc., /// which allow us to perform boolean operations using `&`, `|` and `!`. /// -/// `if` always demands a `bool` value. [`assert!`], which is an important macro in testing, -/// checks whether an expression returns `true` and panics if it isn't. +/// `if` requires a `bool` value as its conditional. [`assert!`], which is an +/// important macro in testing, checks whether an expression is `true` and panics +/// if it isn't. /// /// ``` /// let bool_val = true & false | false; @@ -25,7 +26,7 @@ /// /// # Examples /// -/// A trivial example of the usage of `bool`, +/// A trivial example of the usage of `bool`: /// /// ``` /// let praise_the_borrow_checker = true; @@ -122,9 +123,9 @@ mod prim_bool {} /// `!`, if we have to call [`String::from_str`] for some reason the result will be a /// [`Result<String, !>`] which we can unpack like this: /// -/// ```ignore (string-from-str-error-type-is-not-never-yet) -/// #[feature(exhaustive_patterns)] -/// // NOTE: this does not work today! +/// ``` +/// #![feature(exhaustive_patterns)] +/// use std::str::FromStr; /// let Ok(s) = String::from_str("hello"); /// ``` /// @@ -184,9 +185,6 @@ mod prim_bool {} /// because `!` coerces to `Result<!, ConnectionError>` automatically. /// /// [`String::from_str`]: str::FromStr::from_str -/// [`Result<String, !>`]: Result -/// [`Result<T, !>`]: Result -/// [`Result<!, E>`]: Result /// [`String`]: string::String /// [`FromStr`]: str::FromStr /// diff --git a/library/std/src/sys_common/fs.rs b/library/std/src/sys_common/fs.rs index e30e8018a31..6bdb26cd078 100644 --- a/library/std/src/sys_common/fs.rs +++ b/library/std/src/sys_common/fs.rs @@ -5,19 +5,21 @@ use crate::io::{self, Error, ErrorKind}; use crate::path::Path; pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { - if !from.is_file() { + let mut reader = fs::File::open(from)?; + let metadata = reader.metadata()?; + + if !metadata.is_file() { return Err(Error::new( ErrorKind::InvalidInput, "the source path is not an existing regular file", )); } - let mut reader = fs::File::open(from)?; let mut writer = fs::File::create(to)?; - let perm = reader.metadata()?.permissions(); + let perm = metadata.permissions(); let ret = io::copy(&mut reader, &mut writer)?; - fs::set_permissions(to, perm)?; + writer.set_permissions(perm)?; Ok(ret) } diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index d5804cc3dd8..226557430df 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -26,7 +26,7 @@ default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] backtrace = ["std/backtrace"] compiler-builtins-c = ["std/compiler-builtins-c"] compiler-builtins-mem = ["std/compiler-builtins-mem"] -compiler-builtins-asm = ["std/compiler-builtins-asm"] +compiler-builtins-no-asm = ["std/compiler-builtins-no-asm"] compiler-builtins-mangled-names = ["std/compiler-builtins-mangled-names"] llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] diff --git a/library/unwind/build.rs b/library/unwind/build.rs index fae760c4a4e..694e6b98c82 100644 --- a/library/unwind/build.rs +++ b/library/unwind/build.rs @@ -4,6 +4,10 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); let target = env::var("TARGET").expect("TARGET was not set"); + if cfg!(feature = "system-llvm-libunwind") { + return; + } + if cfg!(feature = "llvm-libunwind") && ((target.contains("linux") && !target.contains("musl")) || target.contains("fuchsia")) { diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index b8bae69d063..6d2d7bbbef9 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -393,7 +393,7 @@ class RustBuild(object): if self.rustc().startswith(self.bin_root()) and \ (not os.path.exists(self.rustc()) or - self.program_out_of_date(self.rustc_stamp())): + self.program_out_of_date(self.rustc_stamp(), self.date)): if os.path.exists(self.bin_root()): shutil.rmtree(self.bin_root()) tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' @@ -413,7 +413,7 @@ class RustBuild(object): lib_dir = "{}/lib".format(self.bin_root()) for lib in os.listdir(lib_dir): if lib.endswith(".so"): - self.fix_bin_or_dylib("{}/{}".format(lib_dir, lib)) + self.fix_bin_or_dylib(os.path.join(lib_dir, lib), rpath_libz=True) with output(self.rustc_stamp()) as rust_stamp: rust_stamp.write(self.date) @@ -429,7 +429,7 @@ class RustBuild(object): self.fix_bin_or_dylib("{}/bin/rustfmt".format(self.bin_root())) self.fix_bin_or_dylib("{}/bin/cargo-fmt".format(self.bin_root())) with output(self.rustfmt_stamp()) as rustfmt_stamp: - rustfmt_stamp.write(self.date + self.rustfmt_channel) + rustfmt_stamp.write(self.rustfmt_channel) if self.downloading_llvm(): # We want the most recent LLVM submodule update to avoid downloading @@ -451,12 +451,17 @@ class RustBuild(object): "{}/src/bootstrap/download-ci-llvm-stamp".format(top_level), ]).decode(sys.getdefaultencoding()).strip() llvm_assertions = self.get_toml('assertions', 'llvm') == 'true' + llvm_root = self.llvm_root() + llvm_lib = os.path.join(llvm_root, "lib") 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("{}/bin/{}".format(self.llvm_root(), binary)) + self.fix_bin_or_dylib(os.path.join(llvm_root, "bin", binary), rpath_libz=True) + 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) with output(self.llvm_stamp()) as llvm_stamp: - llvm_stamp.write(self.date + llvm_sha + str(llvm_assertions)) + llvm_stamp.write(llvm_sha + str(llvm_assertions)) def downloading_llvm(self): opt = self.get_toml('download-ci-llvm', 'llvm') @@ -487,7 +492,12 @@ class RustBuild(object): url = "https://ci-artifacts.rust-lang.org/rustc-builds/{}".format(llvm_sha) if llvm_assertions: url = url.replace('rustc-builds', 'rustc-builds-alt') - tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' + # ci-artifacts are only stored as .xz, not .gz + if not support_xz(): + print("error: XZ support is required to download LLVM") + print("help: consider disabling `download-ci-llvm` or using python3") + exit(1) + tarball_suffix = '.tar.xz' filename = "rust-dev-nightly-" + self.build + tarball_suffix tarball = os.path.join(rustc_cache, filename) if not os.path.exists(tarball): @@ -496,7 +506,7 @@ class RustBuild(object): match="rust-dev", verbose=self.verbose) - def fix_bin_or_dylib(self, fname): + def fix_bin_or_dylib(self, fname, rpath_libz=False): """Modifies the interpreter section of 'fname' to fix the dynamic linker, or the RPATH section, to fix the dynamic library search path @@ -566,20 +576,22 @@ class RustBuild(object): self.nix_deps_dir = nix_deps_dir patchelf = "{}/patchelf/bin/patchelf".format(nix_deps_dir) + patchelf_args = [] - if fname.endswith(".so"): - # Dynamic library, patch RPATH to point to system dependencies. + 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)] - else: + 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: - patchelf_args = ["--set-interpreter", dynamic_linker.read().rstrip()] + patchelf_args += ["--set-interpreter", dynamic_linker.read().rstrip()] try: subprocess.check_output([patchelf] + patchelf_args + [fname]) @@ -618,12 +630,12 @@ class RustBuild(object): return os.path.join(self.llvm_root(), '.llvm-stamp') - def program_out_of_date(self, stamp_path, extra=""): + def program_out_of_date(self, stamp_path, key): """Check if the given program stamp is out of date""" if not os.path.exists(stamp_path) or self.clean: return True with open(stamp_path, 'r') as stamp: - return (self.date + extra) != stamp.read() + return key != stamp.read() def bin_root(self): """Return the binary root directory diff --git a/src/bootstrap/bootstrap_test.py b/src/bootstrap/bootstrap_test.py index 0e941e13676..61507114159 100644 --- a/src/bootstrap/bootstrap_test.py +++ b/src/bootstrap/bootstrap_test.py @@ -70,6 +70,7 @@ class ProgramOutOfDate(unittest.TestCase): self.build.build_dir = self.container self.rustc_stamp_path = os.path.join(self.container, "stage0", ".rustc-stamp") + self.key = self.build.date + str(None) def tearDown(self): rmtree(self.container) @@ -78,19 +79,19 @@ class ProgramOutOfDate(unittest.TestCase): """Return True when the stamp file does not exists""" if os.path.exists(self.rustc_stamp_path): os.unlink(self.rustc_stamp_path) - self.assertTrue(self.build.program_out_of_date(self.rustc_stamp_path)) + self.assertTrue(self.build.program_out_of_date(self.rustc_stamp_path, self.key)) def test_dates_are_different(self): """Return True when the dates are different""" with open(self.rustc_stamp_path, "w") as rustc_stamp: - rustc_stamp.write("2017-06-14") - self.assertTrue(self.build.program_out_of_date(self.rustc_stamp_path)) + rustc_stamp.write("2017-06-14None") + self.assertTrue(self.build.program_out_of_date(self.rustc_stamp_path, self.key)) def test_same_dates(self): """Return False both dates match""" with open(self.rustc_stamp_path, "w") as rustc_stamp: - rustc_stamp.write("2017-06-15") - self.assertFalse(self.build.program_out_of_date(self.rustc_stamp_path)) + rustc_stamp.write("2017-06-15None") + self.assertFalse(self.build.program_out_of_date(self.rustc_stamp_path, self.key)) if __name__ == '__main__': diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index c2abb01fa8c..ec9ce4c820c 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -1534,7 +1534,7 @@ impl Rustflags { fn arg(&mut self, arg: &str) -> &mut Self { assert_eq!(arg.split(' ').count(), 1); if !self.0.is_empty() { - self.0.push_str(" "); + self.0.push(' '); } self.0.push_str(arg); self diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 2b82f6c30b2..6e65be93fec 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -74,9 +74,9 @@ impl GitInfo { if let Some(ref inner) = self.inner { version.push_str(" ("); version.push_str(&inner.short_sha); - version.push_str(" "); + version.push(' '); version.push_str(&inner.commit_date); - version.push_str(")"); + version.push(')'); } version } diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index f65b2b2c79f..72a979338a5 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -21,6 +21,16 @@ fn args(builder: &Builder<'_>) -> Vec<String> { } if let Subcommand::Clippy { fix, .. } = builder.config.cmd { + // disable the most spammy clippy lints + let ignored_lints = vec![ + "many_single_char_names", // there are a lot in stdarch + "collapsible_if", + "type_complexity", + "missing_safety_doc", // almost 3K warnings + "too_many_arguments", + "needless_lifetimes", // people want to keep the lifetimes + "wrong_self_convention", + ]; let mut args = vec![]; if fix { #[rustfmt::skip] @@ -33,6 +43,7 @@ fn args(builder: &Builder<'_>) -> Vec<String> { ])); } args.extend(strings(&["--", "--cap-lints", "warn"])); + args.extend(ignored_lints.iter().map(|lint| format!("-Aclippy::{}", lint))); args } else { vec![] diff --git a/src/bootstrap/clean.rs b/src/bootstrap/clean.rs index f83dfe8e635..9b9df36e7dc 100644 --- a/src/bootstrap/clean.rs +++ b/src/bootstrap/clean.rs @@ -21,6 +21,7 @@ pub fn clean(build: &Build, all: bool) { } else { rm_rf(&build.out.join("tmp")); rm_rf(&build.out.join("dist")); + rm_rf(&build.out.join("bootstrap")); for host in &build.hosts { let entries = match build.out.join(host.triple).read_dir() { diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 2707a640457..39700c087a2 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -188,14 +188,16 @@ fn copy_self_contained_objects( } } else if target.ends_with("-wasi") { let srcdir = builder.wasi_root(target).unwrap().join("lib/wasm32-wasi"); - copy_and_stamp( - builder, - &libdir_self_contained, - &srcdir, - "crt1.o", - &mut target_deps, - DependencyType::TargetSelfContained, - ); + for &obj in &["crt1.o", "crt1-reactor.o"] { + copy_and_stamp( + builder, + &libdir_self_contained, + &srcdir, + obj, + &mut target_deps, + DependencyType::TargetSelfContained, + ); + } } else if target.contains("windows-gnu") { for obj in ["crt2.o", "dllcrt2.o"].iter() { let src = compiler_file(builder, builder.cc(target), target, obj); @@ -356,15 +358,12 @@ fn copy_sanitizers( let dst = libdir.join(&runtime.name); builder.copy(&runtime.path, &dst); - if target == "x86_64-apple-darwin" { - // Update the library install name reflect the fact it has been renamed. - let status = Command::new("install_name_tool") - .arg("-id") - .arg(format!("@rpath/{}", runtime.name)) - .arg(&dst) - .status() - .expect("failed to execute `install_name_tool`"); - assert!(status.success()); + if target == "x86_64-apple-darwin" || target == "aarch64-apple-darwin" { + // Update the library’s install name to reflect that it has has been renamed. + apple_darwin_update_library_name(&dst, &format!("@rpath/{}", &runtime.name)); + // Upon renaming the install name, the code signature of the file will invalidate, + // so we will sign it again. + apple_darwin_sign_file(&dst); } target_deps.push(dst); @@ -373,6 +372,27 @@ fn copy_sanitizers( target_deps } +fn apple_darwin_update_library_name(library_path: &Path, new_name: &str) { + let status = Command::new("install_name_tool") + .arg("-id") + .arg(new_name) + .arg(library_path) + .status() + .expect("failed to execute `install_name_tool`"); + assert!(status.success()); +} + +fn apple_darwin_sign_file(file_path: &Path) { + let status = Command::new("codesign") + .arg("-f") // Force to rewrite the existing signature + .arg("-s") + .arg("-") + .arg(file_path) + .status() + .expect("failed to execute `codesign`"); + assert!(status.success()); +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct StartupObjects { pub compiler: Compiler, diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index daec1656b27..e2c2e19b0bc 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -1326,17 +1326,17 @@ impl Step for Extended { license += &builder.read(&builder.src.join("COPYRIGHT")); license += &builder.read(&builder.src.join("LICENSE-APACHE")); license += &builder.read(&builder.src.join("LICENSE-MIT")); - license.push_str("\n"); - license.push_str("\n"); + license.push('\n'); + license.push('\n'); let rtf = r"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Arial;}}\nowwrap\fs18"; let mut rtf = rtf.to_string(); - rtf.push_str("\n"); + rtf.push('\n'); for line in license.lines() { rtf.push_str(line); rtf.push_str("\\line "); } - rtf.push_str("}"); + rtf.push('}'); fn filter(contents: &str, marker: &str) -> String { let start = format!("tool-{}-start", marker); diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs index 96164947943..fd0acc3a919 100644 --- a/src/bootstrap/install.rs +++ b/src/bootstrap/install.rs @@ -5,7 +5,7 @@ use std::env; use std::fs; -use std::path::{Component, Path, PathBuf}; +use std::path::{Component, PathBuf}; use std::process::Command; use build_helper::t; @@ -26,74 +26,63 @@ fn install_sh( ) { builder.info(&format!("Install {} stage{} ({:?})", package, stage, host)); - let prefix_default = PathBuf::from("/usr/local"); - let sysconfdir_default = PathBuf::from("/etc"); - let datadir_default = PathBuf::from("share"); - let docdir_default = datadir_default.join("doc/rust"); - let libdir_default = PathBuf::from("lib"); - let mandir_default = datadir_default.join("man"); - let prefix = builder.config.prefix.as_ref().unwrap_or(&prefix_default); - let sysconfdir = builder.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default); - let datadir = builder.config.datadir.as_ref().unwrap_or(&datadir_default); - let docdir = builder.config.docdir.as_ref().unwrap_or(&docdir_default); - let bindir = &builder.config.bindir; - let libdir = builder.config.libdir.as_ref().unwrap_or(&libdir_default); - let mandir = builder.config.mandir.as_ref().unwrap_or(&mandir_default); - - let sysconfdir = prefix.join(sysconfdir); - let datadir = prefix.join(datadir); - let docdir = prefix.join(docdir); - let bindir = prefix.join(bindir); - let libdir = prefix.join(libdir); - let mandir = prefix.join(mandir); - - let destdir = env::var_os("DESTDIR").map(PathBuf::from); - - let prefix = add_destdir(&prefix, &destdir); - let sysconfdir = add_destdir(&sysconfdir, &destdir); - let datadir = add_destdir(&datadir, &destdir); - let docdir = add_destdir(&docdir, &destdir); - let bindir = add_destdir(&bindir, &destdir); - let libdir = add_destdir(&libdir, &destdir); - let mandir = add_destdir(&mandir, &destdir); - - let prefix = { - fs::create_dir_all(&prefix) - .unwrap_or_else(|err| panic!("could not create {}: {}", prefix.display(), err)); - fs::canonicalize(&prefix) - .unwrap_or_else(|err| panic!("could not canonicalize {}: {}", prefix.display(), err)) - }; + let prefix = default_path(&builder.config.prefix, "/usr/local"); + let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc")); + let datadir = prefix.join(default_path(&builder.config.datadir, "share")); + let docdir = prefix.join(default_path(&builder.config.docdir, "share/doc")); + let mandir = prefix.join(default_path(&builder.config.mandir, "share/man")); + let libdir = prefix.join(default_path(&builder.config.libdir, "lib")); + let bindir = prefix.join(&builder.config.bindir); // Default in config.rs let empty_dir = builder.out.join("tmp/empty_dir"); - t!(fs::create_dir_all(&empty_dir)); let mut cmd = Command::new("sh"); cmd.current_dir(&empty_dir) .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh"))) - .arg(format!("--prefix={}", sanitize_sh(&prefix))) - .arg(format!("--sysconfdir={}", sanitize_sh(&sysconfdir))) - .arg(format!("--datadir={}", sanitize_sh(&datadir))) - .arg(format!("--docdir={}", sanitize_sh(&docdir))) - .arg(format!("--bindir={}", sanitize_sh(&bindir))) - .arg(format!("--libdir={}", sanitize_sh(&libdir))) - .arg(format!("--mandir={}", sanitize_sh(&mandir))) + .arg(format!("--prefix={}", prepare_dir(prefix))) + .arg(format!("--sysconfdir={}", prepare_dir(sysconfdir))) + .arg(format!("--datadir={}", prepare_dir(datadir))) + .arg(format!("--docdir={}", prepare_dir(docdir))) + .arg(format!("--bindir={}", prepare_dir(bindir))) + .arg(format!("--libdir={}", prepare_dir(libdir))) + .arg(format!("--mandir={}", prepare_dir(mandir))) .arg("--disable-ldconfig"); builder.run(&mut cmd); t!(fs::remove_dir_all(&empty_dir)); } -fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf { - let mut ret = match *destdir { - Some(ref dest) => dest.clone(), - None => return path.to_path_buf(), - }; - for part in path.components() { - if let Component::Normal(s) = part { - ret.push(s) +fn default_path(config: &Option<PathBuf>, default: &str) -> PathBuf { + PathBuf::from(config.as_ref().cloned().unwrap_or_else(|| PathBuf::from(default))) +} + +fn prepare_dir(mut path: PathBuf) -> String { + // The DESTDIR environment variable is a standard way to install software in a subdirectory + // while keeping the original directory structure, even if the prefix or other directories + // contain absolute paths. + // + // More information on the environment variable is available here: + // https://www.gnu.org/prep/standards/html_node/DESTDIR.html + if let Some(destdir) = env::var_os("DESTDIR").map(PathBuf::from) { + let without_destdir = path.clone(); + path = destdir; + // Custom .join() which ignores disk roots. + for part in without_destdir.components() { + if let Component::Normal(s) = part { + path.push(s) + } } } - ret + + // The installation command is not executed from the current directory, but from a temporary + // directory. To prevent relative paths from breaking this converts relative paths to absolute + // paths. std::fs::canonicalize is not used as that requires the path to actually be present. + if path.is_relative() { + path = std::env::current_dir().expect("failed to get the current directory").join(path); + assert!(path.is_absolute(), "could not make the path relative"); + } + + sanitize_sh(&path) } macro_rules! install { diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index a47ddfbcc1f..88fdcfa2d43 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -1083,7 +1083,7 @@ impl Build { if let Some(ref s) = self.config.description { version.push_str(" ("); version.push_str(s); - version.push_str(")"); + version.push(')'); } version } @@ -1144,7 +1144,7 @@ impl Build { && (dep != "profiler_builtins" || target .map(|t| self.config.profiler_enabled(t)) - .unwrap_or(self.config.any_profiler_enabled())) + .unwrap_or_else(|| self.config.any_profiler_enabled())) && (dep != "rustc_codegen_llvm" || self.config.llvm_enabled()) { list.push(*dep); diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index d716b23af60..6412df3fd90 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -802,6 +802,7 @@ fn supported_sanitizers( }; match &*target.triple { + "aarch64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]), "aarch64-fuchsia" => common_libs("fuchsia", "aarch64", &["asan"]), "aarch64-unknown-linux-gnu" => { common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan"]) diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index acb941d9540..08acc3d671f 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -163,7 +163,11 @@ pub fn check(build: &mut Build) { panic!("the iOS target is only supported on macOS"); } - build.config.target_config.entry(*target).or_insert(Target::from_triple(&target.triple)); + build + .config + .target_config + .entry(*target) + .or_insert_with(|| Target::from_triple(&target.triple)); if target.contains("-none-") || target.contains("nvptx") { if build.no_std(*target) == Some(false) { diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 2d4484c562c..725147767db 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -89,7 +89,7 @@ pub fn setup(src_path: &Path, profile: Profile) { std::process::exit(1); } - let path = cfg_file.unwrap_or("config.toml".into()); + let path = cfg_file.unwrap_or_else(|| "config.toml".into()); let settings = format!( "# Includes one of the default files in src/bootstrap/defaults\n\ profile = \"{}\"\n\ @@ -156,7 +156,7 @@ pub fn interactive_path() -> io::Result<Profile> { io::stdout().flush()?; let mut input = String::new(); io::stdin().read_line(&mut input)?; - if input == "" { + if input.is_empty() { eprintln!("EOF on stdin, when expecting answer to question. Giving up."); std::process::exit(1); } diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 33e252a63c9..2e8c574044e 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1126,7 +1126,19 @@ note: if you're sure you want to do this, please open an issue as to why. In the Ok(path) => path, Err(_) => p, }) - .filter(|p| p.starts_with(suite_path) && (p.is_dir() || p.is_file())) + .filter(|p| p.starts_with(suite_path)) + .filter(|p| { + let exists = p.is_dir() || p.is_file(); + if !exists { + if let Some(p) = p.to_str() { + builder.info(&format!( + "Warning: Skipping \"{}\": not a regular file or directory", + p + )); + } + } + exists + }) .filter_map(|p| { // Since test suite paths are themselves directories, if we don't // specify a directory or file, we'll get an empty string here @@ -1135,7 +1147,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the // flag is respected, so providing an empty --test-args conflicts with // any following it. match p.strip_prefix(suite_path).ok().and_then(|p| p.to_str()) { - Some(s) if s != "" => Some(s), + Some(s) if !s.is_empty() => Some(s), _ => None, } }) diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index 93908e9190e..d03d5c75014 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -31,7 +31,12 @@ with runtime flag `ASAN_OPTIONS=detect_leaks=1` on macOS. AddressSanitizer is supported on the following targets: +* `aarch64-apple-darwin` +* `aarch64-fuchsia` +* `aarch64-unknown-linux-gnu` * `x86_64-apple-darwin` +* `x86_64-fuchsia` +* `x86_64-unknown-freebsd` * `x86_64-unknown-linux-gnu` AddressSanitizer works with non-instrumented code although it will impede its @@ -169,10 +174,26 @@ Shadow byte legend (one shadow byte represents 8 application bytes): ==39249==ABORTING ``` +# LeakSanitizer + +LeakSanitizer is run-time memory leak detector. + +LeakSanitizer is supported on the following targets: + +* `aarch64-apple-darwin` +* `aarch64-unknown-linux-gnu` +* `x86_64-apple-darwin` +* `x86_64-unknown-linux-gnu` + # MemorySanitizer -MemorySanitizer is detector of uninitialized reads. It is only supported on the -`x86_64-unknown-linux-gnu` target. +MemorySanitizer is detector of uninitialized reads. + +MemorySanitizer is supported on the following targets: + +* `aarch64-unknown-linux-gnu` +* `x86_64-unknown-freebsd` +* `x86_64-unknown-linux-gnu` MemorySanitizer requires all program code to be instrumented. C/C++ dependencies need to be recompiled using Clang with `-fsanitize=memory` option. Failing to @@ -219,7 +240,10 @@ $ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu ThreadSanitizer is a data race detection tool. It is supported on the following targets: +* `aarch64-apple-darwin` +* `aarch64-unknown-linux-gnu` * `x86_64-apple-darwin` +* `x86_64-unknown-freebsd` * `x86_64-unknown-linux-gnu` To work correctly ThreadSanitizer needs to be "aware" of all synchronization diff --git a/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md b/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md index 98bcadd12ee..8aca0052147 100644 --- a/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md +++ b/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md @@ -123,7 +123,7 @@ The `rustup` option is guaranteed to install a compatible version of the LLVM to ```shell $ rustup component add llvm-tools-preview $ cargo install cargo-binutils -$ cargo profdata -- --help # note the additional "--" preceeding the tool-specific arguments +$ cargo profdata -- --help # note the additional "--" preceding the tool-specific arguments ``` ## Creating coverage reports diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py index 440181a7611..2f7233685db 100644 --- a/src/etc/htmldocck.py +++ b/src/etc/htmldocck.py @@ -218,7 +218,7 @@ def concat_multi_lines(f): LINE_PATTERN = re.compile(r''' - (?<=(?<!\S)@)(?P<negated>!?) + (?<=(?<!\S))(?P<invalid>!?)@(?P<negated>!?) (?P<cmd>[A-Za-z]+(?:-[A-Za-z]+)*) (?P<args>.*)$ ''', re.X | re.UNICODE) @@ -233,6 +233,16 @@ def get_commands(template): negated = (m.group('negated') == '!') cmd = m.group('cmd') + if m.group('invalid') == '!': + print_err( + lineno, + line, + 'Invalid command: `!@{0}{1}`, (help: try with `@!{1}`)'.format( + '!' if negated else '', + cmd, + ), + ) + continue args = m.group('args') if args and not args[:1].isspace(): print_err(lineno, line, 'Invalid template syntax') diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 2a8b6a321f1..43fb53ba18f 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -84,14 +84,14 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { new_generics }); - let polarity; + let negative_polarity; let new_generics = match result { AutoTraitResult::PositiveImpl(new_generics) => { - polarity = None; + negative_polarity = false; new_generics } AutoTraitResult::NegativeImpl => { - polarity = Some(ImplPolarity::Negative); + negative_polarity = true; // For negative impls, we use the generic params, but *not* the predicates, // from the original type. Otherwise, the displayed impl appears to be a @@ -130,7 +130,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { trait_: Some(trait_ref.clean(self.cx).get_trait_type().unwrap()), for_: ty.clean(self.cx), items: Vec::new(), - polarity, + negative_polarity, synthetic: true, blanket_impl: None, }), @@ -351,8 +351,8 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { if let Some(data) = ty_to_fn.get(&ty) { let (poly_trait, output) = (data.0.as_ref().expect("as_ref failed").clone(), data.1.as_ref().cloned()); - let new_ty = match &poly_trait.trait_ { - &Type::ResolvedPath { + let new_ty = match poly_trait.trait_ { + Type::ResolvedPath { ref path, ref param_names, ref did, @@ -738,11 +738,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { } fn is_fn_ty(&self, tcx: TyCtxt<'_>, ty: &Type) -> bool { - match &ty { - &&Type::ResolvedPath { ref did, .. } => { - *did == tcx.require_lang_item(LangItem::Fn, None) - || *did == tcx.require_lang_item(LangItem::FnMut, None) - || *did == tcx.require_lang_item(LangItem::FnOnce, None) + match ty { + &Type::ResolvedPath { did, .. } => { + did == tcx.require_lang_item(LangItem::Fn, None) + || did == tcx.require_lang_item(LangItem::FnMut, None) + || did == tcx.require_lang_item(LangItem::FnOnce, None) } _ => false, } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index ba3eb007e38..f1c26feea46 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -131,7 +131,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { .in_definition_order() .collect::<Vec<_>>() .clean(self.cx), - polarity: None, + negative_polarity: false, synthetic: false, blanket_impl: Some(trait_ref.self_ty().clean(self.cx)), }), diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 2f169d1d3f3..444b73246da 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -177,10 +177,7 @@ impl Cfg { Cfg::Any(ref sub_cfgs) | Cfg::All(ref sub_cfgs) => { sub_cfgs.first().map(Cfg::should_capitalize_first_letter).unwrap_or(false) } - Cfg::Cfg(name, _) => match name { - sym::debug_assertions | sym::target_endian => true, - _ => false, - }, + Cfg::Cfg(name, _) => name == sym::debug_assertions || name == sym::target_endian, } } @@ -188,18 +185,13 @@ impl Cfg { match *self { Cfg::False | Cfg::True => false, Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true, - Cfg::Not(ref child) => match **child { - Cfg::Cfg(..) => true, - _ => false, - }, + Cfg::Not(box Cfg::Cfg(..)) => true, + Cfg::Not(..) => false, } } fn should_use_with_in_description(&self) -> bool { - match *self { - Cfg::Cfg(name, _) if name == sym::target_feature => true, - _ => false, - } + matches!(self, Cfg::Cfg(sym::target_feature, _)) } /// Attempt to simplify this cfg by assuming that `assume` is already known to be true, will diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index c168c56d30d..4c46771fd71 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -169,7 +169,17 @@ crate fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKin if !s.is_empty() { Some(s) } else { None } }); let fqn = if let clean::TypeKind::Macro = kind { - vec![crate_name, relative.last().expect("relative was empty")] + // Check to see if it is a macro 2.0 or built-in macro + if matches!( + cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())), + LoadedMacro::MacroDef(def, _) + if matches!(&def.kind, ast::ItemKind::MacroDef(ast_def) + if !ast_def.macro_rules) + ) { + once(crate_name).chain(relative).collect() + } else { + vec![crate_name, relative.last().expect("relative was empty")] + } } else { once(crate_name).chain(relative).collect() }; @@ -261,26 +271,12 @@ fn build_union(cx: &DocContext<'_>, did: DefId) -> clean::Union { fn build_type_alias(cx: &DocContext<'_>, did: DefId) -> clean::Typedef { let predicates = cx.tcx.explicit_predicates_of(did); + let type_ = cx.tcx.type_of(did).clean(cx); clean::Typedef { - type_: cx.tcx.type_of(did).clean(cx), + type_, generics: (cx.tcx.generics_of(did), predicates).clean(cx), - item_type: build_type_alias_type(cx, did), - } -} - -fn build_type_alias_type(cx: &DocContext<'_>, did: DefId) -> Option<clean::Type> { - let type_ = cx.tcx.type_of(did).clean(cx); - type_.def_id().and_then(|did| build_ty(cx, did)) -} - -crate fn build_ty(cx: &DocContext<'_>, did: DefId) -> Option<clean::Type> { - match cx.tcx.def_kind(did) { - DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::Const | DefKind::Static => { - Some(cx.tcx.type_of(did).clean(cx)) - } - DefKind::TyAlias => build_type_alias_type(cx, did), - _ => None, + item_type: None, } } @@ -442,7 +438,7 @@ crate fn build_impl( trait_, for_, items: trait_items, - polarity: Some(polarity.clean(cx)), + negative_polarity: polarity.clean(cx), synthetic: false, blanket_impl: None, }), @@ -455,60 +451,51 @@ crate fn build_impl( fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet<DefId>) -> clean::Module { let mut items = Vec::new(); - fill_in(cx, did, &mut items, visited); - return clean::Module { items, is_crate: false }; - - fn fill_in( - cx: &DocContext<'_>, - did: DefId, - items: &mut Vec<clean::Item>, - visited: &mut FxHashSet<DefId>, - ) { - // If we're re-exporting a re-export it may actually re-export something in - // two namespaces, so the target may be listed twice. Make sure we only - // visit each node at most once. - for &item in cx.tcx.item_children(did).iter() { - if item.vis == ty::Visibility::Public { - if let Some(def_id) = item.res.mod_def_id() { - if did == def_id || !visited.insert(def_id) { - continue; - } + + // If we're re-exporting a re-export it may actually re-export something in + // two namespaces, so the target may be listed twice. Make sure we only + // visit each node at most once. + for &item in cx.tcx.item_children(did).iter() { + if item.vis == ty::Visibility::Public { + if let Some(def_id) = item.res.mod_def_id() { + if did == def_id || !visited.insert(def_id) { + continue; } - if let Res::PrimTy(p) = item.res { - // Primitive types can't be inlined so generate an import instead. - items.push(clean::Item { - name: None, - attrs: clean::Attributes::default(), - source: clean::Span::dummy(), - def_id: DefId::local(CRATE_DEF_INDEX), - visibility: clean::Public, - kind: box clean::ImportItem(clean::Import::new_simple( - item.ident.name, - clean::ImportSource { - path: clean::Path { - global: false, - res: item.res, - segments: vec![clean::PathSegment { - name: clean::PrimitiveType::from(p).as_sym(), - args: clean::GenericArgs::AngleBracketed { - args: Vec::new(), - bindings: Vec::new(), - }, - }], - }, - did: None, + } + if let Res::PrimTy(p) = item.res { + // Primitive types can't be inlined so generate an import instead. + items.push(clean::Item { + name: None, + attrs: clean::Attributes::default(), + source: clean::Span::dummy(), + def_id: DefId::local(CRATE_DEF_INDEX), + visibility: clean::Public, + kind: box clean::ImportItem(clean::Import::new_simple( + item.ident.name, + clean::ImportSource { + path: clean::Path { + global: false, + res: item.res, + segments: vec![clean::PathSegment { + name: clean::PrimitiveType::from(p).as_sym(), + args: clean::GenericArgs::AngleBracketed { + args: Vec::new(), + bindings: Vec::new(), + }, + }], }, - true, - )), - }); - } else if let Some(i) = - try_inline(cx, did, item.res, item.ident.name, None, visited) - { - items.extend(i) - } + did: None, + }, + true, + )), + }); + } else if let Some(i) = try_inline(cx, did, item.res, item.ident.name, None, visited) { + items.extend(i) } } } + + clean::Module { items, is_crate: false } } crate fn print_inlined_const(cx: &DocContext<'_>, did: DefId) -> String { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2f430842f9d..14564e7f64a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -640,10 +640,10 @@ impl Clean<Generics> for hir::Generics<'_> { /// /// [`lifetime_to_generic_param`]: rustc_ast_lowering::LoweringContext::lifetime_to_generic_param fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool { - match param.kind { - hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided } => true, - _ => false, - } + matches!( + param.kind, + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided } + ) } let impl_trait_params = self @@ -801,7 +801,7 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics, ty::GenericPredicates<'tcx for (param, mut bounds) in impl_trait { // Move trait bounds to the front. - bounds.sort_by_key(|b| if let GenericBound::TraitBound(..) = b { false } else { true }); + bounds.sort_by_key(|b| !matches!(b, GenericBound::TraitBound(..))); if let crate::core::ImplTraitParam::ParamIndex(idx) = param { if let Some(proj) = impl_trait_proj.remove(&idx) { @@ -942,7 +942,7 @@ impl<'a> Clean<Arguments> for (&'a [hir::Ty<'a>], &'a [Ident]) { .iter() .enumerate() .map(|(i, ty)| { - let mut name = self.1.get(i).map(|ident| ident.name).unwrap_or(kw::Empty); + let mut name = self.1.get(i).map_or(kw::Empty, |ident| ident.name); if name.is_empty() { name = kw::Underscore; } @@ -963,7 +963,7 @@ impl<'a> Clean<Arguments> for (&'a [hir::Ty<'a>], hir::BodyId) { .iter() .enumerate() .map(|(i, ty)| Argument { - name: name_from_pat(&body.params[i].pat), + name: Symbol::intern(&rustc_hir_pretty::param_to_string(&body.params[i])), type_: ty.clean(cx), }) .collect(), @@ -1001,7 +1001,7 @@ impl<'tcx> Clean<FnDecl> for (DefId, ty::PolyFnSig<'tcx>) { .iter() .map(|t| Argument { type_: t.clean(cx), - name: names.next().map(|i| i.name).unwrap_or(kw::Empty), + name: names.next().map_or(kw::Empty, |i| i.name), }) .collect(), }, @@ -1119,10 +1119,17 @@ impl Clean<Item> for hir::ImplItem<'_> { } MethodItem(m, Some(self.defaultness)) } - hir::ImplItemKind::TyAlias(ref ty) => { - let type_ = ty.clean(cx); - let item_type = type_.def_id().and_then(|did| inline::build_ty(cx, did)); - TypedefItem(Typedef { type_, generics: Generics::default(), item_type }, true) + hir::ImplItemKind::TyAlias(ref hir_ty) => { + let type_ = hir_ty.clean(cx); + let item_type = hir_ty_to_ty(cx.tcx, hir_ty).clean(cx); + TypedefItem( + Typedef { + type_, + generics: Generics::default(), + item_type: Some(item_type), + }, + true, + ) } }; Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx) @@ -1268,13 +1275,13 @@ impl Clean<Item> for ty::AssocItem { AssocTypeItem(bounds, ty.clean(cx)) } else { + // FIXME: when could this happen? ASsociated items in inherent impls? let type_ = cx.tcx.type_of(self.def_id).clean(cx); - let item_type = type_.def_id().and_then(|did| inline::build_ty(cx, did)); TypedefItem( Typedef { type_, generics: Generics { params: Vec::new(), where_predicates: Vec::new() }, - item_type, + item_type: None, }, true, ) @@ -1987,11 +1994,15 @@ impl Clean<Vec<Item>> for (&hir::Item<'_>, Option<Symbol>) { bounds: ty.bounds.clean(cx), generics: ty.generics.clean(cx), }), - ItemKind::TyAlias(ty, ref generics) => { - let rustdoc_ty = ty.clean(cx); - let item_type = rustdoc_ty.def_id().and_then(|did| inline::build_ty(cx, did)); + ItemKind::TyAlias(hir_ty, ref generics) => { + let rustdoc_ty = hir_ty.clean(cx); + let ty = hir_ty_to_ty(cx.tcx, hir_ty).clean(cx); TypedefItem( - Typedef { type_: rustdoc_ty, generics: generics.clean(cx), item_type }, + Typedef { + type_: rustdoc_ty, + generics: generics.clean(cx), + item_type: Some(ty), + }, false, ) } @@ -2058,13 +2069,14 @@ impl Clean<Item> for hir::Variant<'_> { } } -impl Clean<ImplPolarity> for ty::ImplPolarity { - fn clean(&self, _: &DocContext<'_>) -> ImplPolarity { +impl Clean<bool> for ty::ImplPolarity { + /// Returns whether the impl has negative polarity. + fn clean(&self, _: &DocContext<'_>) -> bool { match self { &ty::ImplPolarity::Positive | // FIXME: do we want to do something else here? - &ty::ImplPolarity::Reservation => ImplPolarity::Positive, - &ty::ImplPolarity::Negative => ImplPolarity::Negative, + &ty::ImplPolarity::Reservation => false, + &ty::ImplPolarity::Negative => true, } } } @@ -2105,7 +2117,7 @@ fn clean_impl(impl_: &hir::Item<'_>, cx: &DocContext<'_>) -> Vec<Item> { trait_, for_, items, - polarity: Some(cx.tcx.impl_polarity(def_id).clean(cx)), + negative_polarity: cx.tcx.impl_polarity(def_id).clean(cx), synthetic: false, blanket_impl: None, }); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 0d33bc9afd5..7e567bedc78 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -122,7 +122,7 @@ impl Item { /// Finds the `doc` attribute as a NameValue and returns the corresponding /// value found. - crate fn doc_value(&self) -> Option<&str> { + crate fn doc_value(&self) -> Option<String> { self.attrs.doc_value() } @@ -175,11 +175,11 @@ impl Item { } crate fn is_crate(&self) -> bool { - match *self.kind { + matches!( + *self.kind, StrippedItem(box ModuleItem(Module { is_crate: true, .. })) - | ModuleItem(Module { is_crate: true, .. }) => true, - _ => false, - } + | ModuleItem(Module { is_crate: true, .. }) + ) } crate fn is_mod(&self) -> bool { self.type_() == ItemType::Module @@ -293,7 +293,9 @@ impl Item { } } - /// See comments on next_def_id + /// See the documentation for [`next_def_id()`]. + /// + /// [`next_def_id()`]: DocContext::next_def_id() crate fn is_fake(&self) -> bool { MAX_DEF_ID.with(|m| { m.borrow().get(&self.def_id.krate).map(|id| self.def_id >= *id).unwrap_or(false) @@ -334,6 +336,10 @@ crate enum ItemKind { ProcMacroItem(ProcMacro), PrimitiveItem(PrimitiveType), AssocConstItem(Type, Option<String>), + /// An associated item in a trait or trait impl. + /// + /// The bounds may be non-empty if there is a `where` clause. + /// The `Option<Type>` is the default concrete type (e.g. `trait Trait { type Target = usize; }`) AssocTypeItem(Vec<GenericBound>, Option<Type>), /// An item that has been stripped by a rustdoc pass StrippedItem(Box<ItemKind>), @@ -378,10 +384,7 @@ impl ItemKind { } crate fn is_type_alias(&self) -> bool { - match *self { - ItemKind::TypedefItem(_, _) | ItemKind::AssocTypeItem(_, _) => true, - _ => false, - } + matches!(self, ItemKind::TypedefItem(..) | ItemKind::AssocTypeItem(..)) } } @@ -474,11 +477,13 @@ crate struct DocFragment { /// This allows distinguishing between the original documentation and a pub re-export. /// If it is `None`, the item was not re-exported. crate parent_module: Option<DefId>, - crate doc: String, + crate doc: Symbol, crate kind: DocFragmentKind, + crate need_backline: bool, + crate indent: usize, } -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] crate enum DocFragmentKind { /// A doc fragment created from a `///` or `//!` doc comment. SugaredDoc, @@ -486,7 +491,33 @@ crate enum DocFragmentKind { RawDoc, /// A doc fragment created from a `#[doc(include="filename")]` attribute. Contains both the /// given filename and the file contents. - Include { filename: String }, + Include { filename: Symbol }, +} + +// The goal of this function is to apply the `DocFragment` transformations that are required when +// transforming into the final markdown. So the transformations in here are: +// +// * Applying the computed indent to each lines in each doc fragment (a `DocFragment` can contain +// multiple lines in case of `#[doc = ""]`). +// * Adding backlines between `DocFragment`s and adding an extra one if required (stored in the +// `need_backline` field). +fn add_doc_fragment(out: &mut String, frag: &DocFragment) { + let s = frag.doc.as_str(); + let mut iter = s.lines().peekable(); + while let Some(line) = iter.next() { + if line.chars().any(|c| !c.is_whitespace()) { + assert!(line.len() >= frag.indent); + out.push_str(&line[frag.indent..]); + } else { + out.push_str(line); + } + if iter.peek().is_some() { + out.push('\n'); + } + } + if frag.need_backline { + out.push('\n'); + } } impl<'a> FromIterator<&'a DocFragment> for String { @@ -494,11 +525,18 @@ impl<'a> FromIterator<&'a DocFragment> for String { where T: IntoIterator<Item = &'a DocFragment>, { + let mut prev_kind: Option<DocFragmentKind> = None; iter.into_iter().fold(String::new(), |mut acc, frag| { - if !acc.is_empty() { + if !acc.is_empty() + && prev_kind + .take() + .map(|p| matches!(p, DocFragmentKind::Include { .. }) && p != frag.kind) + .unwrap_or(false) + { acc.push('\n'); } - acc.push_str(&frag.doc); + add_doc_fragment(&mut acc, &frag); + prev_kind = Some(frag.kind); acc }) } @@ -570,7 +608,7 @@ impl Attributes { /// Reads a `MetaItem` from within an attribute, looks for whether it is a /// `#[doc(include="file")]`, and returns the filename and contents of the file as loaded from /// its expansion. - crate fn extract_include(mi: &ast::MetaItem) -> Option<(String, String)> { + crate fn extract_include(mi: &ast::MetaItem) -> Option<(Symbol, Symbol)> { mi.meta_item_list().and_then(|list| { for meta in list { if meta.has_name(sym::include) { @@ -578,17 +616,17 @@ impl Attributes { // `#[doc(include(file="filename", contents="file contents")]` so we need to // look for that instead return meta.meta_item_list().and_then(|list| { - let mut filename: Option<String> = None; - let mut contents: Option<String> = None; + let mut filename: Option<Symbol> = None; + let mut contents: Option<Symbol> = None; for it in list { if it.has_name(sym::file) { if let Some(name) = it.value_str() { - filename = Some(name.to_string()); + filename = Some(name); } } else if it.has_name(sym::contents) { if let Some(docs) = it.value_str() { - contents = Some(docs.to_string()); + contents = Some(docs); } } } @@ -627,15 +665,30 @@ impl Attributes { attrs: &[ast::Attribute], additional_attrs: Option<(&[ast::Attribute], DefId)>, ) -> Attributes { - let mut doc_strings = vec![]; + let mut doc_strings: Vec<DocFragment> = vec![]; let mut sp = None; let mut cfg = Cfg::True; let mut doc_line = 0; + fn update_need_backline(doc_strings: &mut Vec<DocFragment>, frag: &DocFragment) { + if let Some(prev) = doc_strings.last_mut() { + if matches!(prev.kind, DocFragmentKind::Include { .. }) + || prev.kind != frag.kind + || prev.parent_module != frag.parent_module + { + // add a newline for extra padding between segments + prev.need_backline = prev.kind == DocFragmentKind::SugaredDoc + || prev.kind == DocFragmentKind::RawDoc + } else { + prev.need_backline = true; + } + } + } + let clean_attr = |(attr, parent_module): (&ast::Attribute, _)| { if let Some(value) = attr.doc_str() { trace!("got doc_str={:?}", value); - let value = beautify_doc_string(value).to_string(); + let value = beautify_doc_string(value); let kind = if attr.is_doc_comment() { DocFragmentKind::SugaredDoc } else { @@ -643,14 +696,20 @@ impl Attributes { }; let line = doc_line; - doc_line += value.lines().count(); - doc_strings.push(DocFragment { + doc_line += value.as_str().lines().count(); + let frag = DocFragment { line, span: attr.span, doc: value, kind, parent_module, - }); + need_backline: false, + indent: 0, + }; + + update_need_backline(&mut doc_strings, &frag); + + doc_strings.push(frag); if sp.is_none() { sp = Some(attr.span); @@ -668,14 +727,18 @@ impl Attributes { } else if let Some((filename, contents)) = Attributes::extract_include(&mi) { let line = doc_line; - doc_line += contents.lines().count(); - doc_strings.push(DocFragment { + doc_line += contents.as_str().lines().count(); + let frag = DocFragment { line, span: attr.span, doc: contents, kind: DocFragmentKind::Include { filename }, - parent_module: parent_module, - }); + parent_module, + need_backline: false, + indent: 0, + }; + update_need_backline(&mut doc_strings, &frag); + doc_strings.push(frag); } } } @@ -726,14 +789,41 @@ impl Attributes { /// Finds the `doc` attribute as a NameValue and returns the corresponding /// value found. - crate fn doc_value(&self) -> Option<&str> { - self.doc_strings.first().map(|s| s.doc.as_str()) + crate fn doc_value(&self) -> Option<String> { + let mut iter = self.doc_strings.iter(); + + let ori = iter.next()?; + let mut out = String::new(); + add_doc_fragment(&mut out, &ori); + while let Some(new_frag) = iter.next() { + if matches!(ori.kind, DocFragmentKind::Include { .. }) + || new_frag.kind != ori.kind + || new_frag.parent_module != ori.parent_module + { + break; + } + add_doc_fragment(&mut out, &new_frag); + } + if out.is_empty() { None } else { Some(out) } + } + + /// Return the doc-comments on this item, grouped by the module they came from. + /// + /// The module can be different if this is a re-export with added documentation. + crate fn collapsed_doc_value_by_module_level(&self) -> FxHashMap<Option<DefId>, String> { + let mut ret = FxHashMap::default(); + + for new_frag in self.doc_strings.iter() { + let out = ret.entry(new_frag.parent_module).or_default(); + add_doc_fragment(out, &new_frag); + } + ret } /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined /// with newlines. crate fn collapsed_doc_value(&self) -> Option<String> { - if !self.doc_strings.is_empty() { Some(self.doc_strings.iter().collect()) } else { None } + if self.doc_strings.is_empty() { None } else { Some(self.doc_strings.iter().collect()) } } /// Gets links as a vector @@ -750,7 +840,7 @@ impl Attributes { Some(did) => { if let Some((mut href, ..)) = href(did) { if let Some(ref fragment) = *fragment { - href.push_str("#"); + href.push('#'); href.push_str(fragment); } Some(RenderedLink { @@ -945,10 +1035,7 @@ crate enum GenericParamDefKind { impl GenericParamDefKind { crate fn is_type(&self) -> bool { - match *self { - GenericParamDefKind::Type { .. } => true, - _ => false, - } + matches!(self, GenericParamDefKind::Type { .. }) } // FIXME(eddyb) this either returns the default of a type parameter, or the @@ -1141,6 +1228,7 @@ crate enum Type { BareFunction(Box<BareFunctionDecl>), Tuple(Vec<Type>), Slice(Box<Type>), + /// The `String` field is about the size or the constant representing the array's length. Array(Box<Type>, String), Never, RawPointer(Mutability, Box<Type>), @@ -1292,15 +1380,12 @@ impl Type { } crate fn is_full_generic(&self) -> bool { - match *self { - Type::Generic(_) => true, - _ => false, - } + matches!(self, Type::Generic(_)) } crate fn projection(&self) -> Option<(&Type, DefId, Symbol)> { let (self_, trait_, name) = match self { - QPath { ref self_type, ref trait_, name } => (self_type, trait_, name), + QPath { self_type, trait_, name } => (self_type, trait_, name), _ => return None, }; let trait_did = match **trait_ { @@ -1728,7 +1813,12 @@ crate struct PathSegment { crate struct Typedef { crate type_: Type, crate generics: Generics, - // Type of target item. + /// `type_` can come from either the HIR or from metadata. If it comes from HIR, it may be a type + /// alias instead of the final type. This will always have the final type, regardless of whether + /// `type_` came from HIR or from metadata. + /// + /// If `item_type.is_none()`, `type_` is guarenteed to come from metadata (and therefore hold the + /// final type). crate item_type: Option<Type>, } @@ -1770,12 +1860,6 @@ crate struct Constant { crate is_literal: bool, } -#[derive(Clone, PartialEq, Debug)] -crate enum ImplPolarity { - Positive, - Negative, -} - #[derive(Clone, Debug)] crate struct Impl { crate unsafety: hir::Unsafety, @@ -1784,7 +1868,7 @@ crate struct Impl { crate trait_: Option<Type>, crate for_: Type, crate items: Vec<Item>, - crate polarity: Option<ImplPolarity>, + crate negative_polarity: bool, crate synthetic: bool, crate blanket_impl: Option<Type>, } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index d4482d6fa90..0f5495c8310 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -179,12 +179,12 @@ crate fn get_real_types( if arg.is_full_generic() { let arg_s = Symbol::intern(&arg.print().to_string()); if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g { - &WherePredicate::BoundPredicate { ref ty, .. } => ty.def_id() == arg.def_id(), + WherePredicate::BoundPredicate { ty, .. } => ty.def_id() == arg.def_id(), _ => false, }) { let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]); for bound in bounds.iter() { - if let GenericBound::TraitBound(ref poly_trait, _) = *bound { + if let GenericBound::TraitBound(poly_trait, _) = bound { for x in poly_trait.generic_params.iter() { if !x.is_type() { continue; @@ -314,25 +314,6 @@ crate fn strip_path(path: &Path) -> Path { Path { global: path.global, res: path.res, segments } } -crate fn qpath_to_string(p: &hir::QPath<'_>) -> String { - let segments = match *p { - hir::QPath::Resolved(_, ref path) => &path.segments, - hir::QPath::TypeRelative(_, ref segment) => return segment.ident.to_string(), - hir::QPath::LangItem(lang_item, ..) => return lang_item.name().to_string(), - }; - - let mut s = String::new(); - for (i, seg) in segments.iter().enumerate() { - if i > 0 { - s.push_str("::"); - } - if seg.ident.name != kw::PathRoot { - s.push_str(&seg.ident.as_str()); - } - } - s -} - crate fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut Vec<Item>) { let tcx = cx.tcx; @@ -376,57 +357,6 @@ impl ToSource for rustc_span::Span { } } -crate fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { - use rustc_hir::*; - debug!("trying to get a name from pattern: {:?}", p); - - Symbol::intern(&match p.kind { - PatKind::Wild => return kw::Underscore, - PatKind::Binding(_, _, ident, _) => return ident.name, - PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p), - PatKind::Struct(ref name, ref fields, etc) => format!( - "{} {{ {}{} }}", - qpath_to_string(name), - fields - .iter() - .map(|fp| format!("{}: {}", fp.ident, name_from_pat(&fp.pat))) - .collect::<Vec<String>>() - .join(", "), - if etc { ", .." } else { "" } - ), - PatKind::Or(ref pats) => pats - .iter() - .map(|p| name_from_pat(&**p).to_string()) - .collect::<Vec<String>>() - .join(" | "), - PatKind::Tuple(ref elts, _) => format!( - "({})", - elts.iter() - .map(|p| name_from_pat(&**p).to_string()) - .collect::<Vec<String>>() - .join(", ") - ), - PatKind::Box(ref p) => return name_from_pat(&**p), - PatKind::Ref(ref p, _) => return name_from_pat(&**p), - PatKind::Lit(..) => { - warn!( - "tried to get argument name from PatKind::Lit, which is silly in function arguments" - ); - return Symbol::intern("()"); - } - PatKind::Range(..) => panic!( - "tried to get argument name from PatKind::Range, \ - which is not allowed in function arguments" - ), - PatKind::Slice(ref begin, ref mid, ref end) => { - let begin = begin.iter().map(|p| name_from_pat(&**p).to_string()); - let mid = mid.as_ref().map(|p| format!("..{}", name_from_pat(&**p))).into_iter(); - let end = end.iter().map(|p| name_from_pat(&**p).to_string()); - format!("[{}]", begin.chain(mid).chain(end).collect::<Vec<_>>().join(", ")) - } - }) -} - crate fn print_const(cx: &DocContext<'_>, n: &'tcx ty::Const<'_>) -> String { match n.val { ty::ConstKind::Unevaluated(def, _, promoted) => { diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 2d58614b139..e43ea965c04 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -37,10 +37,7 @@ crate enum OutputFormat { impl OutputFormat { crate fn is_json(&self) -> bool { - match self { - OutputFormat::Json => true, - _ => false, - } + matches!(self, OutputFormat::Json) } } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 7e85342ac7d..4db5a0bccc8 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -120,14 +120,20 @@ impl<'tcx> DocContext<'tcx> { r } - // This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly - // refactoring either librustdoc or librustc_middle. In particular, allowing new DefIds to be - // registered after the AST is constructed would require storing the defid mapping in a - // RefCell, decreasing the performance for normal compilation for very little gain. - // - // Instead, we construct 'fake' def ids, which start immediately after the last DefId. - // In the Debug impl for clean::Item, we explicitly check for fake - // def ids, as we'll end up with a panic if we use the DefId Debug impl for fake DefIds + /// Create a new "fake" [`DefId`]. + /// + /// This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly + /// refactoring either rustdoc or [`rustc_middle`]. In particular, allowing new [`DefId`]s + /// to be registered after the AST is constructed would require storing the [`DefId`] mapping + /// in a [`RefCell`], decreasing the performance for normal compilation for very little gain. + /// + /// Instead, we construct "fake" [`DefId`]s, which start immediately after the last `DefId`. + /// In the [`Debug`] impl for [`clean::Item`], we explicitly check for fake `DefId`s, + /// as we'll end up with a panic if we use the `DefId` `Debug` impl for fake `DefId`s. + /// + /// [`RefCell`]: std::cell::RefCell + /// [`Debug`]: std::fmt::Debug + /// [`clean::Item`]: crate::clean::types::Item crate fn next_def_id(&self, crate_num: CrateNum) -> DefId { let start_def_id = { let num_def_ids = if crate_num == LOCAL_CRATE { @@ -428,16 +434,15 @@ crate fn create_resolver<'a>( sess.time("load_extern_crates", || { for extern_name in &extern_names { debug!("loading extern crate {}", extern_name); - resolver + if let Err(()) = resolver .resolve_str_path_error( DUMMY_SP, extern_name, TypeNS, LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(), - ) - .unwrap_or_else(|()| { - panic!("Unable to resolve external crate {}", extern_name) - }); + ) { + warn!("unable to resolve external crate {} (do you have an unused `--extern` crate?)", extern_name) + } } }); }); @@ -525,7 +530,7 @@ crate fn run_global_ctxt( let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt)); if let Some(ref m) = krate.module { - if let None | Some("") = m.doc_value() { + if m.doc_value().map(|d| d.is_empty()).unwrap_or(true) { let help = "The following guide may be of use:\n\ https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html"; tcx.struct_lint_node( @@ -623,6 +628,9 @@ crate fn run_global_ctxt( ctxt.sess().abort_if_errors(); + // The main crate doc comments are always collapsed. + krate.collapsed = true; + (krate, ctxt.renderinfo.into_inner(), ctxt.render_options) } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 02dd42ce0c1..09627be9701 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -636,15 +636,15 @@ fn partition_source(s: &str) -> (String, String, String) { match state { PartitionState::Attrs => { before.push_str(line); - before.push_str("\n"); + before.push('\n'); } PartitionState::Crates => { crates.push_str(line); - crates.push_str("\n"); + crates.push('\n'); } PartitionState::Other => { after.push_str(line); - after.push_str("\n"); + after.push('\n'); } } } @@ -987,7 +987,6 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { self.collector.names.push(name); } - attrs.collapse_doc_comments(); attrs.unindent_doc_comments(); // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with // anything else, this will combine them for us. diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index c39cc3ca397..4d45c8866a7 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -61,7 +61,7 @@ crate trait DocFolder: Sized { j.fields = j.fields.into_iter().filter_map(|x| self.fold_item(x)).collect(); j.fields_stripped |= num_fields != j.fields.len() || j.fields.iter().any(|f| f.is_stripped()); - VariantItem(Variant { kind: VariantKind::Struct(j), ..i2 }) + VariantItem(Variant { kind: VariantKind::Struct(j) }) } _ => VariantItem(i2), } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 899d61d8e43..c1f8b12cac4 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -316,7 +316,7 @@ impl DocFolder for Cache { path: path.join("::"), desc: item .doc_value() - .map_or_else(|| String::new(), short_markdown_summary), + .map_or_else(String::new, |x| short_markdown_summary(&x.as_str())), parent, parent_idx: None, search_type: get_index_search_type(&item), diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 9b2fb8582f5..5c2adca3126 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -245,7 +245,7 @@ impl<'a> fmt::Display for WhereClause<'a> { } match pred { - &clean::WherePredicate::BoundPredicate { ref ty, ref bounds } => { + clean::WherePredicate::BoundPredicate { ty, bounds } => { let bounds = bounds; if f.alternate() { clause.push_str(&format!( @@ -261,7 +261,7 @@ impl<'a> fmt::Display for WhereClause<'a> { )); } } - &clean::WherePredicate::RegionPredicate { ref lifetime, ref bounds } => { + clean::WherePredicate::RegionPredicate { lifetime, bounds } => { clause.push_str(&format!( "{}: {}", lifetime.print(), @@ -272,7 +272,7 @@ impl<'a> fmt::Display for WhereClause<'a> { .join(" + ") )); } - &clean::WherePredicate::EqPredicate { ref lhs, ref rhs } => { + clean::WherePredicate::EqPredicate { lhs, rhs } => { if f.alternate() { clause.push_str(&format!("{:#} == {:#}", lhs.print(), rhs.print())); } else { @@ -376,8 +376,8 @@ impl clean::GenericBound { impl clean::GenericArgs { fn print(&self) -> impl fmt::Display + '_ { display_fn(move |f| { - match *self { - clean::GenericArgs::AngleBracketed { ref args, ref bindings } => { + match self { + clean::GenericArgs::AngleBracketed { args, bindings } => { if !args.is_empty() || !bindings.is_empty() { if f.alternate() { f.write_str("<")?; @@ -414,7 +414,7 @@ impl clean::GenericArgs { } } } - clean::GenericArgs::Parenthesized { ref inputs, ref output } => { + clean::GenericArgs::Parenthesized { inputs, output } => { f.write_str("(")?; let mut comma = false; for ty in inputs { @@ -501,7 +501,7 @@ crate fn href(did: DefId) -> Option<(String, ItemType, Vec<String>)> { }; for component in &fqp[..fqp.len() - 1] { url.push_str(component); - url.push_str("/"); + url.push('/'); } match shortty { ItemType::Module => { @@ -510,7 +510,7 @@ crate fn href(did: DefId) -> Option<(String, ItemType, Vec<String>)> { } _ => { url.push_str(shortty.as_str()); - url.push_str("."); + url.push('.'); url.push_str(fqp.last().unwrap()); url.push_str(".html"); } @@ -870,7 +870,7 @@ impl clean::Impl { } if let Some(ref ty) = self.trait_ { - if self.polarity == Some(clean::ImplPolarity::Negative) { + if self.negative_polarity { write!(f, "!")?; } @@ -1021,7 +1021,7 @@ impl Function<'_> { } else { if i > 0 { args.push_str(" <br>"); - args_plain.push_str(" "); + args_plain.push(' '); } if !input.name.is_empty() { args.push_str(&format!("{}: ", input.name)); diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 20cf48915c3..33639055b59 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -25,6 +25,7 @@ use rustc_session::lint; use rustc_span::edition::Edition; use rustc_span::Span; use std::borrow::Cow; +use std::cell::RefCell; use std::collections::VecDeque; use std::default::Default; use std::fmt::Write; @@ -36,7 +37,9 @@ use crate::doctest; use crate::html::highlight; use crate::html::toc::TocBuilder; -use pulldown_cmark::{html, BrokenLink, CodeBlockKind, CowStr, Event, Options, Parser, Tag}; +use pulldown_cmark::{ + html, BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, +}; #[cfg(test)] mod tests; @@ -326,8 +329,6 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> { type Item = Event<'a>; fn next(&mut self) -> Option<Self::Item> { - use pulldown_cmark::LinkType; - let mut event = self.inner.next(); // Replace intra-doc links and remove disambiguators from shortcut links (`[fn@f]`). @@ -414,11 +415,13 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> { } } +type SpannedEvent<'a> = (Event<'a>, Range<usize>); + /// Make headings links with anchor IDs and build up TOC. struct HeadingLinks<'a, 'b, 'ids, I> { inner: I, toc: Option<&'b mut TocBuilder>, - buf: VecDeque<Event<'a>>, + buf: VecDeque<SpannedEvent<'a>>, id_map: &'ids mut IdMap, } @@ -428,8 +431,10 @@ impl<'a, 'b, 'ids, I> HeadingLinks<'a, 'b, 'ids, I> { } } -impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a, 'b, 'ids, I> { - type Item = Event<'a>; +impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator + for HeadingLinks<'a, 'b, 'ids, I> +{ + type Item = SpannedEvent<'a>; fn next(&mut self) -> Option<Self::Item> { if let Some(e) = self.buf.pop_front() { @@ -437,31 +442,29 @@ impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a, } let event = self.inner.next(); - if let Some(Event::Start(Tag::Heading(level))) = event { + if let Some((Event::Start(Tag::Heading(level)), _)) = event { let mut id = String::new(); for event in &mut self.inner { - match &event { + match &event.0 { Event::End(Tag::Heading(..)) => break, + Event::Start(Tag::Link(_, _, _)) | Event::End(Tag::Link(..)) => {} Event::Text(text) | Event::Code(text) => { id.extend(text.chars().filter_map(slugify)); + self.buf.push_back(event); } - _ => {} - } - match event { - Event::Start(Tag::Link(_, _, _)) | Event::End(Tag::Link(..)) => {} - event => self.buf.push_back(event), + _ => self.buf.push_back(event), } } let id = self.id_map.derive(id); if let Some(ref mut builder) = self.toc { let mut html_header = String::new(); - html::push_html(&mut html_header, self.buf.iter().cloned()); + html::push_html(&mut html_header, self.buf.iter().map(|(ev, _)| ev.clone())); let sec = builder.push(level as u32, html_header, id.clone()); - self.buf.push_front(Event::Html(format!("{} ", sec).into())); + self.buf.push_front((Event::Html(format!("{} ", sec).into()), 0..0)); } - self.buf.push_back(Event::Html(format!("</a></h{}>", level).into())); + self.buf.push_back((Event::Html(format!("</a></h{}>", level).into()), 0..0)); let start_tags = format!( "<h{level} id=\"{id}\" class=\"section-header\">\ @@ -469,7 +472,7 @@ impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a, id = id, level = level ); - return Some(Event::Html(start_tags.into())); + return Some((Event::Html(start_tags.into()), 0..0)); } event } @@ -489,15 +492,10 @@ impl<'a, I: Iterator<Item = Event<'a>>> SummaryLine<'a, I> { } fn check_if_allowed_tag(t: &Tag<'_>) -> bool { - match *t { - Tag::Paragraph - | Tag::Item - | Tag::Emphasis - | Tag::Strong - | Tag::Link(..) - | Tag::BlockQuote => true, - _ => false, - } + matches!( + t, + Tag::Paragraph | Tag::Item | Tag::Emphasis | Tag::Strong | Tag::Link(..) | Tag::BlockQuote + ) } impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> { @@ -560,23 +558,23 @@ impl<'a, I> Footnotes<'a, I> { } } -impl<'a, I: Iterator<Item = Event<'a>>> Iterator for Footnotes<'a, I> { - type Item = Event<'a>; +impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> { + type Item = SpannedEvent<'a>; fn next(&mut self) -> Option<Self::Item> { loop { match self.inner.next() { - Some(Event::FootnoteReference(ref reference)) => { + Some((Event::FootnoteReference(ref reference), range)) => { let entry = self.get_entry(&reference); let reference = format!( "<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>", (*entry).1 ); - return Some(Event::Html(reference.into())); + return Some((Event::Html(reference.into()), range)); } - Some(Event::Start(Tag::FootnoteDefinition(def))) => { + Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => { let mut content = Vec::new(); - for event in &mut self.inner { + for (event, _) in &mut self.inner { if let Event::End(Tag::FootnoteDefinition(..)) = event { break; } @@ -607,7 +605,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for Footnotes<'a, I> { ret.push_str("</li>"); } ret.push_str("</ol></div>"); - return Some(Event::Html(ret.into())); + return Some((Event::Html(ret.into()), 0..0)); } else { return None; } @@ -917,13 +915,14 @@ impl Markdown<'_> { }; let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut replacer)); + let p = p.into_offset_iter(); let mut s = String::with_capacity(md.len() * 3 / 2); let p = HeadingLinks::new(p, None, &mut ids); - let p = LinkReplacer::new(p, links); - let p = CodeBlocks::new(p, codes, edition, playground); let p = Footnotes::new(p); + let p = LinkReplacer::new(p.map(|(ev, _)| ev), links); + let p = CodeBlocks::new(p, codes, edition, playground); html::push_html(&mut s, p); s @@ -934,7 +933,7 @@ impl MarkdownWithToc<'_> { crate fn into_string(self) -> String { let MarkdownWithToc(md, mut ids, codes, edition, playground) = self; - let p = Parser::new_ext(md, opts()); + let p = Parser::new_ext(md, opts()).into_offset_iter(); let mut s = String::with_capacity(md.len() * 3 / 2); @@ -942,8 +941,8 @@ impl MarkdownWithToc<'_> { { let p = HeadingLinks::new(p, Some(&mut toc), &mut ids); - let p = CodeBlocks::new(p, codes, edition, playground); let p = Footnotes::new(p); + let p = CodeBlocks::new(p.map(|(ev, _)| ev), codes, edition, playground); html::push_html(&mut s, p); } @@ -959,19 +958,19 @@ impl MarkdownHtml<'_> { if md.is_empty() { return String::new(); } - let p = Parser::new_ext(md, opts()); + let p = Parser::new_ext(md, opts()).into_offset_iter(); // Treat inline HTML as plain text. - let p = p.map(|event| match event { - Event::Html(text) => Event::Text(text), + let p = p.map(|event| match event.0 { + Event::Html(text) => (Event::Text(text), event.1), _ => event, }); let mut s = String::with_capacity(md.len() * 3 / 2); let p = HeadingLinks::new(p, None, &mut ids); - let p = CodeBlocks::new(p, codes, edition, playground); let p = Footnotes::new(p); + let p = CodeBlocks::new(p.map(|(ev, _)| ev), codes, edition, playground); html::push_html(&mut s, p); s @@ -1124,56 +1123,75 @@ crate fn plain_text_summary(md: &str) -> String { s } -crate fn markdown_links(md: &str) -> Vec<(String, Option<Range<usize>>)> { +crate struct MarkdownLink { + pub kind: LinkType, + pub link: String, + pub range: Range<usize>, +} + +crate fn markdown_links(md: &str) -> Vec<MarkdownLink> { if md.is_empty() { return vec![]; } - let mut links = vec![]; - let mut shortcut_links = vec![]; - - { - let locate = |s: &str| unsafe { - let s_start = s.as_ptr(); - let s_end = s_start.add(s.len()); - let md_start = md.as_ptr(); - let md_end = md_start.add(md.len()); - if md_start <= s_start && s_end <= md_end { - let start = s_start.offset_from(md_start) as usize; - let end = s_end.offset_from(md_start) as usize; - Some(start..end) - } else { - None - } - }; - - let mut push = |link: BrokenLink<'_>| { - // FIXME: use `link.span` instead of `locate` - // (doing it now includes the `[]` as well as the text) - shortcut_links.push((link.reference.to_owned(), locate(link.reference))); - None - }; - let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut push)); - - // There's no need to thread an IdMap through to here because - // the IDs generated aren't going to be emitted anywhere. - let mut ids = IdMap::new(); - let iter = Footnotes::new(HeadingLinks::new(p, None, &mut ids)); - - for ev in iter { - if let Event::Start(Tag::Link(_, dest, _)) = ev { - debug!("found link: {}", dest); - links.push(match dest { - CowStr::Borrowed(s) => (s.to_owned(), locate(s)), - s @ (CowStr::Boxed(..) | CowStr::Inlined(..)) => (s.into_string(), None), - }); - } + let links = RefCell::new(vec![]); + + // FIXME: remove this function once pulldown_cmark can provide spans for link definitions. + let locate = |s: &str, fallback: Range<usize>| unsafe { + let s_start = s.as_ptr(); + let s_end = s_start.add(s.len()); + let md_start = md.as_ptr(); + let md_end = md_start.add(md.len()); + if md_start <= s_start && s_end <= md_end { + let start = s_start.offset_from(md_start) as usize; + let end = s_end.offset_from(md_start) as usize; + start..end + } else { + fallback + } + }; + + let span_for_link = |link: &CowStr<'_>, span: Range<usize>| { + // For diagnostics, we want to underline the link's definition but `span` will point at + // where the link is used. This is a problem for reference-style links, where the definition + // is separate from the usage. + match link { + // `Borrowed` variant means the string (the link's destination) may come directly from + // the markdown text and we can locate the original link destination. + // NOTE: LinkReplacer also provides `Borrowed` but possibly from other sources, + // so `locate()` can fall back to use `span`. + CowStr::Borrowed(s) => locate(s, span), + + // For anything else, we can only use the provided range. + CowStr::Boxed(_) | CowStr::Inlined(_) => span, + } + }; + + let mut push = |link: BrokenLink<'_>| { + let span = span_for_link(&CowStr::Borrowed(link.reference), link.span); + links.borrow_mut().push(MarkdownLink { + kind: LinkType::ShortcutUnknown, + link: link.reference.to_owned(), + range: span, + }); + None + }; + let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut push)).into_offset_iter(); + + // There's no need to thread an IdMap through to here because + // the IDs generated aren't going to be emitted anywhere. + let mut ids = IdMap::new(); + let iter = Footnotes::new(HeadingLinks::new(p, None, &mut ids)); + + for ev in iter { + if let Event::Start(Tag::Link(kind, dest, _)) = ev.0 { + debug!("found link: {}", dest); + let span = span_for_link(&dest, ev.1); + links.borrow_mut().push(MarkdownLink { kind, link: dest.into_string(), range: span }); } } - links.append(&mut shortcut_links); - - links + links.into_inner() } #[derive(Debug)] @@ -1185,6 +1203,7 @@ crate struct RustCodeBlock { crate code: Range<usize>, crate is_fenced: bool, crate syntax: Option<String>, + crate is_ignore: bool, } /// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or @@ -1200,7 +1219,7 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_, '_>) -> Vec<RustC while let Some((event, offset)) = p.next() { if let Event::Start(Tag::CodeBlock(syntax)) = event { - let (syntax, code_start, code_end, range, is_fenced) = match syntax { + let (syntax, code_start, code_end, range, is_fenced, is_ignore) = match syntax { CodeBlockKind::Fenced(syntax) => { let syntax = syntax.as_ref(); let lang_string = if syntax.is_empty() { @@ -1211,6 +1230,7 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_, '_>) -> Vec<RustC if !lang_string.rust { continue; } + let is_ignore = lang_string.ignore != Ignore::None; let syntax = if syntax.is_empty() { None } else { Some(syntax.to_owned()) }; let (code_start, mut code_end) = match p.next() { Some((Event::Text(_), offset)) => (offset.start, offset.end), @@ -1221,6 +1241,7 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_, '_>) -> Vec<RustC range: offset, code, syntax, + is_ignore, }); continue; } @@ -1231,6 +1252,7 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_, '_>) -> Vec<RustC range: offset, code, syntax, + is_ignore, }); continue; } @@ -1238,7 +1260,7 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_, '_>) -> Vec<RustC while let Some((Event::Text(_), offset)) = p.next() { code_end = offset.end; } - (syntax, code_start, code_end, offset, true) + (syntax, code_start, code_end, offset, true, is_ignore) } CodeBlockKind::Indented => { // The ending of the offset goes too far sometime so we reduce it by one in @@ -1250,9 +1272,10 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_, '_>) -> Vec<RustC offset.end, Range { start: offset.start, end: offset.end - 1 }, false, + false, ) } else { - (None, offset.start, offset.end, offset, false) + (None, offset.start, offset.end, offset, false, false) } } }; @@ -1262,6 +1285,7 @@ crate fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_, '_>) -> Vec<RustC range, code: Range { start: code_start, end: code_end }, syntax, + is_ignore, }); } } @@ -1298,7 +1322,6 @@ fn init_id_map() -> FxHashMap<String, usize> { map.insert("trait-implementations".to_owned(), 1); map.insert("synthetic-implementations".to_owned(), 1); map.insert("blanket-implementations".to_owned(), 1); - map.insert("deref-methods".to_owned(), 1); map } diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs index c408e576639..497cbbb4250 100644 --- a/src/librustdoc/html/render/cache.rs +++ b/src/librustdoc/html/render/cache.rs @@ -78,7 +78,7 @@ crate fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { ty: item.type_(), name: item.name.unwrap().to_string(), path: fqp[..fqp.len() - 1].join("::"), - desc: item.doc_value().map_or_else(|| String::new(), short_markdown_summary), + desc: item.doc_value().map_or_else(String::new, |s| short_markdown_summary(&s)), parent: Some(did), parent_idx: None, search_type: get_index_search_type(&item), @@ -127,7 +127,7 @@ crate fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { let crate_doc = krate .module .as_ref() - .map(|module| module.doc_value().map_or_else(|| String::new(), short_markdown_summary)) + .map(|module| module.doc_value().map_or_else(String::new, |s| short_markdown_summary(&s))) .unwrap_or_default(); #[derive(Serialize)] diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index c19262b72cf..6a32be60991 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -30,7 +30,6 @@ crate mod cache; #[cfg(test)] mod tests; -use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::cmp::Ordering; use std::collections::{BTreeMap, VecDeque}; @@ -116,6 +115,9 @@ crate struct Context<'tcx> { crate render_redirect_pages: bool, /// The map used to ensure all generated 'id=' attributes are unique. id_map: Rc<RefCell<IdMap>>, + /// Tracks section IDs for `Deref` targets so they match in both the main + /// body and the sidebar. + deref_id_map: Rc<RefCell<FxHashMap<DefId, String>>>, crate shared: Arc<SharedContext<'tcx>>, all: Rc<RefCell<AllTypes>>, /// Storage for the errors produced while generating documentation so they @@ -198,12 +200,8 @@ impl SharedContext<'_> { /// Based on whether the `collapse-docs` pass was run, return either the `doc_value` or the /// `collapsed_doc_value` of the given item. - crate fn maybe_collapsed_doc_value<'a>(&self, item: &'a clean::Item) -> Option<Cow<'a, str>> { - if self.collapsed { - item.collapsed_doc_value().map(|s| s.into()) - } else { - item.doc_value().map(|s| s.into()) - } + crate fn maybe_collapsed_doc_value<'a>(&self, item: &'a clean::Item) -> Option<String> { + if self.collapsed { item.collapsed_doc_value() } else { item.doc_value() } } } @@ -377,7 +375,6 @@ crate fn initial_ids() -> Vec<String> { "implementors-list", "synthetic-implementors-list", "methods", - "deref-methods", "implementations", ] .iter() @@ -511,6 +508,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { dst, render_redirect_pages: false, id_map: Rc::new(RefCell::new(id_map)), + deref_id_map: Rc::new(RefCell::new(FxHashMap::default())), shared: Arc::new(scx), all: Rc::new(RefCell::new(AllTypes::new())), errors: Rc::new(receiver), @@ -979,7 +977,7 @@ themePicker.onblur = handleThemeButtonsBlur; .iter() .map(|s| format!("\"{}\"", s.to_str().expect("invalid osstring conversion"))) .collect::<Vec<_>>(); - files.sort_unstable_by(|a, b| a.cmp(b)); + files.sort_unstable(); let subs = subs.iter().map(|s| s.to_json_string()).collect::<Vec<_>>().join(","); let dirs = if subs.is_empty() { String::new() } else { format!(",\"dirs\":[{}]", subs) }; @@ -1428,7 +1426,7 @@ impl Setting { .map(|opt| format!( "<option value=\"{}\" {}>{}</option>", opt.0, - if &opt.0 == default_value { "selected" } else { "" }, + if opt.0 == default_value { "selected" } else { "" }, opt.1, )) .collect::<String>(), @@ -1595,7 +1593,7 @@ impl Context<'_> { if let Some(&(ref names, ty)) = cache.paths.get(&it.def_id) { for name in &names[..names.len() - 1] { url.push_str(name); - url.push_str("/"); + url.push('/'); } url.push_str(&item_path(ty, names.last().unwrap())); layout::redirect(&url) @@ -1622,7 +1620,7 @@ impl Context<'_> { let short = short.to_string(); map.entry(short).or_default().push(( myname, - Some(item.doc_value().map_or_else(|| String::new(), plain_text_summary)), + Some(item.doc_value().map_or_else(String::new, |s| plain_text_summary(&s))), )); } @@ -1880,7 +1878,7 @@ fn document_short( return; } if let Some(s) = item.doc_value() { - let mut summary_html = MarkdownSummaryLine(s, &item.links()).into_string(); + let mut summary_html = MarkdownSummaryLine(&s, &item.links()).into_string(); if s.contains('\n') { let link = format!(r#" <a href="{}">Read more</a>"#, naive_assoc_href(item, link)); @@ -2197,7 +2195,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl let stab = myitem.stability_class(cx.tcx()); let add = if stab.is_some() { " " } else { "" }; - let doc_value = myitem.doc_value().unwrap_or(""); + let doc_value = myitem.doc_value().unwrap_or_default(); write!( w, "<tr class=\"{stab}{add}module-item\">\ @@ -2207,7 +2205,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()).into_string(), + docs = MarkdownSummaryLine(&doc_value, &myitem.links()).into_string(), class = myitem.type_(), add = add, stab = stab.unwrap_or_else(String::new), @@ -2308,7 +2306,7 @@ fn short_item_info( let since = &since.as_str(); if !stability::deprecation_in_effect(is_since_rustc_version, Some(since)) { if *since == "TBD" { - format!("Deprecating in a future Rust version") + String::from("Deprecating in a future Rust version") } else { format!("Deprecating in {}", Escape(since)) } @@ -3522,14 +3520,18 @@ 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()))); + cx.deref_id_map.borrow_mut().insert(type_.def_id().unwrap(), id.clone()); write!( w, - "<h2 id=\"deref-methods\" class=\"small-section-header\">\ - Methods from {}<Target = {}>\ - <a href=\"#deref-methods\" class=\"anchor\"></a>\ + "<h2 id=\"{id}\" class=\"small-section-header\">\ + Methods from {trait_}<Target = {type_}>\ + <a href=\"#{id}\" class=\"anchor\"></a>\ </h2>", - trait_.print(), - type_.print() + id = id, + trait_ = trait_.print(), + type_ = type_.print(), ); RenderMode::ForDeref { mut_: deref_mut_ } } @@ -3553,9 +3555,6 @@ fn render_assoc_items( ); } } - if let AssocItemRender::DerefFor { .. } = what { - return; - } if !traits.is_empty() { let deref_impl = traits.iter().find(|t| t.inner_impl().trait_.def_id() == cache.deref_trait_did); @@ -3565,6 +3564,12 @@ fn render_assoc_items( render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, cache); } + // If we were already one level into rendering deref methods, we don't want to render + // anything after recursing into any further deref methods above. + if let AssocItemRender::DerefFor { .. } = what { + return; + } + let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) = traits.iter().partition(|t| t.inner_impl().synthetic); let (blanket_impl, concrete): (Vec<&&Impl>, _) = @@ -3636,6 +3641,13 @@ fn render_deref_methods( let what = AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut }; if let Some(did) = target.def_id() { + if let Some(type_did) = impl_.inner_impl().for_.def_id() { + // `impl Deref<Target = S> for S` + if did == type_did { + // Avoid infinite cycles + return; + } + } render_assoc_items(w, cx, container_item, did, what, cache); } else { if let Some(prim) = target.primitive_type() { @@ -4170,14 +4182,14 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer, cache: ); } match *it.kind { - clean::StructItem(ref s) => sidebar_struct(buffer, it, s), - clean::TraitItem(ref t) => sidebar_trait(buffer, it, t), - clean::PrimitiveItem(_) => sidebar_primitive(buffer, it), - clean::UnionItem(ref u) => sidebar_union(buffer, it, u), - clean::EnumItem(ref e) => sidebar_enum(buffer, it, e), - clean::TypedefItem(_, _) => sidebar_typedef(buffer, it), + clean::StructItem(ref s) => sidebar_struct(cx, buffer, it, s), + clean::TraitItem(ref t) => sidebar_trait(cx, buffer, it, t), + clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it), + clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u), + clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e), + clean::TypedefItem(_, _) => sidebar_typedef(cx, buffer, it), clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items), - clean::ForeignTypeItem => sidebar_foreign_type(buffer, it), + clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it), _ => (), } @@ -4278,7 +4290,7 @@ fn small_url_encode(s: &str) -> String { .replace("\"", "%22") } -fn sidebar_assoc_items(it: &clean::Item) -> String { +fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String { let mut out = String::new(); let c = cache(); if let Some(v) = c.impls.get(&it.def_id) { @@ -4308,69 +4320,22 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { .filter(|i| i.inner_impl().trait_.is_some()) .find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did) { - if let Some((target, real_target)) = - impl_.inner_impl().items.iter().find_map(|item| match *item.kind { - clean::TypedefItem(ref t, true) => Some(match *t { - clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), - _ => (&t.type_, &t.type_), - }), - _ => None, - }) - { - let deref_mut = v - .iter() - .filter(|i| i.inner_impl().trait_.is_some()) - .any(|i| i.inner_impl().trait_.def_id() == c.deref_mut_trait_did); - let inner_impl = target - .def_id() - .or(target - .primitive_type() - .and_then(|prim| c.primitive_locations.get(&prim).cloned())) - .and_then(|did| c.impls.get(&did)); - if let Some(impls) = inner_impl { - out.push_str("<a class=\"sidebar-title\" href=\"#deref-methods\">"); - out.push_str(&format!( - "Methods from {}<Target={}>", - Escape(&format!( - "{:#}", - impl_.inner_impl().trait_.as_ref().unwrap().print() - )), - Escape(&format!("{:#}", real_target.print())) - )); - out.push_str("</a>"); - let mut ret = impls - .iter() - .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(|i| { - get_methods(i.inner_impl(), true, &mut used_links, deref_mut) - }) - .collect::<Vec<_>>(); - // We want links' order to be reproducible so we don't use unstable sort. - ret.sort(); - if !ret.is_empty() { - out.push_str(&format!( - "<div class=\"sidebar-links\">{}</div>", - ret.join("") - )); - } - } - } + out.push_str(&sidebar_deref_methods(cx, impl_, v)); } let format_impls = |impls: Vec<&Impl>| { let mut links = FxHashSet::default(); let mut ret = impls .iter() - .filter_map(|i| { - let is_negative_impl = is_negative_impl(i.inner_impl()); - if let Some(ref i) = i.inner_impl().trait_ { + .filter_map(|it| { + if let Some(ref i) = it.inner_impl().trait_ { let i_display = format!("{:#}", i.print()); let out = Escape(&i_display); let encoded = small_url_encode(&format!("{:#}", i.print())); let generated = format!( "<a href=\"#impl-{}\">{}{}</a>", encoded, - if is_negative_impl { "!" } else { "" }, + if it.inner_impl().negative_polarity { "!" } else { "" }, out ); if links.insert(generated.clone()) { Some(generated) } else { None } @@ -4422,7 +4387,81 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { out } -fn sidebar_struct(buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) { +fn sidebar_deref_methods(cx: &Context<'_>, impl_: &Impl, v: &Vec<Impl>) -> String { + let mut out = String::new(); + let c = cache(); + + debug!("found Deref: {:?}", impl_); + if let Some((target, real_target)) = + impl_.inner_impl().items.iter().find_map(|item| match *item.kind { + clean::TypedefItem(ref t, true) => Some(match *t { + clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), + _ => (&t.type_, &t.type_), + }), + _ => None, + }) + { + debug!("found target, real_target: {:?} {:?}", target, real_target); + let deref_mut = v + .iter() + .filter(|i| i.inner_impl().trait_.is_some()) + .any(|i| i.inner_impl().trait_.def_id() == c.deref_mut_trait_did); + let inner_impl = target + .def_id() + .or_else(|| { + target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned()) + }) + .and_then(|did| c.impls.get(&did)); + if let Some(impls) = inner_impl { + debug!("found inner_impl: {:?}", impls); + let mut used_links = FxHashSet::default(); + let mut ret = impls + .iter() + .filter(|i| i.inner_impl().trait_.is_none()) + .flat_map(|i| get_methods(i.inner_impl(), true, &mut used_links, deref_mut)) + .collect::<Vec<_>>(); + if !ret.is_empty() { + let deref_id_map = cx.deref_id_map.borrow(); + let id = deref_id_map + .get(&real_target.def_id().unwrap()) + .expect("Deref section without derived id"); + out.push_str(&format!( + "<a class=\"sidebar-title\" href=\"#{}\">Methods from {}<Target={}></a>", + id, + Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print())), + Escape(&format!("{:#}", real_target.print())), + )); + // We want links' order to be reproducible so we don't use unstable sort. + ret.sort(); + out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", ret.join(""))); + } + } + + // Recurse into any further impls that might exist for `target` + if let Some(target_did) = target.def_id() { + if let Some(target_impls) = c.impls.get(&target_did) { + if let Some(target_deref_impl) = target_impls + .iter() + .filter(|i| i.inner_impl().trait_.is_some()) + .find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did) + { + if let Some(type_did) = impl_.inner_impl().for_.def_id() { + // `impl Deref<Target = S> for S` + if target_did == type_did { + // Avoid infinite cycles + return out; + } + } + out.push_str(&sidebar_deref_methods(cx, target_deref_impl, target_impls)); + } + } + } + } + + out +} + +fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) { let mut sidebar = String::new(); let fields = get_struct_fields_name(&s.fields); @@ -4436,7 +4475,7 @@ fn sidebar_struct(buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) { } } - sidebar.push_str(&sidebar_assoc_items(it)); + sidebar.push_str(&sidebar_assoc_items(cx, it)); if !sidebar.is_empty() { write!(buf, "<div class=\"block items\">{}</div>", sidebar); @@ -4463,11 +4502,7 @@ fn extract_for_impl_name(item: &clean::Item) -> Option<(String, String)> { } } -fn is_negative_impl(i: &clean::Impl) -> bool { - i.polarity == Some(clean::ImplPolarity::Negative) -} - -fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { +fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { let mut sidebar = String::new(); let mut types = t @@ -4567,7 +4602,7 @@ fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { } } - sidebar.push_str(&sidebar_assoc_items(it)); + sidebar.push_str(&sidebar_assoc_items(cx, it)); sidebar.push_str("<a class=\"sidebar-title\" href=\"#implementors\">Implementors</a>"); if t.is_auto { @@ -4580,16 +4615,16 @@ fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { write!(buf, "<div class=\"block items\">{}</div>", sidebar) } -fn sidebar_primitive(buf: &mut Buffer, it: &clean::Item) { - let sidebar = sidebar_assoc_items(it); +fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { + let sidebar = sidebar_assoc_items(cx, it); if !sidebar.is_empty() { write!(buf, "<div class=\"block items\">{}</div>", sidebar); } } -fn sidebar_typedef(buf: &mut Buffer, it: &clean::Item) { - let sidebar = sidebar_assoc_items(it); +fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { + let sidebar = sidebar_assoc_items(cx, it); if !sidebar.is_empty() { write!(buf, "<div class=\"block items\">{}</div>", sidebar); @@ -4611,7 +4646,7 @@ fn get_struct_fields_name(fields: &[clean::Item]) -> String { fields.join("") } -fn sidebar_union(buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { +fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { let mut sidebar = String::new(); let fields = get_struct_fields_name(&u.fields); @@ -4623,14 +4658,14 @@ fn sidebar_union(buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { )); } - sidebar.push_str(&sidebar_assoc_items(it)); + sidebar.push_str(&sidebar_assoc_items(cx, it)); if !sidebar.is_empty() { write!(buf, "<div class=\"block items\">{}</div>", sidebar); } } -fn sidebar_enum(buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { +fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { let mut sidebar = String::new(); let mut variants = e @@ -4650,7 +4685,7 @@ fn sidebar_enum(buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { )); } - sidebar.push_str(&sidebar_assoc_items(it)); + sidebar.push_str(&sidebar_assoc_items(cx, it)); if !sidebar.is_empty() { write!(buf, "<div class=\"block items\">{}</div>", sidebar); @@ -4739,8 +4774,8 @@ fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { } } -fn sidebar_foreign_type(buf: &mut Buffer, it: &clean::Item) { - let sidebar = sidebar_assoc_items(it); +fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { + let sidebar = sidebar_assoc_items(cx, it); if !sidebar.is_empty() { write!(buf, "<div class=\"block items\">{}</div>", sidebar); } diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 1de4b0016c5..ec8024ffca5 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -493,11 +493,7 @@ function defocusSearchBar() { document.addEventListener("keypress", handleShortcut); document.addEventListener("keydown", handleShortcut); - function resetMouseMoved(ev) { - mouseMovedAfterSearch = true; - } - - document.addEventListener("mousemove", resetMouseMoved); + document.addEventListener("mousemove", function() { mouseMovedAfterSearch = true; }); var handleSourceHighlight = (function() { var prev_line_id = 0; @@ -667,13 +663,7 @@ function defocusSearchBar() { results = {}, results_in_args = {}, results_returned = {}, split = valLower.split("::"); - var length = split.length; - for (var z = 0; z < length; ++z) { - if (split[z] === "") { - split.splice(z, 1); - z -= 1; - } - } + split = split.filter(function(segment) { return segment !== ""; }); function transformResults(results, isType) { var out = []; @@ -2157,14 +2147,14 @@ function defocusSearchBar() { var code = document.createElement("code"); code.innerHTML = struct.text; - var x = code.getElementsByTagName("a"); - var xlength = x.length; - for (var it = 0; it < xlength; it++) { - var href = x[it].getAttribute("href"); + onEachLazy(code.getElementsByTagName("a"), function(elem) { + var href = elem.getAttribute("href"); + if (href && href.indexOf("http") !== 0) { - x[it].setAttribute("href", rootPath + href); + elem.setAttribute("href", rootPath + href); } - } + }); + var display = document.createElement("h3"); addClass(display, "impl"); display.innerHTML = "<span class=\"in-band\"><table class=\"table-display\">" + @@ -2553,14 +2543,12 @@ function defocusSearchBar() { var hiddenElems = e.getElementsByClassName("hidden"); var needToggle = false; - var hlength = hiddenElems.length; - for (var i = 0; i < hlength; ++i) { - if (hasClass(hiddenElems[i], "content") === false && - hasClass(hiddenElems[i], "docblock") === false) { - needToggle = true; - break; + var needToggle = onEachLazy(e.getElementsByClassName("hidden"), function(hiddenElem) { + if (hasClass(hiddenElem, "content") === false && + hasClass(hiddenElem, "docblock") === false) { + return true; } - } + }); if (needToggle === true) { var inner_toggle = newToggle.cloneNode(true); inner_toggle.onclick = toggleClicked; diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 62b08e519bf..8dad26dced9 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -1412,6 +1412,7 @@ h4 > .notable-traits { .sidebar > .block.version { border-bottom: none; margin-top: 12px; + margin-bottom: 0; } nav.sub { @@ -1582,7 +1583,10 @@ h4 > .notable-traits { height: 73px; } - #main { + /* This is to prevent the search bar from being underneath the <section> + * element following it. + */ + #main, #search { margin-top: 100px; } diff --git a/src/librustdoc/html/toc.rs b/src/librustdoc/html/toc.rs index b39a4e179cd..c55f2459a9c 100644 --- a/src/librustdoc/html/toc.rs +++ b/src/librustdoc/html/toc.rs @@ -132,7 +132,7 @@ impl TocBuilder { } Some(entry) => { sec_number = entry.sec_number.clone(); - sec_number.push_str("."); + sec_number.push('.'); (entry.level, &entry.children) } }; diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index e347f7f8411..5dea64ef145 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -422,7 +422,7 @@ impl From<clean::Impl> for Impl { trait_, for_, items, - polarity, + negative_polarity, synthetic, blanket_impl, } = impl_; @@ -436,7 +436,7 @@ impl From<clean::Impl> for Impl { trait_: trait_.map(Into::into), for_: for_.into(), items: ids(items), - negative: polarity == Some(clean::ImplPolarity::Negative), + negative: negative_polarity, synthetic, blanket_impl: blanket_impl.map(Into::into), } diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 0a00323a317..05a3a15adac 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -235,12 +235,7 @@ impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> { let mut tests = Tests { found_tests: 0 }; find_testable_code( - &i.attrs - .doc_strings - .iter() - .map(|d| d.doc.as_str()) - .collect::<Vec<_>>() - .join("\n"), + &i.attrs.collapsed_doc_value().unwrap_or_default(), &mut tests, ErrorCodes::No, false, diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 0c76dc571be..554392c213e 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -51,10 +51,10 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { let mut diag = if let Some(sp) = super::source_span_for_markdown_range(self.cx, &dox, &code_block.range, &item.attrs) { - let warning_message = if buffer.has_errors { - "could not parse code block as Rust code" + let (warning_message, suggest_using_text) = if buffer.has_errors { + ("could not parse code block as Rust code", true) } else { - "Rust code block is empty" + ("Rust code block is empty", false) }; let mut diag = self.cx.sess().struct_span_warn(sp, warning_message); @@ -67,6 +67,15 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { String::from("```text"), Applicability::MachineApplicable, ); + } else if suggest_using_text && code_block.is_ignore { + let sp = sp.from_inner(InnerSpan::new(0, 3)); + diag.span_suggestion( + sp, + "`ignore` code blocks require valid Rust code for syntax highlighting. \ + Mark blocks that do not contain Rust code as text", + String::from("```text,"), + Applicability::MachineApplicable, + ); } diag diff --git a/src/librustdoc/passes/collapse_docs.rs b/src/librustdoc/passes/collapse_docs.rs deleted file mode 100644 index e1ba75baa0f..00000000000 --- a/src/librustdoc/passes/collapse_docs.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::clean::{self, DocFragment, DocFragmentKind, Item}; -use crate::core::DocContext; -use crate::fold; -use crate::fold::DocFolder; -use crate::passes::Pass; - -use std::mem::take; - -crate const COLLAPSE_DOCS: Pass = Pass { - name: "collapse-docs", - run: collapse_docs, - description: "concatenates all document attributes into one document attribute", -}; - -crate fn collapse_docs(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate { - let mut krate = Collapser.fold_crate(krate); - krate.collapsed = true; - krate -} - -struct Collapser; - -impl fold::DocFolder for Collapser { - fn fold_item(&mut self, mut i: Item) -> Option<Item> { - i.attrs.collapse_doc_comments(); - Some(self.fold_item_recur(i)) - } -} - -fn collapse(doc_strings: &mut Vec<DocFragment>) { - let mut docs = vec![]; - let mut last_frag: Option<DocFragment> = None; - - for frag in take(doc_strings) { - if let Some(mut curr_frag) = last_frag.take() { - let curr_kind = &curr_frag.kind; - let new_kind = &frag.kind; - - if matches!(*curr_kind, DocFragmentKind::Include { .. }) - || curr_kind != new_kind - || curr_frag.parent_module != frag.parent_module - { - if *curr_kind == DocFragmentKind::SugaredDoc - || *curr_kind == DocFragmentKind::RawDoc - { - // add a newline for extra padding between segments - curr_frag.doc.push('\n'); - } - docs.push(curr_frag); - last_frag = Some(frag); - } else { - curr_frag.doc.push('\n'); - curr_frag.doc.push_str(&frag.doc); - curr_frag.span = curr_frag.span.to(frag.span); - last_frag = Some(curr_frag); - } - } else { - last_frag = Some(frag); - } - } - - if let Some(frag) = last_frag.take() { - docs.push(frag); - } - *doc_strings = docs; -} - -impl clean::Attributes { - crate fn collapse_doc_comments(&mut self) { - collapse(&mut self.doc_strings); - } -} diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 9f15038a353..708d7710058 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -25,6 +25,8 @@ use rustc_span::symbol::Symbol; use rustc_span::DUMMY_SP; use smallvec::{smallvec, SmallVec}; +use pulldown_cmark::LinkType; + use std::borrow::Cow; use std::cell::Cell; use std::convert::{TryFrom, TryInto}; @@ -34,7 +36,7 @@ use std::ops::Range; use crate::clean::{self, utils::find_nearest_parent_module, Crate, Item, ItemLink, PrimitiveType}; use crate::core::DocContext; use crate::fold::DocFolder; -use crate::html::markdown::markdown_links; +use crate::html::markdown::{markdown_links, MarkdownLink}; use crate::passes::Pass; use super::span_of_attrs; @@ -245,7 +247,7 @@ struct DiagnosticInfo<'a> { item: &'a Item, dox: &'a str, ori_link: &'a str, - link_range: Option<Range<usize>>, + link_range: Range<usize>, } #[derive(Clone, Debug, Hash)] @@ -265,8 +267,9 @@ struct LinkCollector<'a, 'tcx> { /// because `clean` and the disambiguator code expect them to be different. /// See the code for associated items on inherent impls for details. kind_side_channel: Cell<Option<(DefKind, DefId)>>, - /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link - visited_links: FxHashMap<ResolutionInfo, CachedLink>, + /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link. + /// The link will be `None` if it could not be resolved (i.e. the error was cached). + visited_links: FxHashMap<ResolutionInfo, Option<CachedLink>>, } impl<'a, 'tcx> LinkCollector<'a, 'tcx> { @@ -391,10 +394,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ns, impl_, ) - .map(|item| match item.kind { - ty::AssocKind::Fn => "method", - ty::AssocKind::Const => "associatedconstant", - ty::AssocKind::Type => "associatedtype", + .map(|item| { + let kind = item.kind; + self.kind_side_channel.set(Some((kind.as_def_kind(), item.def_id))); + match kind { + ty::AssocKind::Fn => "method", + ty::AssocKind::Const => "associatedconstant", + ty::AssocKind::Type => "associatedtype", + } }) .map(|out| { ( @@ -497,15 +504,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { match res { // FIXME(#76467): make this fallthrough to lookup the associated // item a separate function. - Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => { - assert_eq!(ns, ValueNS); - } - Res::Def(DefKind::AssocTy, _) => { - assert_eq!(ns, TypeNS); - } - Res::Def(DefKind::Variant, _) => { - return handle_variant(cx, res, extra_fragment); - } + Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => assert_eq!(ns, ValueNS), + Res::Def(DefKind::AssocTy, _) => assert_eq!(ns, TypeNS), + Res::Def(DefKind::Variant, _) => return handle_variant(cx, res, extra_fragment), // Not a trait item; just return what we found. Res::Primitive(ty) => { if extra_fragment.is_some() { @@ -515,12 +516,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } return Ok((res, Some(ty.as_str().to_owned()))); } - Res::Def(DefKind::Mod, _) => { - return Ok((res, extra_fragment.clone())); - } - _ => { - return Ok((res, extra_fragment.clone())); - } + _ => return Ok((res, extra_fragment.clone())), } } @@ -891,43 +887,18 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { // In the presence of re-exports, this is not the same as the module of the item. // Rather than merging all documentation into one, resolve it one attribute at a time // so we know which module it came from. - let mut attrs = item.attrs.doc_strings.iter().peekable(); - while let Some(attr) = attrs.next() { - // `collapse_docs` does not have the behavior we want: - // we want `///` and `#[doc]` to count as the same attribute, - // but currently it will treat them as separate. - // As a workaround, combine all attributes with the same parent module into the same attribute. - let mut combined_docs = attr.doc.clone(); - loop { - match attrs.peek() { - Some(next) if next.parent_module == attr.parent_module => { - combined_docs.push('\n'); - combined_docs.push_str(&attrs.next().unwrap().doc); - } - _ => break, - } - } - debug!("combined_docs={}", combined_docs); + for (parent_module, doc) in item.attrs.collapsed_doc_value_by_module_level() { + debug!("combined_docs={}", doc); - let (krate, parent_node) = if let Some(id) = attr.parent_module { - trace!("docs {:?} came from {:?}", attr.doc, id); + let (krate, parent_node) = if let Some(id) = parent_module { (id.krate, Some(id)) } else { - trace!("no parent found for {:?}", attr.doc); (item.def_id.krate, parent_node) }; // NOTE: if there are links that start in one crate and end in another, this will not resolve them. // This is a degenerate case and it's not supported by rustdoc. - for (ori_link, link_range) in markdown_links(&combined_docs) { - let link = self.resolve_link( - &item, - &combined_docs, - &self_name, - parent_node, - krate, - ori_link, - link_range, - ); + for md_link in markdown_links(&doc) { + let link = self.resolve_link(&item, &doc, &self_name, parent_node, krate, md_link); if let Some(link) = link { item.attrs.links.push(link); } @@ -959,27 +930,26 @@ impl LinkCollector<'_, '_> { self_name: &Option<String>, parent_node: Option<DefId>, krate: CrateNum, - ori_link: String, - link_range: Option<Range<usize>>, + ori_link: MarkdownLink, ) -> Option<ItemLink> { - trace!("considering link '{}'", ori_link); + trace!("considering link '{}'", ori_link.link); // Bail early for real links. - if ori_link.contains('/') { + if ori_link.link.contains('/') { return None; } // [] is mostly likely not supposed to be a link - if ori_link.is_empty() { + if ori_link.link.is_empty() { return None; } let cx = self.cx; - let link = ori_link.replace("`", ""); + let link = ori_link.link.replace("`", ""); let parts = link.split('#').collect::<Vec<_>>(); let (link, extra_fragment) = if parts.len() > 2 { // A valid link can't have multiple #'s - anchor_failure(cx, &item, &link, dox, link_range, AnchorFailure::MultipleAnchors); + anchor_failure(cx, &item, &link, dox, ori_link.range, AnchorFailure::MultipleAnchors); return None; } else if parts.len() == 2 { if parts[0].trim().is_empty() { @@ -1035,7 +1005,7 @@ impl LinkCollector<'_, '_> { path_str, disambiguator, dox, - link_range, + ori_link.range, smallvec![ResolutionFailure::NoParentItem], ); return None; @@ -1043,12 +1013,18 @@ impl LinkCollector<'_, '_> { let resolved_self; // replace `Self` with suitable item's parent name - if path_str.starts_with("Self::") { + let is_lone_self = path_str == "Self"; + let is_lone_crate = path_str == "crate"; + if path_str.starts_with("Self::") || is_lone_self { if let Some(ref name) = self_name { - resolved_self = format!("{}::{}", name, &path_str[6..]); - path_str = &resolved_self; + if is_lone_self { + path_str = name; + } else { + resolved_self = format!("{}::{}", name, &path_str[6..]); + path_str = &resolved_self; + } } - } else if path_str.starts_with("crate::") { + } else if path_str.starts_with("crate::") || is_lone_crate { use rustc_span::def_id::CRATE_DEF_INDEX; // HACK(jynelson): rustc_resolve thinks that `crate` is the crate currently being documented. @@ -1057,8 +1033,12 @@ impl LinkCollector<'_, '_> { // HACK(jynelson)(2): If we just strip `crate::` then suddenly primitives become ambiguous // (consider `crate::char`). Instead, change it to `self::`. This works because 'self' is now the crate root. // FIXME(#78696): This doesn't always work. - resolved_self = format!("self::{}", &path_str["crate::".len()..]); - path_str = &resolved_self; + if is_lone_crate { + path_str = "self"; + } else { + resolved_self = format!("self::{}", &path_str["crate::".len()..]); + path_str = &resolved_self; + } module_id = DefId { krate, index: CRATE_DEF_INDEX }; } @@ -1075,7 +1055,7 @@ impl LinkCollector<'_, '_> { path_str, disambiguator, dox, - link_range, + ori_link.range, smallvec![err_kind], ); return None; @@ -1091,15 +1071,22 @@ impl LinkCollector<'_, '_> { return None; } - let key = ResolutionInfo { - module_id, - dis: disambiguator, - path_str: path_str.to_owned(), - extra_fragment, + let diag_info = DiagnosticInfo { + item, + dox, + ori_link: &ori_link.link, + link_range: ori_link.range.clone(), }; - let diag = - DiagnosticInfo { item, dox, ori_link: &ori_link, link_range: link_range.clone() }; - let (mut res, mut fragment) = self.resolve_with_disambiguator_cached(key, diag)?; + let (mut res, mut fragment) = self.resolve_with_disambiguator_cached( + ResolutionInfo { + module_id, + dis: disambiguator, + path_str: path_str.to_owned(), + extra_fragment, + }, + diag_info, + matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut), + )?; // Check for a primitive which might conflict with a module // Report the ambiguity and require that the user specify which one they meant. @@ -1118,7 +1105,7 @@ impl LinkCollector<'_, '_> { &item, path_str, dox, - link_range, + ori_link.range, AnchorFailure::RustdocAnchorConflict(prim), ); return None; @@ -1128,7 +1115,7 @@ impl LinkCollector<'_, '_> { } else { // `[char]` when a `char` module is in scope let candidates = vec![res, prim]; - ambiguity_error(cx, &item, path_str, dox, link_range, candidates); + ambiguity_error(cx, &item, path_str, dox, ori_link.range, candidates); return None; } } @@ -1146,61 +1133,89 @@ impl LinkCollector<'_, '_> { specified.descr() ); diag.note(¬e); - suggest_disambiguator(resolved, diag, path_str, dox, sp, &link_range); + suggest_disambiguator(resolved, diag, path_str, dox, sp, &ori_link.range); }; - report_diagnostic(cx, BROKEN_INTRA_DOC_LINKS, &msg, &item, dox, &link_range, callback); + report_diagnostic( + cx, + BROKEN_INTRA_DOC_LINKS, + &msg, + &item, + dox, + &ori_link.range, + callback, + ); }; - match res { - Res::Primitive(_) => match disambiguator { - Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => { - Some(ItemLink { link: ori_link, link_text, did: None, fragment }) - } - Some(other) => { - report_mismatch(other, Disambiguator::Primitive); - None - } - }, - Res::Def(kind, id) => { - debug!("intra-doc link to {} resolved to {:?}", path_str, res); - - // Disallow e.g. linking to enums with `struct@` - debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator); - match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) { - | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const))) - // NOTE: this allows 'method' to mean both normal functions and associated functions - // This can't cause ambiguity because both are in the same namespace. - | (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn))) - // These are namespaces; allow anything in the namespace to match - | (_, Some(Disambiguator::Namespace(_))) - // If no disambiguator given, allow anything - | (_, None) - // All of these are valid, so do nothing - => {} - (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {} - (_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => { - report_mismatch(specified, Disambiguator::Kind(kind)); - return None; - } + + let verify = |kind: DefKind, id: DefId| { + debug!("intra-doc link to {} resolved to {:?}", path_str, res); + + // Disallow e.g. linking to enums with `struct@` + debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator); + match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) { + | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const))) + // NOTE: this allows 'method' to mean both normal functions and associated functions + // This can't cause ambiguity because both are in the same namespace. + | (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn))) + // These are namespaces; allow anything in the namespace to match + | (_, Some(Disambiguator::Namespace(_))) + // If no disambiguator given, allow anything + | (_, None) + // All of these are valid, so do nothing + => {} + (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {} + (_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => { + report_mismatch(specified, Disambiguator::Kind(kind)); + return None; } + } + + // item can be non-local e.g. when using #[doc(primitive = "pointer")] + if let Some((src_id, dst_id)) = id + .as_local() + .and_then(|dst_id| item.def_id.as_local().map(|src_id| (src_id, dst_id))) + { + use rustc_hir::def_id::LOCAL_CRATE; - // item can be non-local e.g. when using #[doc(primitive = "pointer")] - if let Some((src_id, dst_id)) = id - .as_local() - .and_then(|dst_id| item.def_id.as_local().map(|src_id| (src_id, dst_id))) + let hir_src = self.cx.tcx.hir().local_def_id_to_hir_id(src_id); + let hir_dst = self.cx.tcx.hir().local_def_id_to_hir_id(dst_id); + + if self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_src) + && !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_dst) { - use rustc_hir::def_id::LOCAL_CRATE; + privacy_error(cx, &item, &path_str, dox, &ori_link); + } + } - let hir_src = self.cx.tcx.hir().local_def_id_to_hir_id(src_id); - let hir_dst = self.cx.tcx.hir().local_def_id_to_hir_id(dst_id); + Some((kind, id)) + }; - if self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_src) - && !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_dst) - { - privacy_error(cx, &item, &path_str, dox, link_range); + match res { + Res::Primitive(_) => { + if let Some((kind, id)) = self.kind_side_channel.take() { + // We're actually resolving an associated item of a primitive, so we need to + // verify the disambiguator (if any) matches the type of the associated item. + // This case should really follow the same flow as the `Res::Def` branch below, + // but attempting to add a call to `clean::register_res` causes an ICE. @jyn514 + // thinks `register_res` is only needed for cross-crate re-exports, but Rust + // doesn't allow statements like `use str::trim;`, making this a (hopefully) + // valid omission. See https://github.com/rust-lang/rust/pull/80660#discussion_r551585677 + // for discussion on the matter. + verify(kind, id)?; + } else { + match disambiguator { + Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {} + Some(other) => { + report_mismatch(other, Disambiguator::Primitive); + return None; + } } } + Some(ItemLink { link: ori_link.link, link_text, did: None, fragment }) + } + Res::Def(kind, id) => { + let (kind, id) = verify(kind, id)?; let id = clean::register_res(cx, rustc_hir::def::Res::Def(kind, id)); - Some(ItemLink { link: ori_link, link_text, did: Some(id), fragment }) + Some(ItemLink { link: ori_link.link, link_text, did: Some(id), fragment }) } } } @@ -1209,28 +1224,47 @@ impl LinkCollector<'_, '_> { &mut self, key: ResolutionInfo, diag: DiagnosticInfo<'_>, + cache_resolution_failure: bool, ) -> Option<(Res, Option<String>)> { // Try to look up both the result and the corresponding side channel value if let Some(ref cached) = self.visited_links.get(&key) { - self.kind_side_channel.set(cached.side_channel); - return Some(cached.res.clone()); + match cached { + Some(cached) => { + self.kind_side_channel.set(cached.side_channel.clone()); + return Some(cached.res.clone()); + } + None if cache_resolution_failure => return None, + None => { + // Although we hit the cache and found a resolution error, this link isn't + // supposed to cache those. Run link resolution again to emit the expected + // resolution error. + } + } } let res = self.resolve_with_disambiguator(&key, diag); // Cache only if resolved successfully - don't silence duplicate errors - if let Some(res) = &res { + if let Some(res) = res { // Store result for the actual namespace self.visited_links.insert( key, - CachedLink { + Some(CachedLink { res: res.clone(), side_channel: self.kind_side_channel.clone().into_inner(), - }, + }), ); - } - res + Some(res) + } else { + if cache_resolution_failure { + // For reference-style links we only want to report one resolution error + // so let's cache them as well. + self.visited_links.insert(key, None); + } + + None + } } /// After parsing the disambiguator, resolve the main part of the link. @@ -1276,7 +1310,7 @@ impl LinkCollector<'_, '_> { // This could just be a normal link or a broken link // we could potentially check if something is // "intra-doc-link-like" and warn in that case. - return None; + None } Err(ErrorKind::AnchorFailure(msg)) => { anchor_failure( @@ -1287,7 +1321,7 @@ impl LinkCollector<'_, '_> { diag.link_range, msg, ); - return None; + None } } } @@ -1383,7 +1417,7 @@ impl LinkCollector<'_, '_> { diag.link_range, candidates.present_items().collect(), ); - return None; + None } } Some(MacroNS) => { @@ -1408,7 +1442,7 @@ impl LinkCollector<'_, '_> { diag.link_range, smallvec![kind], ); - return None; + None } } } @@ -1606,7 +1640,7 @@ fn report_diagnostic( msg: &str, item: &Item, dox: &str, - link_range: &Option<Range<usize>>, + link_range: &Range<usize>, decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>), ) { let hir_id = match cx.as_local_hir_id(item.def_id) { @@ -1624,31 +1658,27 @@ fn report_diagnostic( cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| { let mut diag = lint.build(msg); - let span = link_range - .as_ref() - .and_then(|range| super::source_span_for_markdown_range(cx, dox, range, attrs)); + let span = super::source_span_for_markdown_range(cx, dox, link_range, attrs); - if let Some(link_range) = link_range { - if let Some(sp) = span { - diag.set_span(sp); - } else { - // blah blah blah\nblah\nblah [blah] blah blah\nblah blah - // ^ ~~~~ - // | link_range - // last_new_line_offset - let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1); - let line = dox[last_new_line_offset..].lines().next().unwrap_or(""); - - // Print the line containing the `link_range` and manually mark it with '^'s. - diag.note(&format!( - "the link appears in this line:\n\n{line}\n\ + if let Some(sp) = span { + diag.set_span(sp); + } else { + // blah blah blah\nblah\nblah [blah] blah blah\nblah blah + // ^ ~~~~ + // | link_range + // last_new_line_offset + let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1); + let line = dox[last_new_line_offset..].lines().next().unwrap_or(""); + + // Print the line containing the `link_range` and manually mark it with '^'s. + diag.note(&format!( + "the link appears in this line:\n\n{line}\n\ {indicator: <before$}{indicator:^<found$}", - line = line, - indicator = "", - before = link_range.start - last_new_line_offset, - found = link_range.len(), - )); - } + line = line, + indicator = "", + before = link_range.start - last_new_line_offset, + found = link_range.len(), + )); } decorate(&mut diag, span); @@ -1668,7 +1698,7 @@ fn resolution_failure( path_str: &str, disambiguator: Option<Disambiguator>, dox: &str, - link_range: Option<Range<usize>>, + link_range: Range<usize>, kinds: SmallVec<[ResolutionFailure<'_>; 3]>, ) { let tcx = collector.cx.tcx; @@ -1892,7 +1922,7 @@ fn anchor_failure( item: &Item, path_str: &str, dox: &str, - link_range: Option<Range<usize>>, + link_range: Range<usize>, failure: AnchorFailure, ) { let msg = match failure { @@ -1917,7 +1947,7 @@ fn ambiguity_error( item: &Item, path_str: &str, dox: &str, - link_range: Option<Range<usize>>, + link_range: Range<usize>, candidates: Vec<Res>, ) { let mut msg = format!("`{}` is ", path_str); @@ -1966,13 +1996,12 @@ fn suggest_disambiguator( path_str: &str, dox: &str, sp: Option<rustc_span::Span>, - link_range: &Option<Range<usize>>, + link_range: &Range<usize>, ) { let suggestion = disambiguator.suggestion(); let help = format!("to link to the {}, {}", disambiguator.descr(), suggestion.descr()); if let Some(sp) = sp { - let link_range = link_range.as_ref().expect("must have a link range if we have a span"); let msg = if dox.bytes().nth(link_range.start) == Some(b'`') { format!("`{}`", suggestion.as_help(path_str)) } else { @@ -1986,13 +2015,7 @@ fn suggest_disambiguator( } /// Report a link from a public item to a private one. -fn privacy_error( - cx: &DocContext<'_>, - item: &Item, - path_str: &str, - dox: &str, - link_range: Option<Range<usize>>, -) { +fn privacy_error(cx: &DocContext<'_>, item: &Item, path_str: &str, dox: &str, link: &MarkdownLink) { let sym; let item_name = match item.name { Some(name) => { @@ -2004,7 +2027,7 @@ fn privacy_error( let msg = format!("public documentation for `{}` links to private item `{}`", item_name, path_str); - report_diagnostic(cx, PRIVATE_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| { + report_diagnostic(cx, PRIVATE_INTRA_DOC_LINKS, &msg, item, dox, &link.range, |diag, sp| { if let Some(sp) = sp { diag.span_label(sp, "this item is private"); } @@ -2062,13 +2085,13 @@ fn resolve_primitive(path_str: &str, ns: Namespace) -> Option<Res> { "f64" => F64, "char" => Char, "bool" | "true" | "false" => Bool, - "str" => Str, + "str" | "&str" => Str, // See #80181 for why these don't have symbols associated. "slice" => Slice, "array" => Array, "tuple" => Tuple, "unit" => Unit, - "pointer" | "*" | "*const" | "*mut" => RawPointer, + "pointer" | "*const" | "*mut" => RawPointer, "reference" | "&" | "&mut" => Reference, "fn" => Fn, "never" | "!" => Never, diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 9b0ae09cb3f..7b5e9e5905f 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -3,7 +3,7 @@ use crate::clean::*; use crate::core::DocContext; use crate::fold::DocFolder; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::ty::DefIdTree; use rustc_span::symbol::sym; @@ -54,39 +54,6 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { } } - let mut cleaner = BadImplStripper { prims, items: crate_items }; - - // scan through included items ahead of time to splice in Deref targets to the "valid" sets - for it in &new_items { - if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = *it.kind { - if cleaner.keep_item(for_) && trait_.def_id() == cx.tcx.lang_items().deref_trait() { - let target = items - .iter() - .find_map(|item| match *item.kind { - TypedefItem(ref t, true) => Some(&t.type_), - _ => None, - }) - .expect("Deref impl without Target type"); - - if let Some(prim) = target.primitive_type() { - cleaner.prims.insert(prim); - } else if let Some(did) = target.def_id() { - cleaner.items.insert(did); - } - } - } - } - - new_items.retain(|it| { - if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = *it.kind { - cleaner.keep_item(for_) - || trait_.as_ref().map_or(false, |t| cleaner.keep_item(t)) - || blanket_impl.is_some() - } else { - true - } - }); - // `tcx.crates()` doesn't include the local crate, and `tcx.all_trait_implementations` // doesn't work with it anyway, so pull them from the HIR map instead for &trait_did in cx.tcx.all_traits(LOCAL_CRATE).iter() { @@ -123,6 +90,63 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { } } + let mut cleaner = BadImplStripper { prims, items: crate_items }; + + let mut type_did_to_deref_target: FxHashMap<DefId, &Type> = FxHashMap::default(); + // Gather all type to `Deref` target edges. + for it in &new_items { + if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = *it.kind { + if trait_.def_id() == cx.tcx.lang_items().deref_trait() { + let target = items.iter().find_map(|item| match *item.kind { + TypedefItem(ref t, true) => Some(&t.type_), + _ => None, + }); + if let (Some(for_did), Some(target)) = (for_.def_id(), target) { + type_did_to_deref_target.insert(for_did, target); + } + } + } + } + // Follow all `Deref` targets of included items and recursively add them as valid + fn add_deref_target( + map: &FxHashMap<DefId, &Type>, + cleaner: &mut BadImplStripper, + type_did: &DefId, + ) { + if let Some(target) = map.get(type_did) { + debug!("add_deref_target: type {:?}, target {:?}", type_did, target); + if let Some(target_prim) = target.primitive_type() { + cleaner.prims.insert(target_prim); + } else if let Some(target_did) = target.def_id() { + // `impl Deref<Target = S> for S` + if target_did == *type_did { + // Avoid infinite cycles + return; + } + cleaner.items.insert(target_did); + add_deref_target(map, cleaner, &target_did); + } + } + } + for type_did in type_did_to_deref_target.keys() { + // Since only the `DefId` portion of the `Type` instances is known to be same for both the + // `Deref` target type and the impl for type positions, this map of types is keyed by + // `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly. + if cleaner.keep_impl_with_def_id(type_did) { + add_deref_target(&type_did_to_deref_target, &mut cleaner, type_did); + } + } + + new_items.retain(|it| { + if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = *it.kind { + cleaner.keep_impl(for_) + || trait_.as_ref().map_or(false, |t| cleaner.keep_impl(t)) + || blanket_impl.is_some() + } else { + true + } + }); + if let Some(ref mut it) = krate.module { if let ModuleItem(Module { ref mut items, .. }) = *it.kind { items.extend(synth.impls); @@ -192,16 +216,20 @@ struct BadImplStripper { } impl BadImplStripper { - fn keep_item(&self, ty: &Type) -> bool { + fn keep_impl(&self, ty: &Type) -> bool { if let Generic(_) = ty { // keep impls made on generics true } else if let Some(prim) = ty.primitive_type() { self.prims.contains(&prim) } else if let Some(did) = ty.def_id() { - self.items.contains(&did) + self.keep_impl_with_def_id(&did) } else { false } } + + fn keep_impl_with_def_id(&self, did: &DefId) -> bool { + self.items.contains(did) + } } diff --git a/src/librustdoc/passes/html_tags.rs b/src/librustdoc/passes/html_tags.rs index a7a1ba1118d..38ec2bef0ad 100644 --- a/src/librustdoc/passes/html_tags.rs +++ b/src/librustdoc/passes/html_tags.rs @@ -59,7 +59,7 @@ fn drop_tag( continue; } let last_tag_name_low = last_tag_name.to_lowercase(); - if ALLOWED_UNCLOSED.iter().any(|&at| at == &last_tag_name_low) { + if ALLOWED_UNCLOSED.iter().any(|&at| at == last_tag_name_low) { continue; } // `tags` is used as a queue, meaning that everything after `pos` is included inside it. diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 51818d7faf0..7ac42c75992 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -14,9 +14,6 @@ crate use stripper::*; mod non_autolinks; crate use self::non_autolinks::CHECK_NON_AUTOLINKS; -mod collapse_docs; -crate use self::collapse_docs::COLLAPSE_DOCS; - mod strip_hidden; crate use self::strip_hidden::STRIP_HIDDEN; @@ -84,7 +81,6 @@ crate const PASSES: &[Pass] = &[ CHECK_PRIVATE_ITEMS_DOC_TESTS, STRIP_HIDDEN, UNINDENT_COMMENTS, - COLLAPSE_DOCS, STRIP_PRIVATE, STRIP_PRIV_IMPORTS, PROPAGATE_DOC_CFG, @@ -99,7 +95,6 @@ crate const PASSES: &[Pass] = &[ /// The list of passes run by default. crate const DEFAULT_PASSES: &[ConditionalPass] = &[ ConditionalPass::always(COLLECT_TRAIT_IMPLS), - ConditionalPass::always(COLLAPSE_DOCS), ConditionalPass::always(UNINDENT_COMMENTS), ConditionalPass::always(CHECK_PRIVATE_ITEMS_DOC_TESTS), ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden), diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index 01e3d0acaa8..a276b7a6337 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -26,9 +26,7 @@ crate fn strip_hidden(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate { // strip all impls referencing stripped items let mut stripper = ImplStripper { retained: &retained }; - let krate = stripper.fold_crate(krate); - - krate + stripper.fold_crate(krate) } struct Stripper<'a> { diff --git a/src/librustdoc/passes/unindent_comments.rs b/src/librustdoc/passes/unindent_comments.rs index d0345d1e48c..1cad480d4e8 100644 --- a/src/librustdoc/passes/unindent_comments.rs +++ b/src/librustdoc/passes/unindent_comments.rs @@ -68,7 +68,7 @@ fn unindent_fragments(docs: &mut Vec<DocFragment>) { let min_indent = match docs .iter() .map(|fragment| { - fragment.doc.lines().fold(usize::MAX, |min_indent, line| { + fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| { if line.chars().all(|c| c.is_whitespace()) { min_indent } else { @@ -87,7 +87,7 @@ fn unindent_fragments(docs: &mut Vec<DocFragment>) { }; for fragment in docs { - if fragment.doc.lines().count() == 0 { + if fragment.doc.as_str().lines().count() == 0 { continue; } @@ -97,18 +97,6 @@ fn unindent_fragments(docs: &mut Vec<DocFragment>) { min_indent }; - fragment.doc = fragment - .doc - .lines() - .map(|line| { - if line.chars().all(|c| c.is_whitespace()) { - line.to_string() - } else { - assert!(line.len() >= min_indent); - line[min_indent..].to_string() - } - }) - .collect::<Vec<_>>() - .join("\n"); + fragment.indent = min_indent; } } diff --git a/src/librustdoc/passes/unindent_comments/tests.rs b/src/librustdoc/passes/unindent_comments/tests.rs index 9dec71f7683..9c9924841b9 100644 --- a/src/librustdoc/passes/unindent_comments/tests.rs +++ b/src/librustdoc/passes/unindent_comments/tests.rs @@ -1,21 +1,27 @@ use super::*; use rustc_span::source_map::DUMMY_SP; +use rustc_span::symbol::Symbol; +use rustc_span::with_default_session_globals; fn create_doc_fragment(s: &str) -> Vec<DocFragment> { vec![DocFragment { line: 0, span: DUMMY_SP, parent_module: None, - doc: s.to_string(), + doc: Symbol::intern(s), kind: DocFragmentKind::SugaredDoc, + need_backline: false, + indent: 0, }] } #[track_caller] fn run_test(input: &str, expected: &str) { - let mut s = create_doc_fragment(input); - unindent_fragments(&mut s); - assert_eq!(s[0].doc, expected); + with_default_session_globals(|| { + let mut s = create_doc_fragment(input); + unindent_fragments(&mut s); + assert_eq!(&s.iter().collect::<String>(), expected); + }); } #[test] diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs index 3bcf64f91c9..24d57705412 100644 --- a/src/librustdoc/theme.rs +++ b/src/librustdoc/theme.rs @@ -70,15 +70,15 @@ impl Events { } fn is_comment(&self) -> bool { - match *self { - Events::StartLineComment(_) | Events::StartComment(_) | Events::EndComment(_) => true, - _ => false, - } + matches!( + self, + Events::StartLineComment(_) | Events::StartComment(_) | Events::EndComment(_) + ) } } fn previous_is_line_comment(events: &[Events]) -> bool { - if let Some(&Events::StartLineComment(_)) = events.last() { true } else { false } + matches!(events.last(), Some(&Events::StartLineComment(_))) } fn is_line_comment(pos: usize, v: &[u8], events: &[Events]) -> bool { diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 3c0aeaad43e..1fedd26a1ef 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -61,20 +61,60 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } crate fn visit(mut self, krate: &'tcx hir::Crate<'_>) -> Module<'tcx> { - let mut module = self.visit_mod_contents( + let mut top_level_module = self.visit_mod_contents( krate.item.span, &Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Public }, hir::CRATE_HIR_ID, &krate.item.module, None, ); - // Attach the crate's exported macros to the top-level module: - module.macros.extend(krate.exported_macros.iter().map(|def| (def, None))); - module.is_crate = true; - + top_level_module.is_crate = true; + // Attach the crate's exported macros to the top-level module. + // In the case of macros 2.0 (`pub macro`), and for built-in `derive`s or attributes as + // well (_e.g._, `Copy`), these are wrongly bundled in there too, so we need to fix that by + // moving them back to their correct locations. + 'exported_macros: for def in krate.exported_macros { + // The `def` of a macro in `exported_macros` should correspond to either: + // - a `#[macro_export] macro_rules!` macro, + // - a built-in `derive` (or attribute) macro such as the ones in `::core`, + // - a `pub macro`. + // Only the last two need to be fixed, thus: + if def.ast.macro_rules { + top_level_module.macros.push((def, None)); + continue 'exported_macros; + } + let tcx = self.cx.tcx; + // Note: this is not the same as `.parent_module()`. Indeed, the latter looks + // for the closest module _ancestor_, which is not necessarily a direct parent + // (since a direct parent isn't necessarily a module, c.f. #77828). + let macro_parent_def_id = { + use rustc_middle::ty::DefIdTree; + tcx.parent(tcx.hir().local_def_id(def.hir_id).to_def_id()).unwrap() + }; + let macro_parent_path = tcx.def_path(macro_parent_def_id); + // HACK: rustdoc has no way to lookup `doctree::Module`s by their HirId. Instead, + // lookup the module by its name, by looking at each path segment one at a time. + let mut cur_mod = &mut top_level_module; + for path_segment in macro_parent_path.data { + // Path segments may refer to a module (in which case they belong to the type + // namespace), which is _necessary_ for the macro to be accessible outside it + // (no "associated macros" as of yet). Else we bail with an outer `continue`. + let path_segment_ty_ns = match path_segment.data { + rustc_hir::definitions::DefPathData::TypeNs(symbol) => symbol, + _ => continue 'exported_macros, + }; + // Descend into the child module that matches this path segment (if any). + match cur_mod.mods.iter_mut().find(|child| child.name == Some(path_segment_ty_ns)) { + Some(child_mod) => cur_mod = &mut *child_mod, + None => continue 'exported_macros, + } + } + let cur_mod_def_id = tcx.hir().local_def_id(cur_mod.id).to_def_id(); + assert_eq!(cur_mod_def_id, macro_parent_def_id); + cur_mod.macros.push((def, None)); + } self.cx.renderinfo.get_mut().exact_paths = self.exact_paths; - - module + top_level_module } fn visit_mod_contents( diff --git a/src/test/codegen/sanitizer-no-sanitize.rs b/src/test/codegen/sanitizer-no-sanitize.rs index 1b2b18822e6..fb9d249da03 100644 --- a/src/test/codegen/sanitizer-no-sanitize.rs +++ b/src/test/codegen/sanitizer-no-sanitize.rs @@ -1,4 +1,4 @@ -// Verifies that no_sanitze attribute can be used to +// Verifies that no_sanitize attribute can be used to // selectively disable sanitizer instrumentation. // // needs-sanitizer-address diff --git a/src/test/incremental/hygiene/load_cached_hygiene.rs b/src/test/incremental/hygiene/load_cached_hygiene.rs index 8124141418b..d6a5cb993a4 100644 --- a/src/test/incremental/hygiene/load_cached_hygiene.rs +++ b/src/test/incremental/hygiene/load_cached_hygiene.rs @@ -1,5 +1,5 @@ // revisions:rpass1 rpass2 -// compile-flags: -Z query-dep-graph +// compile-flags: -Z query-dep-graph -O // aux-build:cached_hygiene.rs // This tests the folllowing scenario @@ -19,7 +19,12 @@ // the metadata. Specifically, we were not resetting `orig_id` // for an `EpxnData` generate in the current crate, which would cause // us to serialize the `ExpnId` pointing to a garbage location in -// the metadata. +// the metadata.o + +// NOTE: We're explicitly passing the `-O` optimization flag because if optimizations are not +// enabled, then rustc will ignore the `#[inline(always)]` attribute which means we do not load +// the optimized mir for the unmodified function to be loaded and so the CGU containing that +// function will be reused. #![feature(rustc_attrs)] diff --git a/src/test/incremental/remapped_paths_cc/main.rs b/src/test/incremental/remapped_paths_cc/main.rs index b01f02444ea..735635029da 100644 --- a/src/test/incremental/remapped_paths_cc/main.rs +++ b/src/test/incremental/remapped_paths_cc/main.rs @@ -1,11 +1,18 @@ // revisions:rpass1 rpass2 rpass3 -// compile-flags: -Z query-dep-graph -g +// compile-flags: -Z query-dep-graph -g -O // aux-build:extern_crate.rs // ignore-asmjs wasm2js does not support source maps yet + // This test case makes sure that we detect if paths emitted into debuginfo // are changed, even when the change happens in an external crate. +// NOTE: We're explicitly passing the `-O` optimization flag because if no optimizations are +// requested, rustc will ignore the `#[inline]` attribute. This is a performance optimization for +// non-optimized builds which causes us to generate fewer copies of inlined functions when +// runtime performance doesn't matter. Without this flag, the function will go into a different +// CGU which can be reused by this crate. + #![feature(rustc_attrs)] #![rustc_partition_reused(module="main", cfg="rpass2")] diff --git a/src/test/run-make-fulldeps/coverage-reports/Makefile b/src/test/run-make-fulldeps/coverage-reports/Makefile index c4700b317ef..f98245b4a99 100644 --- a/src/test/run-make-fulldeps/coverage-reports/Makefile +++ b/src/test/run-make-fulldeps/coverage-reports/Makefile @@ -172,7 +172,7 @@ else # files are redundant, so there is no need to generate `expected_*.json` files or # compare actual JSON results.) - $(DIFF) --ignore-matching-lines='::<.*>.*:$$' \ + $(DIFF) --ignore-matching-lines='^ | .*::<.*>.*:$$' --ignore-matching-lines='^ | <.*>::.*:$$' \ expected_show_coverage.$@.txt "$(TMPDIR)"/actual_show_coverage.$@.txt || \ ( grep -q '^\/\/ ignore-llvm-cov-show-diffs' $(SOURCEDIR)/$@.rs && \ >&2 echo 'diff failed, but suppressed with `// ignore-llvm-cov-show-diffs` in $(SOURCEDIR)/$@.rs' \ diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.doctest.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.doctest.txt index e1731c7223c..8f67170561a 100644 --- a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.doctest.txt +++ b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.doctest.txt @@ -21,50 +21,86 @@ 20| |//! 21| |//! doctest returning a result: 22| 1|//! ``` - 23| 1|//! #[derive(Debug)] - 24| 1|//! struct SomeError; - 25| 1|//! let mut res = Err(SomeError); - 26| 1|//! if res.is_ok() { - 27| 0|//! res?; - 28| 1|//! } else { - 29| 1|//! res = Ok(0); - 30| 1|//! } - 31| |//! // need to be explicit because rustdoc cant infer the return type - 32| 1|//! Ok::<(), SomeError>(()) - 33| 1|//! ``` - 34| |//! - 35| |//! doctest with custom main: - 36| |//! ``` - 37| |//! #[derive(Debug)] - 38| |//! struct SomeError; - 39| |//! - 40| |//! extern crate doctest_crate; - 41| |//! - 42| 1|//! fn doctest_main() -> Result<(), SomeError> { - 43| 1|//! doctest_crate::fn_run_in_doctests(2); - 44| 1|//! Ok(()) - 45| 1|//! } - 46| |//! - 47| |//! // this `main` is not shown as covered, as it clashes with all the other - 48| |//! // `main` functions that were automatically generated for doctests - 49| |//! fn main() -> Result<(), SomeError> { - 50| |//! doctest_main() - 51| |//! } - 52| |//! ``` - 53| | - 54| |/// doctest attached to fn testing external code: - 55| |/// ``` - 56| 1|/// extern crate doctest_crate; - 57| 1|/// doctest_crate::fn_run_in_doctests(3); - 58| 1|/// ``` - 59| |/// - 60| 1|fn main() { - 61| 1| if true { - 62| 1| assert_eq!(1, 1); - 63| | } else { - 64| | assert_eq!(1, 2); - 65| | } - 66| 1|} + 23| 2|//! #[derive(Debug, PartialEq)] + ^1 + 24| 1|//! struct SomeError { + 25| 1|//! msg: String, + 26| 1|//! } + 27| 1|//! let mut res = Err(SomeError { msg: String::from("a message") }); + 28| 1|//! if res.is_ok() { + 29| 0|//! res?; + 30| |//! } else { + 31| 1|//! if *res.as_ref().unwrap_err() == *res.as_ref().unwrap_err() { + 32| 1|//! println!("{:?}", res); + 33| 1|//! } + ^0 + 34| 1|//! if *res.as_ref().unwrap_err() == *res.as_ref().unwrap_err() { + 35| 1|//! res = Ok(1); + 36| 1|//! } + ^0 + 37| 1|//! res = Ok(0); + 38| |//! } + 39| |//! // need to be explicit because rustdoc cant infer the return type + 40| 1|//! Ok::<(), SomeError>(()) + 41| 1|//! ``` + 42| |//! + 43| |//! doctest with custom main: + 44| |//! ``` + 45| 1|//! fn some_func() { + 46| 1|//! println!("called some_func()"); + 47| 1|//! } + 48| |//! + 49| |//! #[derive(Debug)] + 50| |//! struct SomeError; + 51| |//! + 52| |//! extern crate doctest_crate; + 53| |//! + 54| 1|//! fn doctest_main() -> Result<(), SomeError> { + 55| 1|//! some_func(); + 56| 1|//! doctest_crate::fn_run_in_doctests(2); + 57| 1|//! Ok(()) + 58| 1|//! } + 59| |//! + 60| |//! // this `main` is not shown as covered, as it clashes with all the other + 61| |//! // `main` functions that were automatically generated for doctests + 62| |//! fn main() -> Result<(), SomeError> { + 63| |//! doctest_main() + 64| |//! } + 65| |//! ``` + 66| | + 67| |/// doctest attached to fn testing external code: + 68| |/// ``` + 69| 1|/// extern crate doctest_crate; + 70| 1|/// doctest_crate::fn_run_in_doctests(3); + 71| 1|/// ``` + 72| |/// + 73| 1|fn main() { + 74| 1| if true { + 75| 1| assert_eq!(1, 1); + 76| | } else { + 77| | assert_eq!(1, 2); + 78| | } + 79| 1|} + 80| | + 81| |// FIXME(Swatinem): Fix known issue that coverage code region columns need to be offset by the + 82| |// doc comment line prefix (`///` or `//!`) and any additional indent (before or after the doc + 83| |// comment characters). This test produces `llvm-cov show` results demonstrating the problem. + 84| |// + 85| |// One of the above tests now includes: `derive(Debug, PartialEq)`, producing an `llvm-cov show` + 86| |// result with a distinct count for `Debug`, denoted by `^1`, but the caret points to the wrong + 87| |// column. Similarly, the `if` blocks without `else` blocks show `^0`, which should point at, or + 88| |// one character past, the `if` block's closing brace. In both cases, these are most likely off + 89| |// by the number of characters stripped from the beginning of each doc comment line: indent + 90| |// whitespace, if any, doc comment prefix (`//!` in this case) and (I assume) one space character + 91| |// (?). Note, when viewing `llvm-cov show` results in `--color` mode, the column offset errors are + 92| |// more pronounced, and show up in more places, with background color used to show some distinct + 93| |// code regions with different coverage counts. + 94| |// + 95| |// NOTE: Since the doc comment line prefix may vary, one possible solution is to replace each + 96| |// character stripped from the beginning of doc comment lines with a space. This will give coverage + 97| |// results the correct column offsets, and I think it should compile correctly, but I don't know + 98| |// what affect it might have on diagnostic messages from the compiler, and whether anyone would care + 99| |// if the indentation changed. I don't know if there is a more viable solution. ../coverage/lib/doctest_crate.rs: 1| |/// A function run only from within doctests diff --git a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest/doctest.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest/doctest.main.-------.InstrumentCoverage.0.html index 8d074558aae..333476a2df5 100644 --- a/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest/doctest.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview/expected_mir_dump.doctest/doctest.main.-------.InstrumentCoverage.0.html @@ -69,59 +69,59 @@ For revisions in Pull Requests (PR): </style> </head> <body> -<div class="code" style="counter-reset: line 59"><span class="line"><span><span class="code even" style="--layer: 1"><span class="annotation">@0⦊</span>fn main() <span class="annotation">⦉@0</span></span></span><span class="code" style="--layer: 0">{</span></span> -<span class="line"><span class="code" style="--layer: 0"> if </span><span><span class="code even" style="--layer: 1" title="61:8-61:12: @0[1]: _1 = const true -61:8-61:12: @0[2]: FakeRead(ForMatchedPlace, _1)"><span class="annotation">@0⦊</span>true<span class="annotation">⦉@0</span></span></span><span class="code" style="--layer: 0"> {</span></span> -<span class="line"><span class="code" style="--layer: 0"> </span><span><span class="code odd" style="--layer: 1" title="62:9-62:26: @5[0]: _2 = const ()"><span class="annotation">@5⦊</span></span></span><span class="code even" style="--layer: 2" title="62:9-62:26: @6[5]: _75 = const main::promoted[3] -62:9-62:26: @6[6]: _18 = &(*_75) -62:9-62:26: @6[7]: _17 = &(*_18) -62:9-62:26: @6[8]: _16 = move _17 as &[&str] (Pointer(Unsize)) -62:9-62:26: @6[17]: _26 = &(*_8) -62:9-62:26: @6[18]: _25 = &_26 -62:9-62:26: @6[21]: _28 = &(*_9) -62:9-62:26: @6[22]: _27 = &_28 -62:9-62:26: @6[23]: _24 = (move _25, move _27) -62:9-62:26: @6[26]: FakeRead(ForMatchedPlace, _24) -62:9-62:26: @6[28]: _29 = (_24.0: &&i32) -62:9-62:26: @6[30]: _30 = (_24.1: &&i32) -62:9-62:26: @6[33]: _32 = &(*_29) -62:9-62:26: @6[35]: _33 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)) -62:9-62:26: @6.Call: _31 = ArgumentV1::new::<&i32>(move _32, move _33) -> [return: bb7, unwind: bb17] -62:9-62:26: @7[4]: _35 = &(*_30) -62:9-62:26: @7[6]: _36 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)) -62:9-62:26: @7.Call: _34 = ArgumentV1::new::<&i32>(move _35, move _36) -> [return: bb8, unwind: bb17] -62:9-62:26: @8[2]: _23 = [move _31, move _34] -62:9-62:26: @8[7]: _22 = &_23 -62:9-62:26: @8[8]: _21 = &(*_22) -62:9-62:26: @8[9]: _20 = move _21 as &[std::fmt::ArgumentV1] (Pointer(Unsize)) -62:9-62:26: @8.Call: _15 = Arguments::new_v1(move _16, move _20) -> [return: bb9, unwind: bb17] -62:9-62:26: @9.Call: core::panicking::panic_fmt(move _15) -> bb17"><span class="annotation">@4,6,7,8,9⦊</span>assert_eq!(1, 1);<span class="annotation">⦉@4,6,7,8,9</span></span><span><span class="code odd" style="--layer: 1" title="62:9-62:26: @5[0]: _2 = const ()"><span class="annotation">⦉@5</span></span></span><span class="code" style="--layer: 0"></span></span> +<div class="code" style="counter-reset: line 72"><span class="line"><span><span class="code even" style="--layer: 1"><span class="annotation">@0⦊</span>fn main() <span class="annotation">⦉@0</span></span></span><span class="code" style="--layer: 0">{</span></span> +<span class="line"><span class="code" style="--layer: 0"> if </span><span><span class="code even" style="--layer: 1" title="74:8-74:12: @0[1]: _1 = const true +74:8-74:12: @0[2]: FakeRead(ForMatchedPlace, _1)"><span class="annotation">@0⦊</span>true<span class="annotation">⦉@0</span></span></span><span class="code" style="--layer: 0"> {</span></span> +<span class="line"><span class="code" style="--layer: 0"> </span><span><span class="code odd" style="--layer: 1" title="75:9-75:26: @5[0]: _2 = const ()"><span class="annotation">@5⦊</span></span></span><span class="code even" style="--layer: 2" title="75:9-75:26: @6[5]: _75 = const main::promoted[3] +75:9-75:26: @6[6]: _18 = &(*_75) +75:9-75:26: @6[7]: _17 = &(*_18) +75:9-75:26: @6[8]: _16 = move _17 as &[&str] (Pointer(Unsize)) +75:9-75:26: @6[17]: _26 = &(*_8) +75:9-75:26: @6[18]: _25 = &_26 +75:9-75:26: @6[21]: _28 = &(*_9) +75:9-75:26: @6[22]: _27 = &_28 +75:9-75:26: @6[23]: _24 = (move _25, move _27) +75:9-75:26: @6[26]: FakeRead(ForMatchedPlace, _24) +75:9-75:26: @6[28]: _29 = (_24.0: &&i32) +75:9-75:26: @6[30]: _30 = (_24.1: &&i32) +75:9-75:26: @6[33]: _32 = &(*_29) +75:9-75:26: @6[35]: _33 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)) +75:9-75:26: @6.Call: _31 = ArgumentV1::new::<&i32>(move _32, move _33) -> [return: bb7, unwind: bb17] +75:9-75:26: @7[4]: _35 = &(*_30) +75:9-75:26: @7[6]: _36 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)) +75:9-75:26: @7.Call: _34 = ArgumentV1::new::<&i32>(move _35, move _36) -> [return: bb8, unwind: bb17] +75:9-75:26: @8[2]: _23 = [move _31, move _34] +75:9-75:26: @8[7]: _22 = &_23 +75:9-75:26: @8[8]: _21 = &(*_22) +75:9-75:26: @8[9]: _20 = move _21 as &[std::fmt::ArgumentV1] (Pointer(Unsize)) +75:9-75:26: @8.Call: _15 = Arguments::new_v1(move _16, move _20) -> [return: bb9, unwind: bb17] +75:9-75:26: @9.Call: core::panicking::panic_fmt(move _15) -> bb17"><span class="annotation">@4,6,7,8,9⦊</span>assert_eq!(1, 1);<span class="annotation">⦉@4,6,7,8,9</span></span><span><span class="code odd" style="--layer: 1" title="75:9-75:26: @5[0]: _2 = const ()"><span class="annotation">⦉@5</span></span></span><span class="code" style="--layer: 0"></span></span> <span class="line"><span class="code" style="--layer: 0"> } else {</span></span> -<span class="line"><span class="code" style="--layer: 0"> </span><span><span class="code even" style="--layer: 1" title="64:9-64:26: @11[0]: _37 = const ()"><span class="annotation">@11⦊</span></span></span><span class="code even" style="--layer: 2" title="64:9-64:26: @12[5]: _72 = const main::promoted[0] -64:9-64:26: @12[6]: _53 = &(*_72) -64:9-64:26: @12[7]: _52 = &(*_53) -64:9-64:26: @12[8]: _51 = move _52 as &[&str] (Pointer(Unsize)) -64:9-64:26: @12[17]: _61 = &(*_43) -64:9-64:26: @12[18]: _60 = &_61 -64:9-64:26: @12[21]: _63 = &(*_44) -64:9-64:26: @12[22]: _62 = &_63 -64:9-64:26: @12[23]: _59 = (move _60, move _62) -64:9-64:26: @12[26]: FakeRead(ForMatchedPlace, _59) -64:9-64:26: @12[28]: _64 = (_59.0: &&i32) -64:9-64:26: @12[30]: _65 = (_59.1: &&i32) -64:9-64:26: @12[33]: _67 = &(*_64) -64:9-64:26: @12[35]: _68 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)) -64:9-64:26: @12.Call: _66 = ArgumentV1::new::<&i32>(move _67, move _68) -> [return: bb13, unwind: bb17] -64:9-64:26: @13[4]: _70 = &(*_65) -64:9-64:26: @13[6]: _71 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)) -64:9-64:26: @13.Call: _69 = ArgumentV1::new::<&i32>(move _70, move _71) -> [return: bb14, unwind: bb17] -64:9-64:26: @14[2]: _58 = [move _66, move _69] -64:9-64:26: @14[7]: _57 = &_58 -64:9-64:26: @14[8]: _56 = &(*_57) -64:9-64:26: @14[9]: _55 = move _56 as &[std::fmt::ArgumentV1] (Pointer(Unsize)) -64:9-64:26: @14.Call: _50 = Arguments::new_v1(move _51, move _55) -> [return: bb15, unwind: bb17] -64:9-64:26: @15.Call: core::panicking::panic_fmt(move _50) -> bb17"><span class="annotation">@10,12,13,14,15⦊</span>assert_eq!(1, 2);<span class="annotation">⦉@10,12,13,14,15</span></span><span><span class="code even" style="--layer: 1" title="64:9-64:26: @11[0]: _37 = const ()"><span class="annotation">⦉@11</span></span></span><span class="code" style="--layer: 0"></span></span> +<span class="line"><span class="code" style="--layer: 0"> </span><span><span class="code even" style="--layer: 1" title="77:9-77:26: @11[0]: _37 = const ()"><span class="annotation">@11⦊</span></span></span><span class="code even" style="--layer: 2" title="77:9-77:26: @12[5]: _72 = const main::promoted[0] +77:9-77:26: @12[6]: _53 = &(*_72) +77:9-77:26: @12[7]: _52 = &(*_53) +77:9-77:26: @12[8]: _51 = move _52 as &[&str] (Pointer(Unsize)) +77:9-77:26: @12[17]: _61 = &(*_43) +77:9-77:26: @12[18]: _60 = &_61 +77:9-77:26: @12[21]: _63 = &(*_44) +77:9-77:26: @12[22]: _62 = &_63 +77:9-77:26: @12[23]: _59 = (move _60, move _62) +77:9-77:26: @12[26]: FakeRead(ForMatchedPlace, _59) +77:9-77:26: @12[28]: _64 = (_59.0: &&i32) +77:9-77:26: @12[30]: _65 = (_59.1: &&i32) +77:9-77:26: @12[33]: _67 = &(*_64) +77:9-77:26: @12[35]: _68 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)) +77:9-77:26: @12.Call: _66 = ArgumentV1::new::<&i32>(move _67, move _68) -> [return: bb13, unwind: bb17] +77:9-77:26: @13[4]: _70 = &(*_65) +77:9-77:26: @13[6]: _71 = <&i32 as Debug>::fmt as for<'r, 's, 't0> fn(&'r &i32, &'s mut std::fmt::Formatter<'t0>) -> std::result::Result<(), std::fmt::Error> (Pointer(ReifyFnPointer)) +77:9-77:26: @13.Call: _69 = ArgumentV1::new::<&i32>(move _70, move _71) -> [return: bb14, unwind: bb17] +77:9-77:26: @14[2]: _58 = [move _66, move _69] +77:9-77:26: @14[7]: _57 = &_58 +77:9-77:26: @14[8]: _56 = &(*_57) +77:9-77:26: @14[9]: _55 = move _56 as &[std::fmt::ArgumentV1] (Pointer(Unsize)) +77:9-77:26: @14.Call: _50 = Arguments::new_v1(move _51, move _55) -> [return: bb15, unwind: bb17] +77:9-77:26: @15.Call: core::panicking::panic_fmt(move _50) -> bb17"><span class="annotation">@10,12,13,14,15⦊</span>assert_eq!(1, 2);<span class="annotation">⦉@10,12,13,14,15</span></span><span><span class="code even" style="--layer: 1" title="77:9-77:26: @11[0]: _37 = const ()"><span class="annotation">⦉@11</span></span></span><span class="code" style="--layer: 0"></span></span> <span class="line"><span class="code" style="--layer: 0"> }</span></span> -<span class="line"><span class="code" style="--layer: 0">}</span><span><span class="code odd" style="--layer: 1" title="66:2-66:2: @16.Return: return"><span class="annotation">@16⦊</span>‸<span class="annotation">⦉@16</span></span></span></span></div> +<span class="line"><span class="code" style="--layer: 0">}</span><span><span class="code odd" style="--layer: 1" title="79:2-79:2: @16.Return: return"><span class="annotation">@16⦊</span>‸<span class="annotation">⦉@16</span></span></span></span></div> </body> </html> diff --git a/src/test/run-make-fulldeps/coverage/doctest.rs b/src/test/run-make-fulldeps/coverage/doctest.rs index e41d669bf0c..ec04ea57063 100644 --- a/src/test/run-make-fulldeps/coverage/doctest.rs +++ b/src/test/run-make-fulldeps/coverage/doctest.rs @@ -20,13 +20,21 @@ //! //! doctest returning a result: //! ``` -//! #[derive(Debug)] -//! struct SomeError; -//! let mut res = Err(SomeError); +//! #[derive(Debug, PartialEq)] +//! struct SomeError { +//! msg: String, +//! } +//! let mut res = Err(SomeError { msg: String::from("a message") }); //! if res.is_ok() { -//! res?; +//! res?; //! } else { -//! res = Ok(0); +//! if *res.as_ref().unwrap_err() == *res.as_ref().unwrap_err() { +//! println!("{:?}", res); +//! } +//! if *res.as_ref().unwrap_err() == *res.as_ref().unwrap_err() { +//! res = Ok(1); +//! } +//! res = Ok(0); //! } //! // need to be explicit because rustdoc cant infer the return type //! Ok::<(), SomeError>(()) @@ -34,12 +42,17 @@ //! //! doctest with custom main: //! ``` +//! fn some_func() { +//! println!("called some_func()"); +//! } +//! //! #[derive(Debug)] //! struct SomeError; //! //! extern crate doctest_crate; //! //! fn doctest_main() -> Result<(), SomeError> { +//! some_func(); //! doctest_crate::fn_run_in_doctests(2); //! Ok(()) //! } @@ -64,3 +77,23 @@ fn main() { assert_eq!(1, 2); } } + +// FIXME(Swatinem): Fix known issue that coverage code region columns need to be offset by the +// doc comment line prefix (`///` or `//!`) and any additional indent (before or after the doc +// comment characters). This test produces `llvm-cov show` results demonstrating the problem. +// +// One of the above tests now includes: `derive(Debug, PartialEq)`, producing an `llvm-cov show` +// result with a distinct count for `Debug`, denoted by `^1`, but the caret points to the wrong +// column. Similarly, the `if` blocks without `else` blocks show `^0`, which should point at, or +// one character past, the `if` block's closing brace. In both cases, these are most likely off +// by the number of characters stripped from the beginning of each doc comment line: indent +// whitespace, if any, doc comment prefix (`//!` in this case) and (I assume) one space character +// (?). Note, when viewing `llvm-cov show` results in `--color` mode, the column offset errors are +// more pronounced, and show up in more places, with background color used to show some distinct +// code regions with different coverage counts. +// +// NOTE: Since the doc comment line prefix may vary, one possible solution is to replace each +// character stripped from the beginning of doc comment lines with a space. This will give coverage +// results the correct column offsets, and I think it should compile correctly, but I don't know +// what affect it might have on diagnostic messages from the compiler, and whether anyone would care +// if the indentation changed. I don't know if there is a more viable solution. diff --git a/src/test/run-make-fulldeps/inline-always-many-cgu/Makefile b/src/test/run-make-fulldeps/inline-always-many-cgu/Makefile index 0cab955f644..d12a23fbbf0 100644 --- a/src/test/run-make-fulldeps/inline-always-many-cgu/Makefile +++ b/src/test/run-make-fulldeps/inline-always-many-cgu/Makefile @@ -1,7 +1,12 @@ -include ../tools.mk all: - $(RUSTC) foo.rs --emit llvm-ir -C codegen-units=2 + $(RUSTC) foo.rs --emit llvm-ir -C codegen-units=2 -C opt-level=0 + if ![cat $(TMPDIR)/*.ll | $(CGREP) -e '\bcall\b']; then \ + echo "not found call instruction when one was expected"; \ + exit 1; \ + fi + $(RUSTC) foo.rs --emit llvm-ir -C codegen-units=2 -C opt-level=1 if cat $(TMPDIR)/*.ll | $(CGREP) -e '\bcall\b'; then \ echo "found call instruction when one wasn't expected"; \ exit 1; \ diff --git a/src/test/rustdoc-ui/deref-recursive-cycle.rs b/src/test/rustdoc-ui/deref-recursive-cycle.rs new file mode 100644 index 00000000000..4cb518cbbbd --- /dev/null +++ b/src/test/rustdoc-ui/deref-recursive-cycle.rs @@ -0,0 +1,17 @@ +// check-pass +// #26207: Ensure `Deref` cycles are properly handled without errors. + +#[derive(Copy, Clone)] +struct S; + +impl std::ops::Deref for S { + type Target = S; + + fn deref(&self) -> &S { + self + } +} + +fn main() { + let s: S = *******S; +} diff --git a/src/test/rustdoc-ui/doc-alias-crate-level.rs b/src/test/rustdoc-ui/doc-alias-crate-level.rs index 309d0bc4d43..70618ac01df 100644 --- a/src/test/rustdoc-ui/doc-alias-crate-level.rs +++ b/src/test/rustdoc-ui/doc-alias-crate-level.rs @@ -1,5 +1,3 @@ -#![feature(doc_alias)] - #![doc(alias = "crate-level-not-working")] //~ ERROR #[doc(alias = "shouldn't work!")] //~ ERROR diff --git a/src/test/rustdoc-ui/doc-alias-crate-level.stderr b/src/test/rustdoc-ui/doc-alias-crate-level.stderr index a58e91c5aa7..9e746cba05f 100644 --- a/src/test/rustdoc-ui/doc-alias-crate-level.stderr +++ b/src/test/rustdoc-ui/doc-alias-crate-level.stderr @@ -1,11 +1,11 @@ error: '\'' character isn't allowed in `#[doc(alias = "...")]` - --> $DIR/doc-alias-crate-level.rs:5:15 + --> $DIR/doc-alias-crate-level.rs:3:15 | LL | #[doc(alias = "shouldn't work!")] | ^^^^^^^^^^^^^^^^^ error: `#![doc(alias = "...")]` isn't allowed as a crate level attribute - --> $DIR/doc-alias-crate-level.rs:3:8 + --> $DIR/doc-alias-crate-level.rs:1:8 | LL | #![doc(alias = "crate-level-not-working")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/rustdoc-ui/doc-alias-same-name.rs b/src/test/rustdoc-ui/doc-alias-same-name.rs new file mode 100644 index 00000000000..da97c267618 --- /dev/null +++ b/src/test/rustdoc-ui/doc-alias-same-name.rs @@ -0,0 +1,4 @@ +#![crate_type = "lib"] + +#[doc(alias = "Foo")] //~ ERROR +pub struct Foo; diff --git a/src/test/rustdoc-ui/doc-alias-same-name.stderr b/src/test/rustdoc-ui/doc-alias-same-name.stderr new file mode 100644 index 00000000000..5ba09a2eae1 --- /dev/null +++ b/src/test/rustdoc-ui/doc-alias-same-name.stderr @@ -0,0 +1,8 @@ +error: `#[doc(alias = "...")]` is the same as the item's name + --> $DIR/doc-alias-same-name.rs:3:7 + | +LL | #[doc(alias = "Foo")] + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/rustdoc-ui/ignore-block-help.rs b/src/test/rustdoc-ui/ignore-block-help.rs new file mode 100644 index 00000000000..c22dddd11df --- /dev/null +++ b/src/test/rustdoc-ui/ignore-block-help.rs @@ -0,0 +1,7 @@ +// check-pass + +/// ```ignore (to-prevent-tidy-error) +/// let heart = '❤️'; +/// ``` +//~^^^ WARN +pub struct X; diff --git a/src/test/rustdoc-ui/ignore-block-help.stderr b/src/test/rustdoc-ui/ignore-block-help.stderr new file mode 100644 index 00000000000..d45cd92d2d1 --- /dev/null +++ b/src/test/rustdoc-ui/ignore-block-help.stderr @@ -0,0 +1,17 @@ +warning: could not parse code block as Rust code + --> $DIR/ignore-block-help.rs:3:5 + | +LL | /// ```ignore (to-prevent-tidy-error) + | _____^ +LL | | /// let heart = '❤️'; +LL | | /// ``` + | |_______^ + | + = note: error from rustc: character literal may only contain one codepoint +help: `ignore` code blocks require valid Rust code for syntax highlighting. Mark blocks that do not contain Rust code as text + | +LL | /// ```text,ignore (to-prevent-tidy-error) + | ^^^^^^^^ + +warning: 1 warning emitted + diff --git a/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.rs b/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.rs new file mode 100644 index 00000000000..0d1d5d1134b --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.rs @@ -0,0 +1,3 @@ +#![deny(broken_intra_doc_links)] +//! [static@u8::MIN] +//~^ ERROR incompatible link kind diff --git a/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.stderr b/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.stderr new file mode 100644 index 00000000000..ed1c10f9e0c --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.stderr @@ -0,0 +1,15 @@ +error: incompatible link kind for `u8::MIN` + --> $DIR/incompatible-primitive-disambiguator.rs:2:6 + | +LL | //! [static@u8::MIN] + | ^^^^^^^^^^^^^^ help: to link to the associated constant, prefix with `const@`: `const@u8::MIN` + | +note: the lint level is defined here + --> $DIR/incompatible-primitive-disambiguator.rs:1:9 + | +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: this link resolved to an associated constant, which is not a static + +error: aborting due to previous error + diff --git a/src/test/rustdoc-ui/intra-doc/unused-extern-crate.rs b/src/test/rustdoc-ui/intra-doc/unused-extern-crate.rs new file mode 100644 index 00000000000..186503cf69d --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/unused-extern-crate.rs @@ -0,0 +1,5 @@ +// compile-flags: --extern zip=whatever.rlib +#![deny(broken_intra_doc_links)] +/// See [zip] crate. +//~^ ERROR unresolved +pub struct ArrayZip; diff --git a/src/test/rustdoc-ui/intra-doc/unused-extern-crate.stderr b/src/test/rustdoc-ui/intra-doc/unused-extern-crate.stderr new file mode 100644 index 00000000000..b3b57fd1318 --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/unused-extern-crate.stderr @@ -0,0 +1,15 @@ +error: unresolved link to `zip` + --> $DIR/unused-extern-crate.rs:3:10 + | +LL | /// See [zip] crate. + | ^^^ no item named `zip` in scope + | +note: the lint level is defined here + --> $DIR/unused-extern-crate.rs:2:9 + | +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: aborting due to previous error + diff --git a/src/test/rustdoc-ui/invalid-syntax.stderr b/src/test/rustdoc-ui/invalid-syntax.stderr index 9a7a4d21013..75acdc5ab5f 100644 --- a/src/test/rustdoc-ui/invalid-syntax.stderr +++ b/src/test/rustdoc-ui/invalid-syntax.stderr @@ -127,8 +127,10 @@ LL | /// ```text warning: could not parse code block as Rust code --> $DIR/invalid-syntax.rs:92:9 | -LL | /// \____/ - | ^^^^^^ +LL | /// \____/ + | _________^ +LL | | /// + | |_ | = note: error from rustc: unknown start of token: \ diff --git a/src/test/rustdoc-ui/range-pattern.rs b/src/test/rustdoc-ui/range-pattern.rs new file mode 100644 index 00000000000..fd255d02fcb --- /dev/null +++ b/src/test/rustdoc-ui/range-pattern.rs @@ -0,0 +1,3 @@ +// check-pass + +fn func(0u8..=255: u8) {} diff --git a/src/test/rustdoc-ui/reference-link-reports-error-once.rs b/src/test/rustdoc-ui/reference-link-reports-error-once.rs new file mode 100644 index 00000000000..7957ee373c4 --- /dev/null +++ b/src/test/rustdoc-ui/reference-link-reports-error-once.rs @@ -0,0 +1,20 @@ +#![deny(broken_intra_doc_links)] + +/// Links to [a] [link][a] +/// And also a [third link][a] +/// And also a [reference link][b] +/// +/// Other links to the same target should still emit error: [ref] //~ERROR unresolved link to `ref` +/// Duplicate [ref] //~ERROR unresolved link to `ref` +/// +/// Other links to other targets should still emit error: [ref2] //~ERROR unresolved link to `ref2` +/// Duplicate [ref2] //~ERROR unresolved link to `ref2` +/// +/// [a]: ref +//~^ ERROR unresolved link to `ref` +/// [b]: ref2 +//~^ ERROR unresolved link to + +/// [ref][] +//~^ ERROR unresolved link +pub fn f() {} diff --git a/src/test/rustdoc-ui/reference-link-reports-error-once.stderr b/src/test/rustdoc-ui/reference-link-reports-error-once.stderr new file mode 100644 index 00000000000..218eb334a6f --- /dev/null +++ b/src/test/rustdoc-ui/reference-link-reports-error-once.stderr @@ -0,0 +1,63 @@ +error: unresolved link to `ref` + --> $DIR/reference-link-reports-error-once.rs:13:10 + | +LL | /// [a]: ref + | ^^^ no item named `ref` in scope + | +note: the lint level is defined here + --> $DIR/reference-link-reports-error-once.rs:1:9 + | +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `ref2` + --> $DIR/reference-link-reports-error-once.rs:15:10 + | +LL | /// [b]: ref2 + | ^^^^ no item named `ref2` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `ref` + --> $DIR/reference-link-reports-error-once.rs:7:62 + | +LL | /// Other links to the same target should still emit error: [ref] + | ^^^ no item named `ref` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `ref` + --> $DIR/reference-link-reports-error-once.rs:8:16 + | +LL | /// Duplicate [ref] + | ^^^ no item named `ref` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `ref2` + --> $DIR/reference-link-reports-error-once.rs:10:60 + | +LL | /// Other links to other targets should still emit error: [ref2] + | ^^^^ no item named `ref2` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `ref2` + --> $DIR/reference-link-reports-error-once.rs:11:16 + | +LL | /// Duplicate [ref2] + | ^^^^ no item named `ref2` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `ref` + --> $DIR/reference-link-reports-error-once.rs:18:6 + | +LL | /// [ref][] + | ^^^ no item named `ref` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: aborting due to 7 previous errors + diff --git a/src/test/rustdoc-ui/reference-links.rs b/src/test/rustdoc-ui/reference-links.rs index 7c1a79722c9..6e00b9f0fa1 100644 --- a/src/test/rustdoc-ui/reference-links.rs +++ b/src/test/rustdoc-ui/reference-links.rs @@ -4,4 +4,3 @@ //! //! [a]: std::process::Comman //~^ ERROR unresolved -//~| ERROR unresolved diff --git a/src/test/rustdoc-ui/reference-links.stderr b/src/test/rustdoc-ui/reference-links.stderr index 6ba73fbdb00..3df89df21b4 100644 --- a/src/test/rustdoc-ui/reference-links.stderr +++ b/src/test/rustdoc-ui/reference-links.stderr @@ -10,11 +10,5 @@ note: the lint level is defined here LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: unresolved link to `std::process::Comman` - --> $DIR/reference-links.rs:5:10 - | -LL | //! [a]: std::process::Comman - | ^^^^^^^^^^^^^^^^^^^^ no item named `Comman` in module `process` - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/rustdoc/auxiliary/macro_pub_in_module.rs b/src/test/rustdoc/auxiliary/macro_pub_in_module.rs new file mode 100644 index 00000000000..137b1238600 --- /dev/null +++ b/src/test/rustdoc/auxiliary/macro_pub_in_module.rs @@ -0,0 +1,13 @@ +// edition:2018 + +#![feature(decl_macro)] +#![crate_name = "external_crate"] + +pub mod some_module { + /* == Make sure the logic is not affected by a re-export == */ + mod private { + pub macro external_macro() {} + } + + pub use private::external_macro; +} diff --git a/src/test/rustdoc/deref-recursive-pathbuf.rs b/src/test/rustdoc/deref-recursive-pathbuf.rs new file mode 100644 index 00000000000..759e881aab4 --- /dev/null +++ b/src/test/rustdoc/deref-recursive-pathbuf.rs @@ -0,0 +1,26 @@ +// ignore-tidy-linelength + +// #26207: Show all methods reachable via Deref impls, recursing through multiple dereferencing +// levels and across multiple crates. + +// @has 'foo/struct.Foo.html' +// @has '-' '//*[@id="deref-methods-PathBuf"]' 'Methods from Deref<Target = PathBuf>' +// @has '-' '//*[@class="impl-items"]//*[@id="method.as_path"]' 'pub fn as_path(&self)' +// @has '-' '//*[@id="deref-methods-Path"]' 'Methods from Deref<Target = Path>' +// @has '-' '//*[@class="impl-items"]//*[@id="method.exists"]' 'pub fn exists(&self)' +// @has '-' '//*[@class="sidebar-title"][@href="#deref-methods-PathBuf"]' 'Methods from Deref<Target=PathBuf>' +// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.as_path"]' 'as_path' +// @has '-' '//*[@class="sidebar-title"][@href="#deref-methods-Path"]' 'Methods from Deref<Target=Path>' +// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.exists"]' 'exists' + +#![crate_name = "foo"] + +use std::ops::Deref; +use std::path::PathBuf; + +pub struct Foo(PathBuf); + +impl Deref for Foo { + type Target = PathBuf; + fn deref(&self) -> &PathBuf { &self.0 } +} diff --git a/src/test/rustdoc/deref-recursive.rs b/src/test/rustdoc/deref-recursive.rs new file mode 100644 index 00000000000..5aef87c38cd --- /dev/null +++ b/src/test/rustdoc/deref-recursive.rs @@ -0,0 +1,42 @@ +// ignore-tidy-linelength + +// #26207: Show all methods reachable via Deref impls, recursing through multiple dereferencing +// levels if needed. + +// @has 'foo/struct.Foo.html' +// @has '-' '//*[@id="deref-methods-Bar"]' 'Methods from Deref<Target = Bar>' +// @has '-' '//*[@class="impl-items"]//*[@id="method.bar"]' 'pub fn bar(&self)' +// @has '-' '//*[@id="deref-methods-Baz"]' 'Methods from Deref<Target = Baz>' +// @has '-' '//*[@class="impl-items"]//*[@id="method.baz"]' 'pub fn baz(&self)' +// @has '-' '//*[@class="sidebar-title"][@href="#deref-methods-Bar"]' 'Methods from Deref<Target=Bar>' +// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.bar"]' 'bar' +// @has '-' '//*[@class="sidebar-title"][@href="#deref-methods-Baz"]' 'Methods from Deref<Target=Baz>' +// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.baz"]' 'baz' + +#![crate_name = "foo"] + +use std::ops::Deref; + +pub struct Foo(Bar); +pub struct Bar(Baz); +pub struct Baz; + +impl Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Bar { &self.0 } +} + +impl Deref for Bar { + type Target = Baz; + fn deref(&self) -> &Baz { &self.0 } +} + +impl Bar { + /// This appears under `Foo` methods + pub fn bar(&self) {} +} + +impl Baz { + /// This should also appear in `Foo` methods when recursing + pub fn baz(&self) {} +} diff --git a/src/test/rustdoc/deref-typedef.rs b/src/test/rustdoc/deref-typedef.rs index 770f8d7289c..589f133b975 100644 --- a/src/test/rustdoc/deref-typedef.rs +++ b/src/test/rustdoc/deref-typedef.rs @@ -1,18 +1,29 @@ +// ignore-tidy-linelength + #![crate_name = "foo"] // @has 'foo/struct.Bar.html' -// @has '-' '//*[@id="deref-methods"]' 'Methods from Deref<Target = FooC>' +// @has '-' '//*[@id="deref-methods-FooJ"]' 'Methods from Deref<Target = FooJ>' // @has '-' '//*[@class="impl-items"]//*[@id="method.foo_a"]' 'pub fn foo_a(&self)' // @has '-' '//*[@class="impl-items"]//*[@id="method.foo_b"]' 'pub fn foo_b(&self)' // @has '-' '//*[@class="impl-items"]//*[@id="method.foo_c"]' 'pub fn foo_c(&self)' -// @has '-' '//*[@class="sidebar-title"]' 'Methods from Deref<Target=FooC>' +// @has '-' '//*[@class="impl-items"]//*[@id="method.foo_j"]' 'pub fn foo_j(&self)' +// @has '-' '//*[@class="sidebar-title"][@href="#deref-methods-FooJ"]' 'Methods from Deref<Target=FooJ>' // @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_a"]' 'foo_a' // @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_b"]' 'foo_b' // @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_c"]' 'foo_c' +// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_j"]' 'foo_j' pub struct FooA; pub type FooB = FooA; pub type FooC = FooB; +pub type FooD = FooC; +pub type FooE = FooD; +pub type FooF = FooE; +pub type FooG = FooF; +pub type FooH = FooG; +pub type FooI = FooH; +pub type FooJ = FooI; impl FooA { pub fn foo_a(&self) {} @@ -26,8 +37,12 @@ impl FooC { pub fn foo_c(&self) {} } +impl FooJ { + pub fn foo_j(&self) {} +} + pub struct Bar; impl std::ops::Deref for Bar { - type Target = FooC; + type Target = FooJ; fn deref(&self) -> &Self::Target { unimplemented!() } } diff --git a/src/test/rustdoc/intra-doc-crate/auxiliary/self.rs b/src/test/rustdoc/intra-doc-crate/auxiliary/self.rs index cdfe842f3cc..54902f12eb1 100644 --- a/src/test/rustdoc/intra-doc-crate/auxiliary/self.rs +++ b/src/test/rustdoc/intra-doc-crate/auxiliary/self.rs @@ -1,4 +1,7 @@ #![crate_name = "cross_crate_self"] + +/// Link to [Self] +/// Link to [crate] pub struct S; impl S { diff --git a/src/test/rustdoc/intra-doc-crate/self.rs b/src/test/rustdoc/intra-doc-crate/self.rs index 62aef8e85af..4db63b12b6b 100644 --- a/src/test/rustdoc/intra-doc-crate/self.rs +++ b/src/test/rustdoc/intra-doc-crate/self.rs @@ -1,6 +1,9 @@ // aux-build:self.rs +// build-aux-docs 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="../cross_crate_self/index.html"]' "crate" pub use cross_crate_self::S; diff --git a/src/test/rustdoc/intra-doc/non-path-primitives.rs b/src/test/rustdoc/intra-doc/non-path-primitives.rs index ad4f6ddd9de..2c7e7b5c48c 100644 --- a/src/test/rustdoc/intra-doc/non-path-primitives.rs +++ b/src/test/rustdoc/intra-doc/non-path-primitives.rs @@ -8,14 +8,21 @@ // @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.array.html#method.map"]' 'array::map' //! [array::map] +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html"]' 'owned str' +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html"]' 'str ref' +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html#method.is_empty"]' 'str::is_empty' +// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html#method.len"]' '&str::len' +//! [owned str][str] +//! [str ref][&str] +//! [str::is_empty] +//! [&str::len] + // @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.is_null"]' 'pointer::is_null' // @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.is_null"]' '*const::is_null' // @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.is_null"]' '*mut::is_null' -// @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.is_null"]' '*::is_null' //! [pointer::is_null] //! [*const::is_null] //! [*mut::is_null] -//! [*::is_null] // @has - '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.unit.html"]' 'unit' //! [unit] diff --git a/src/test/rustdoc/intra-doc/primitive-disambiguators.rs b/src/test/rustdoc/intra-doc/primitive-disambiguators.rs new file mode 100644 index 00000000000..acdd07566c9 --- /dev/null +++ b/src/test/rustdoc/intra-doc/primitive-disambiguators.rs @@ -0,0 +1,4 @@ +#![deny(broken_intra_doc_links)] +// @has primitive_disambiguators/index.html +// @has - '//a/@href' 'https://doc.rust-lang.org/nightly/std/primitive.str.html#method.trim' +//! [str::trim()] diff --git a/src/test/rustdoc/macro_pub_in_module.rs b/src/test/rustdoc/macro_pub_in_module.rs new file mode 100644 index 00000000000..4fd85d68994 --- /dev/null +++ b/src/test/rustdoc/macro_pub_in_module.rs @@ -0,0 +1,82 @@ +// aux-build:macro_pub_in_module.rs +// edition:2018 +// build-aux-docs + +//! See issue #74355 +#![feature(decl_macro, no_core, rustc_attrs)] +#![crate_name = "krate"] +#![no_core] + + // @has external_crate/some_module/macro.external_macro.html + // @!has external_crate/macro.external_macro.html +extern crate external_crate; + +pub mod inner { + // @has krate/inner/macro.raw_const.html + // @!has krate/macro.raw_const.html + pub macro raw_const() {} + + // @has krate/inner/macro.test.html + // @!has krate/macro.test.html + #[rustc_builtin_macro] + pub macro test($item:item) {} + + // @has krate/inner/macro.Clone.html + // @!has krate/macro.Clone.html + #[rustc_builtin_macro] + pub macro Clone($item:item) {} + + // Make sure the logic is not affected by re-exports. + mod unrenamed { + // @!has krate/macro.unrenamed.html + #[rustc_macro_transparency = "semitransparent"] + pub macro unrenamed() {} + } + // @has krate/inner/macro.unrenamed.html + pub use unrenamed::unrenamed; + + mod private { + // @!has krate/macro.m.html + pub macro m() {} + } + // @has krate/inner/macro.renamed.html + // @!has krate/macro.renamed.html + pub use private::m as renamed; + + mod private2 { + // @!has krate/macro.m2.html + pub macro m2() {} + } + use private2 as renamed_mod; + // @has krate/inner/macro.m2.html + pub use renamed_mod::m2; + + // @has krate/inner/macro.external_macro.html + // @!has krate/macro.external_macro.html + pub use ::external_crate::some_module::external_macro; +} + +// Namespaces: Make sure the logic does not mix up a function name with a module name… +fn both_fn_and_mod() { + // @!has krate/macro.in_both_fn_and_mod.html + pub macro in_both_fn_and_mod() {} +} +pub mod both_fn_and_mod { + // @!has krate/both_fn_and_mod/macro.in_both_fn_and_mod.html +} + +const __: () = { + // @!has krate/macro.in_both_const_and_mod.html + pub macro in_both_const_and_mod() {} +}; +pub mod __ { + // @!has krate/__/macro.in_both_const_and_mod.html +} + +enum Enum { + Crazy = { + // @!has krate/macro.this_is_getting_weird.html; + pub macro this_is_getting_weird() {} + 42 + }, +} diff --git a/src/test/rustdoc/macros.rs b/src/test/rustdoc/macros.rs index fb4f02ad160..ae0cf7a1478 100644 --- a/src/test/rustdoc/macros.rs +++ b/src/test/rustdoc/macros.rs @@ -8,3 +8,17 @@ macro_rules! my_macro { ($a:tt) => (); ($e:expr) => {}; } + +// Check that exported macro defined in a module are shown at crate root. +// @has macros/macro.my_sub_macro.html //pre 'macro_rules! my_sub_macro {' +// @has - //pre '() => { ... };' +// @has - //pre '($a:tt) => { ... };' +// @has - //pre '($e:expr) => { ... };' +mod sub { + #[macro_export] + macro_rules! my_sub_macro { + () => {}; + ($a:tt) => {}; + ($e:expr) => {}; + } +} diff --git a/src/test/rustdoc/range-arg-pattern.rs b/src/test/rustdoc/range-arg-pattern.rs new file mode 100644 index 00000000000..f4cc36b1055 --- /dev/null +++ b/src/test/rustdoc/range-arg-pattern.rs @@ -0,0 +1,5 @@ +#![crate_name = "foo"] + +// @has foo/fn.f.html +// @has - '//*[@class="rust fn"]' 'pub fn f(0u8 ...255: u8)' +pub fn f(0u8...255: u8) {} diff --git a/src/test/ui/abi/issues/issue-22565-rust-call.rs b/src/test/ui/abi/issues/issue-22565-rust-call.rs index 055d959b46e..a08e0bfb5e5 100644 --- a/src/test/ui/abi/issues/issue-22565-rust-call.rs +++ b/src/test/ui/abi/issues/issue-22565-rust-call.rs @@ -1,8 +1,32 @@ #![feature(unboxed_closures)] extern "rust-call" fn b(_i: i32) {} -//~^ ERROR A function with the "rust-call" ABI must take a single non-self argument that is a tuple +//~^ ERROR functions with the "rust-call" ABI must take a single non-self argument that is a tuple + +trait Tr { + extern "rust-call" fn a(); + + extern "rust-call" fn b() {} + //~^ ERROR functions with the "rust-call" ABI must take a single non-self argument +} + +struct Foo; + +impl Foo { + extern "rust-call" fn bar() {} + //~^ ERROR functions with the "rust-call" ABI must take a single non-self argument +} + +impl Tr for Foo { + extern "rust-call" fn a() {} + //~^ ERROR functions with the "rust-call" ABI must take a single non-self argument +} fn main () { b(10); + + Foo::bar(); + + <Foo as Tr>::a(); + <Foo as Tr>::b(); } diff --git a/src/test/ui/abi/issues/issue-22565-rust-call.stderr b/src/test/ui/abi/issues/issue-22565-rust-call.stderr index 31fb035eb99..3eee10bc5e9 100644 --- a/src/test/ui/abi/issues/issue-22565-rust-call.stderr +++ b/src/test/ui/abi/issues/issue-22565-rust-call.stderr @@ -1,8 +1,26 @@ -error: A function with the "rust-call" ABI must take a single non-self argument that is a tuple +error: functions with the "rust-call" ABI must take a single non-self argument that is a tuple --> $DIR/issue-22565-rust-call.rs:3:1 | LL | extern "rust-call" fn b(_i: i32) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: functions with the "rust-call" ABI must take a single non-self argument that is a tuple + --> $DIR/issue-22565-rust-call.rs:9:5 + | +LL | extern "rust-call" fn b() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions with the "rust-call" ABI must take a single non-self argument that is a tuple + --> $DIR/issue-22565-rust-call.rs:16:5 + | +LL | extern "rust-call" fn bar() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: functions with the "rust-call" ABI must take a single non-self argument that is a tuple + --> $DIR/issue-22565-rust-call.rs:21:5 + | +LL | extern "rust-call" fn a() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors diff --git a/src/test/ui/ast-json/ast-json-noexpand-output.stdout b/src/test/ui/ast-json/ast-json-noexpand-output.stdout index deb2a1af204..9c29a5f2337 100644 --- a/src/test/ui/ast-json/ast-json-noexpand-output.stdout +++ b/src/test/ui/ast-json/ast-json-noexpand-output.stdout @@ -1 +1 @@ -{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Interpolated","fields":[{"variant":"NtExpr","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":null}]}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]} +{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"kind":{"variant":"Interpolated","fields":[{"variant":"NtExpr","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":null}]}]},"span":{"lo":0,"hi":0}}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]} diff --git a/src/test/ui/ast-json/ast-json-output.stdout b/src/test/ui/ast-json/ast-json-output.stdout index 71336f452fc..cccd51985dc 100644 --- a/src/test/ui/ast-json/ast-json-output.stdout +++ b/src/test/ui/ast-json/ast-json-output.stdout @@ -1 +1 @@ -{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"v1","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Interpolated","fields":[{"variant":"NtExpr","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":null}]}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]} +{"module":{"inner":{"lo":0,"hi":0},"unsafety":"No","items":[{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"prelude_import","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"","span":{"lo":0,"hi":0}},"kind":{"variant":"Use","fields":[{"prefix":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"{{root}}","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"std","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"prelude","span":{"lo":0,"hi":0}},"id":0,"args":null},{"ident":{"name":"v1","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"kind":"Glob","span":{"lo":0,"hi":0}}]},"tokens":null},{"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"macro_use","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":"Empty","tokens":null},null]},"id":null,"style":"Outer","span":{"lo":0,"hi":0}}],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"std","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null},{"attrs":[],"id":0,"span":{"lo":0,"hi":0},"vis":{"kind":"Inherited","span":{"lo":0,"hi":0},"tokens":null},"ident":{"name":"core","span":{"lo":0,"hi":0}},"kind":{"variant":"ExternCrate","fields":[null]},"tokens":null}],"inline":true},"attrs":[{"kind":{"variant":"Normal","fields":[{"path":{"span":{"lo":0,"hi":0},"segments":[{"ident":{"name":"crate_type","span":{"lo":0,"hi":0}},"id":0,"args":null}],"tokens":null},"args":{"variant":"Eq","fields":[{"lo":0,"hi":0},{"kind":{"variant":"Interpolated","fields":[{"variant":"NtExpr","fields":[{"id":0,"kind":{"variant":"Lit","fields":[{"token":{"kind":"Str","symbol":"lib","suffix":null},"kind":{"variant":"Str","fields":["lib","Cooked"]},"span":{"lo":0,"hi":0}}]},"span":{"lo":0,"hi":0},"attrs":{"0":null},"tokens":null}]}]},"span":{"lo":0,"hi":0}}]},"tokens":null},{"0":[[{"variant":"Token","fields":[{"kind":"Pound","span":{"lo":0,"hi":0}}]},"Joint"],[{"variant":"Token","fields":[{"kind":"Not","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Delimited","fields":[{"open":{"lo":0,"hi":0},"close":{"lo":0,"hi":0}},"Bracket",{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Ident","fields":["crate_type",false]},"span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":"Eq","span":{"lo":0,"hi":0}}]},"Alone"],[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}]},"Alone"]]}]},"id":null,"style":"Inner","span":{"lo":0,"hi":0}}],"span":{"lo":0,"hi":0},"proc_macros":[]} diff --git a/src/test/ui/async-await/feature-async-closure.stderr b/src/test/ui/async-await/feature-async-closure.stderr index ba851ba7d29..485a838b67f 100644 --- a/src/test/ui/async-await/feature-async-closure.stderr +++ b/src/test/ui/async-await/feature-async-closure.stderr @@ -6,6 +6,7 @@ LL | let _ = async || {}; | = note: see issue #62290 <https://github.com/rust-lang/rust/issues/62290> for more information = help: add `#![feature(async_closure)]` to the crate attributes to enable + = help: to use an async block, remove the `||`: `async {` error: aborting due to previous error diff --git a/src/test/ui/attributes/key-value-expansion-on-mac.rs b/src/test/ui/attributes/key-value-expansion-on-mac.rs new file mode 100644 index 00000000000..1247ff2b230 --- /dev/null +++ b/src/test/ui/attributes/key-value-expansion-on-mac.rs @@ -0,0 +1,15 @@ +#![feature(extended_key_value_attributes)] +#![feature(rustc_attrs)] + +#[rustc_dummy = stringify!(a)] // OK +macro_rules! bar { + () => {}; +} + +// FIXME?: `bar` here expands before `stringify` has a chance to expand. +// `#[rustc_dummy = ...]` is validated and dropped during expansion of `bar`, +// the "unexpected token" errors comes from the validation. +#[rustc_dummy = stringify!(b)] //~ ERROR unexpected token: `stringify!(b)` +bar!(); + +fn main() {} diff --git a/src/test/ui/attributes/key-value-expansion-on-mac.stderr b/src/test/ui/attributes/key-value-expansion-on-mac.stderr new file mode 100644 index 00000000000..b74f3518a7e --- /dev/null +++ b/src/test/ui/attributes/key-value-expansion-on-mac.stderr @@ -0,0 +1,8 @@ +error: unexpected token: `stringify!(b)` + --> $DIR/key-value-expansion-on-mac.rs:12:17 + | +LL | #[rustc_dummy = stringify!(b)] + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/check-doc-alias-attr-location.rs b/src/test/ui/check-doc-alias-attr-location.rs index dac9b7372e0..3a0436e468d 100644 --- a/src/test/ui/check-doc-alias-attr-location.rs +++ b/src/test/ui/check-doc-alias-attr-location.rs @@ -1,5 +1,4 @@ #![crate_type="lib"] -#![feature(doc_alias)] pub struct Bar; pub trait Foo { diff --git a/src/test/ui/check-doc-alias-attr-location.stderr b/src/test/ui/check-doc-alias-attr-location.stderr index 29a99e4470e..a66e9939eaf 100644 --- a/src/test/ui/check-doc-alias-attr-location.stderr +++ b/src/test/ui/check-doc-alias-attr-location.stderr @@ -1,23 +1,23 @@ error: `#[doc(alias = "...")]` isn't allowed on extern block - --> $DIR/check-doc-alias-attr-location.rs:10:7 + --> $DIR/check-doc-alias-attr-location.rs:9:7 | LL | #[doc(alias = "foo")] | ^^^^^^^^^^^^^ error: `#[doc(alias = "...")]` isn't allowed on implementation block - --> $DIR/check-doc-alias-attr-location.rs:13:7 + --> $DIR/check-doc-alias-attr-location.rs:12:7 | LL | #[doc(alias = "bar")] | ^^^^^^^^^^^^^ error: `#[doc(alias = "...")]` isn't allowed on implementation block - --> $DIR/check-doc-alias-attr-location.rs:19:7 + --> $DIR/check-doc-alias-attr-location.rs:18:7 | LL | #[doc(alias = "foobar")] | ^^^^^^^^^^^^^^^^ error: `#[doc(alias = "...")]` isn't allowed on type alias in implementation block - --> $DIR/check-doc-alias-attr-location.rs:21:11 + --> $DIR/check-doc-alias-attr-location.rs:20:11 | LL | #[doc(alias = "assoc")] | ^^^^^^^^^^^^^^^ diff --git a/src/test/ui/check-doc-alias-attr.rs b/src/test/ui/check-doc-alias-attr.rs index 0ca2349a43b..912e35f9165 100644 --- a/src/test/ui/check-doc-alias-attr.rs +++ b/src/test/ui/check-doc-alias-attr.rs @@ -1,5 +1,4 @@ #![crate_type = "lib"] -#![feature(doc_alias)] #[doc(alias = "foo")] // ok! pub struct Bar; diff --git a/src/test/ui/check-doc-alias-attr.stderr b/src/test/ui/check-doc-alias-attr.stderr index dce325f9d38..1c7fc83bb8d 100644 --- a/src/test/ui/check-doc-alias-attr.stderr +++ b/src/test/ui/check-doc-alias-attr.stderr @@ -1,35 +1,35 @@ error: doc alias attribute expects a string: #[doc(alias = "a")] - --> $DIR/check-doc-alias-attr.rs:7:7 + --> $DIR/check-doc-alias-attr.rs:6:7 | LL | #[doc(alias)] | ^^^^^ error: doc alias attribute expects a string: #[doc(alias = "a")] - --> $DIR/check-doc-alias-attr.rs:8:7 + --> $DIR/check-doc-alias-attr.rs:7:7 | LL | #[doc(alias = 0)] | ^^^^^^^^^ error: doc alias attribute expects a string: #[doc(alias = "a")] - --> $DIR/check-doc-alias-attr.rs:9:7 + --> $DIR/check-doc-alias-attr.rs:8:7 | LL | #[doc(alias("bar"))] | ^^^^^^^^^^^^ error: '\"' character isn't allowed in `#[doc(alias = "...")]` - --> $DIR/check-doc-alias-attr.rs:10:15 + --> $DIR/check-doc-alias-attr.rs:9:15 | LL | #[doc(alias = "\"")] | ^^^^ error: '\n' character isn't allowed in `#[doc(alias = "...")]` - --> $DIR/check-doc-alias-attr.rs:11:15 + --> $DIR/check-doc-alias-attr.rs:10:15 | LL | #[doc(alias = "\n")] | ^^^^ error: '\n' character isn't allowed in `#[doc(alias = "...")]` - --> $DIR/check-doc-alias-attr.rs:12:15 + --> $DIR/check-doc-alias-attr.rs:11:15 | LL | #[doc(alias = " | _______________^ @@ -37,19 +37,19 @@ LL | | ")] | |_^ error: '\t' character isn't allowed in `#[doc(alias = "...")]` - --> $DIR/check-doc-alias-attr.rs:14:15 + --> $DIR/check-doc-alias-attr.rs:13:15 | LL | #[doc(alias = "\t")] | ^^^^ error: `#[doc(alias = "...")]` cannot start or end with ' ' - --> $DIR/check-doc-alias-attr.rs:15:15 + --> $DIR/check-doc-alias-attr.rs:14:15 | LL | #[doc(alias = " hello")] | ^^^^^^^^ error: `#[doc(alias = "...")]` cannot start or end with ' ' - --> $DIR/check-doc-alias-attr.rs:16:15 + --> $DIR/check-doc-alias-attr.rs:15:15 | LL | #[doc(alias = "hello ")] | ^^^^^^^^ diff --git a/src/test/ui/codemap_tests/tab_3.stderr b/src/test/ui/codemap_tests/tab_3.stderr index 958d54bbb15..e067dbbf85b 100644 --- a/src/test/ui/codemap_tests/tab_3.stderr +++ b/src/test/ui/codemap_tests/tab_3.stderr @@ -9,7 +9,7 @@ LL | { LL | println!("{:?}", some_vec); | ^^^^^^^^ value borrowed here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `some_vec` +note: this function takes ownership of the receiver `self`, which moves `some_vec` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; diff --git a/src/test/ui/const-generics/array-size-in-generic-struct-param.min.stderr b/src/test/ui/const-generics/array-size-in-generic-struct-param.min.stderr index 7af23103ce7..8f6e56826fa 100644 --- a/src/test/ui/const-generics/array-size-in-generic-struct-param.min.stderr +++ b/src/test/ui/const-generics/array-size-in-generic-struct-param.min.stderr @@ -23,7 +23,7 @@ LL | struct B<const CFG: Config> { | ^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 3 previous errors diff --git a/src/test/ui/const-generics/const-param-before-other-params.min.stderr b/src/test/ui/const-generics/const-param-before-other-params.min.stderr index 1e49588f1b4..a9349ce43c9 100644 --- a/src/test/ui/const-generics/const-param-before-other-params.min.stderr +++ b/src/test/ui/const-generics/const-param-before-other-params.min.stderr @@ -17,7 +17,7 @@ LL | fn bar<const X: (), 'a>(_: &'a ()) { | ^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `()` is forbidden as the type of a const generic parameter --> $DIR/const-param-before-other-params.rs:10:17 @@ -26,7 +26,7 @@ LL | fn foo<const X: (), T>(_: &T) {} | ^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 4 previous errors diff --git a/src/test/ui/const-generics/const-param-elided-lifetime.min.stderr b/src/test/ui/const-generics/const-param-elided-lifetime.min.stderr index 5d3c8f7b2e6..48d33a785ae 100644 --- a/src/test/ui/const-generics/const-param-elided-lifetime.min.stderr +++ b/src/test/ui/const-generics/const-param-elided-lifetime.min.stderr @@ -35,7 +35,7 @@ LL | struct A<const N: &u8>; | ^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `&'static u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:15:15 @@ -44,7 +44,7 @@ LL | impl<const N: &u8> A<N> { | ^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `&'static u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:23:15 @@ -53,7 +53,7 @@ LL | impl<const N: &u8> B for A<N> {} | ^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `&'static u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:27:17 @@ -62,7 +62,7 @@ LL | fn bar<const N: &u8>() {} | ^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `&'static u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:18:21 @@ -71,7 +71,7 @@ LL | fn foo<const M: &u8>(&self) {} | ^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 10 previous errors diff --git a/src/test/ui/const-generics/const-param-type-depends-on-const-param.min.stderr b/src/test/ui/const-generics/const-param-type-depends-on-const-param.min.stderr index d63bc236320..9804363f39a 100644 --- a/src/test/ui/const-generics/const-param-type-depends-on-const-param.min.stderr +++ b/src/test/ui/const-generics/const-param-type-depends-on-const-param.min.stderr @@ -17,7 +17,7 @@ LL | pub struct Dependent<const N: usize, const X: [u8; N]>([(); N]); | ^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `[u8; _]` is forbidden as the type of a const generic parameter --> $DIR/const-param-type-depends-on-const-param.rs:15:35 @@ -26,7 +26,7 @@ LL | pub struct SelfDependent<const N: [u8; N]>; | ^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 4 previous errors diff --git a/src/test/ui/const-generics/different_byref.min.stderr b/src/test/ui/const-generics/different_byref.min.stderr index 05720d15404..93874fb1f5f 100644 --- a/src/test/ui/const-generics/different_byref.min.stderr +++ b/src/test/ui/const-generics/different_byref.min.stderr @@ -5,7 +5,7 @@ LL | struct Const<const V: [usize; 1]> {} | ^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to previous error diff --git a/src/test/ui/const-generics/forbid-non-structural_match-types.min.stderr b/src/test/ui/const-generics/forbid-non-structural_match-types.min.stderr index 3912cf57751..80eac994d55 100644 --- a/src/test/ui/const-generics/forbid-non-structural_match-types.min.stderr +++ b/src/test/ui/const-generics/forbid-non-structural_match-types.min.stderr @@ -5,7 +5,7 @@ LL | struct B<const X: A>; // ok | ^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `C` is forbidden as the type of a const generic parameter --> $DIR/forbid-non-structural_match-types.rs:14:19 @@ -14,7 +14,7 @@ LL | struct D<const X: C>; | ^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error[E0741]: `C` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter --> $DIR/forbid-non-structural_match-types.rs:14:19 diff --git a/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr b/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr index 4c2aaef3493..8701d54f5c9 100644 --- a/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr +++ b/src/test/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr @@ -14,7 +14,7 @@ LL | trait Trait<const S: &'static str> {} | ^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 2 previous errors diff --git a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.min.stderr b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.min.stderr index b6c6e6fe374..e96b9e70352 100644 --- a/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.min.stderr +++ b/src/test/ui/const-generics/issue-66596-impl-trait-for-str-const-arg.min.stderr @@ -5,7 +5,7 @@ LL | trait Trait<const NAME: &'static str> { | ^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to previous error diff --git a/src/test/ui/const-generics/issues/issue-62579-no-match.min.stderr b/src/test/ui/const-generics/issues/issue-62579-no-match.min.stderr index a0aee4821c6..5c9387d4012 100644 --- a/src/test/ui/const-generics/issues/issue-62579-no-match.min.stderr +++ b/src/test/ui/const-generics/issues/issue-62579-no-match.min.stderr @@ -5,7 +5,7 @@ LL | fn foo<const T: NoMatch>() -> bool { | ^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to previous error diff --git a/src/test/ui/const-generics/issues/issue-62878.min.stderr b/src/test/ui/const-generics/issues/issue-62878.min.stderr index 920d7e43b9b..e4a71fe0618 100644 --- a/src/test/ui/const-generics/issues/issue-62878.min.stderr +++ b/src/test/ui/const-generics/issues/issue-62878.min.stderr @@ -11,7 +11,7 @@ LL | fn foo<const N: usize, const A: [u8; N]>() {} | ^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 2 previous errors diff --git a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr index 543e4b29a16..2fb38addb2d 100644 --- a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr +++ b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr @@ -5,7 +5,7 @@ LL | fn test<const T: &'static dyn A>() { | ^^^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error[E0741]: `&'static (dyn A + 'static)` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter --> $DIR/issue-63322-forbid-dyn.rs:9:18 diff --git a/src/test/ui/const-generics/issues/issue-68615-adt.min.stderr b/src/test/ui/const-generics/issues/issue-68615-adt.min.stderr index 2de8ada2766..4782b1d98eb 100644 --- a/src/test/ui/const-generics/issues/issue-68615-adt.min.stderr +++ b/src/test/ui/const-generics/issues/issue-68615-adt.min.stderr @@ -5,7 +5,7 @@ LL | struct Const<const V: [usize; 0]> {} | ^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to previous error diff --git a/src/test/ui/const-generics/issues/issue-68615-array.min.stderr b/src/test/ui/const-generics/issues/issue-68615-array.min.stderr index 0d17b04a5cd..d0c190b91b0 100644 --- a/src/test/ui/const-generics/issues/issue-68615-array.min.stderr +++ b/src/test/ui/const-generics/issues/issue-68615-array.min.stderr @@ -5,7 +5,7 @@ LL | struct Foo<const V: [usize; 0] > {} | ^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to previous error diff --git a/src/test/ui/const-generics/issues/issue-71169.min.stderr b/src/test/ui/const-generics/issues/issue-71169.min.stderr index 68ac47460b3..1c6e08adffd 100644 --- a/src/test/ui/const-generics/issues/issue-71169.min.stderr +++ b/src/test/ui/const-generics/issues/issue-71169.min.stderr @@ -11,7 +11,7 @@ LL | fn foo<const LEN: usize, const DATA: [u8; LEN]>() {} | ^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 2 previous errors diff --git a/src/test/ui/const-generics/issues/issue-73491.min.stderr b/src/test/ui/const-generics/issues/issue-73491.min.stderr index aeab9e26772..c8f2e0dadc1 100644 --- a/src/test/ui/const-generics/issues/issue-73491.min.stderr +++ b/src/test/ui/const-generics/issues/issue-73491.min.stderr @@ -5,7 +5,7 @@ LL | fn hoge<const IN: [u32; LEN]>() {} | ^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to previous error diff --git a/src/test/ui/const-generics/issues/issue-74101.min.stderr b/src/test/ui/const-generics/issues/issue-74101.min.stderr index 6561183f7ca..a7f0ecf0a26 100644 --- a/src/test/ui/const-generics/issues/issue-74101.min.stderr +++ b/src/test/ui/const-generics/issues/issue-74101.min.stderr @@ -5,7 +5,7 @@ LL | fn test<const N: [u8; 1 + 2]>() {} | ^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `[u8; _]` is forbidden as the type of a const generic parameter --> $DIR/issue-74101.rs:9:21 @@ -14,7 +14,7 @@ LL | struct Foo<const N: [u8; 1 + 2]>; | ^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 2 previous errors diff --git a/src/test/ui/const-generics/issues/issue-74255.min.stderr b/src/test/ui/const-generics/issues/issue-74255.min.stderr index 2b6aa7dad97..62ad43974f4 100644 --- a/src/test/ui/const-generics/issues/issue-74255.min.stderr +++ b/src/test/ui/const-generics/issues/issue-74255.min.stderr @@ -5,7 +5,7 @@ LL | fn ice_struct_fn<const I: IceEnum>() {} | ^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to previous error diff --git a/src/test/ui/const-generics/issues/issue-74950.min.stderr b/src/test/ui/const-generics/issues/issue-74950.min.stderr index 27393d38c6b..4e640ff857e 100644 --- a/src/test/ui/const-generics/issues/issue-74950.min.stderr +++ b/src/test/ui/const-generics/issues/issue-74950.min.stderr @@ -5,7 +5,7 @@ LL | struct Outer<const I: Inner>; | ^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `Inner` is forbidden as the type of a const generic parameter --> $DIR/issue-74950.rs:17:23 @@ -14,7 +14,7 @@ LL | struct Outer<const I: Inner>; | ^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `Inner` is forbidden as the type of a const generic parameter --> $DIR/issue-74950.rs:17:23 @@ -23,7 +23,7 @@ LL | struct Outer<const I: Inner>; | ^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `Inner` is forbidden as the type of a const generic parameter --> $DIR/issue-74950.rs:17:23 @@ -32,7 +32,7 @@ LL | struct Outer<const I: Inner>; | ^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `Inner` is forbidden as the type of a const generic parameter --> $DIR/issue-74950.rs:17:23 @@ -41,7 +41,7 @@ LL | struct Outer<const I: Inner>; | ^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 5 previous errors diff --git a/src/test/ui/const-generics/issues/issue-75047.min.stderr b/src/test/ui/const-generics/issues/issue-75047.min.stderr index 4ab90dd1ec6..3c1c3ea97b5 100644 --- a/src/test/ui/const-generics/issues/issue-75047.min.stderr +++ b/src/test/ui/const-generics/issues/issue-75047.min.stderr @@ -5,7 +5,7 @@ LL | struct Foo<const N: [u8; Bar::<u32>::value()]>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to previous error diff --git a/src/test/ui/const-generics/min_const_generics/complex-types.stderr b/src/test/ui/const-generics/min_const_generics/complex-types.stderr index 20d498f9c93..a658a7b3956 100644 --- a/src/test/ui/const-generics/min_const_generics/complex-types.stderr +++ b/src/test/ui/const-generics/min_const_generics/complex-types.stderr @@ -5,7 +5,7 @@ LL | struct Foo<const N: [u8; 0]>; | ^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `()` is forbidden as the type of a const generic parameter --> $DIR/complex-types.rs:6:21 @@ -14,7 +14,7 @@ LL | struct Bar<const N: ()>; | ^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `No` is forbidden as the type of a const generic parameter --> $DIR/complex-types.rs:11:21 @@ -23,7 +23,7 @@ LL | struct Fez<const N: No>; | ^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `&'static u8` is forbidden as the type of a const generic parameter --> $DIR/complex-types.rs:14:21 @@ -32,7 +32,7 @@ LL | struct Faz<const N: &'static u8>; | ^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `!` is forbidden as the type of a const generic parameter --> $DIR/complex-types.rs:17:21 @@ -41,7 +41,7 @@ LL | struct Fiz<const N: !>; | ^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `()` is forbidden as the type of a const generic parameter --> $DIR/complex-types.rs:20:19 @@ -50,7 +50,7 @@ LL | enum Goo<const N: ()> { A, B } | ^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `()` is forbidden as the type of a const generic parameter --> $DIR/complex-types.rs:23:20 @@ -59,7 +59,7 @@ LL | union Boo<const N: ()> { a: () } | ^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 7 previous errors diff --git a/src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.stderr b/src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.stderr index 05939b05bba..647ef5400cb 100644 --- a/src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.stderr +++ b/src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.stderr @@ -5,7 +5,7 @@ LL | fn a<const X: &'static [u32]>() {} | ^^^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to previous error diff --git a/src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.stderr b/src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.stderr index 8724c7e33b1..d612e0c35a1 100644 --- a/src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.stderr +++ b/src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.stderr @@ -5,7 +5,7 @@ LL | struct Const<const P: &'static ()>; | ^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to previous error diff --git a/src/test/ui/const-generics/nested-type.min.stderr b/src/test/ui/const-generics/nested-type.min.stderr index dabb3f245f5..6defd393ba0 100644 --- a/src/test/ui/const-generics/nested-type.min.stderr +++ b/src/test/ui/const-generics/nested-type.min.stderr @@ -12,7 +12,7 @@ LL | | }]>; | |__^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants --> $DIR/nested-type.rs:15:5 diff --git a/src/test/ui/const-generics/slice-const-param-mismatch.min.stderr b/src/test/ui/const-generics/slice-const-param-mismatch.min.stderr index 13d0b217ed2..166a35ee455 100644 --- a/src/test/ui/const-generics/slice-const-param-mismatch.min.stderr +++ b/src/test/ui/const-generics/slice-const-param-mismatch.min.stderr @@ -5,7 +5,7 @@ LL | struct ConstString<const T: &'static str>; | ^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `&'static [u8]` is forbidden as the type of a const generic parameter --> $DIR/slice-const-param-mismatch.rs:9:28 @@ -14,7 +14,7 @@ LL | struct ConstBytes<const T: &'static [u8]>; | ^^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 2 previous errors diff --git a/src/test/ui/const-generics/slice-const-param.min.stderr b/src/test/ui/const-generics/slice-const-param.min.stderr index 821c6e3995a..ed39a0c56b4 100644 --- a/src/test/ui/const-generics/slice-const-param.min.stderr +++ b/src/test/ui/const-generics/slice-const-param.min.stderr @@ -5,7 +5,7 @@ LL | pub fn function_with_str<const STRING: &'static str>() -> &'static str { | ^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `&'static [u8]` is forbidden as the type of a const generic parameter --> $DIR/slice-const-param.rs:12:41 @@ -14,7 +14,7 @@ LL | pub fn function_with_bytes<const BYTES: &'static [u8]>() -> &'static [u8] { | ^^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 2 previous errors diff --git a/src/test/ui/const-generics/std/const-generics-range.min.stderr b/src/test/ui/const-generics/std/const-generics-range.min.stderr index d7d2a8447e9..86e6159fdb5 100644 --- a/src/test/ui/const-generics/std/const-generics-range.min.stderr +++ b/src/test/ui/const-generics/std/const-generics-range.min.stderr @@ -5,7 +5,7 @@ LL | struct _Range<const R: std::ops::Range<usize>>; | ^^^^^^^^^^^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `RangeFrom<usize>` is forbidden as the type of a const generic parameter --> $DIR/const-generics-range.rs:12:28 @@ -14,7 +14,7 @@ LL | struct _RangeFrom<const R: std::ops::RangeFrom<usize>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `RangeFull` is forbidden as the type of a const generic parameter --> $DIR/const-generics-range.rs:17:28 @@ -23,7 +23,7 @@ LL | struct _RangeFull<const R: std::ops::RangeFull>; | ^^^^^^^^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `RangeInclusive<usize>` is forbidden as the type of a const generic parameter --> $DIR/const-generics-range.rs:23:33 @@ -32,7 +32,7 @@ LL | struct _RangeInclusive<const R: std::ops::RangeInclusive<usize>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `RangeTo<usize>` is forbidden as the type of a const generic parameter --> $DIR/const-generics-range.rs:28:26 @@ -41,7 +41,7 @@ LL | struct _RangeTo<const R: std::ops::RangeTo<usize>>; | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `RangeToInclusive<usize>` is forbidden as the type of a const generic parameter --> $DIR/const-generics-range.rs:33:35 @@ -50,7 +50,7 @@ LL | struct _RangeToInclusive<const R: std::ops::RangeToInclusive<usize>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 6 previous errors diff --git a/src/test/ui/const-generics/suggest_const_for_array.rs b/src/test/ui/const-generics/suggest_const_for_array.rs new file mode 100644 index 00000000000..f3e5a3186cd --- /dev/null +++ b/src/test/ui/const-generics/suggest_const_for_array.rs @@ -0,0 +1,10 @@ +#![crate_type = "lib"] + +fn example<const N: usize>() {} + +fn other() { + example::<[usize; 3]>(); + //~^ ERROR type provided when a const + example::<[usize; 4+5]>(); + //~^ ERROR type provided when a const +} diff --git a/src/test/ui/const-generics/suggest_const_for_array.stderr b/src/test/ui/const-generics/suggest_const_for_array.stderr new file mode 100644 index 00000000000..a617bf2bb0d --- /dev/null +++ b/src/test/ui/const-generics/suggest_const_for_array.stderr @@ -0,0 +1,15 @@ +error[E0747]: type provided when a constant was expected + --> $DIR/suggest_const_for_array.rs:6:13 + | +LL | example::<[usize; 3]>(); + | ^^^^^^^^^^ help: array type provided where a `usize` was expected, try: `{ 3 }` + +error[E0747]: type provided when a constant was expected + --> $DIR/suggest_const_for_array.rs:8:13 + | +LL | example::<[usize; 4+5]>(); + | ^^^^^^^^^^^^ help: array type provided where a `usize` was expected, try: `{ 4+5 }` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0747`. diff --git a/src/test/ui/const-generics/type-dependent/issue-71348.min.stderr b/src/test/ui/const-generics/type-dependent/issue-71348.min.stderr index 92f5d815a0f..f3516d1de96 100644 --- a/src/test/ui/const-generics/type-dependent/issue-71348.min.stderr +++ b/src/test/ui/const-generics/type-dependent/issue-71348.min.stderr @@ -5,7 +5,7 @@ LL | trait Get<'a, const N: &'static str> { | ^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: `&'static str` is forbidden as the type of a const generic parameter --> $DIR/issue-71348.rs:18:25 @@ -14,7 +14,7 @@ LL | fn ask<'a, const N: &'static str>(&'a self) -> &'a <Self as Get<N>>::Ta | ^^^^^^^^^^^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/const-address-of-interior-mut.stderr b/src/test/ui/consts/const-address-of-interior-mut.stderr index f15174c33b3..93120753b1a 100644 --- a/src/test/ui/consts/const-address-of-interior-mut.stderr +++ b/src/test/ui/consts/const-address-of-interior-mut.stderr @@ -1,27 +1,39 @@ -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability --> $DIR/const-address-of-interior-mut.rs:5:39 | LL | const A: () = { let x = Cell::new(2); &raw const x; }; | ^^^^^^^^^^^^ + | + = note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information + = help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability --> $DIR/const-address-of-interior-mut.rs:7:40 | LL | static B: () = { let x = Cell::new(2); &raw const x; }; | ^^^^^^^^^^^^ + | + = note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information + = help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability --> $DIR/const-address-of-interior-mut.rs:9:44 | LL | static mut C: () = { let x = Cell::new(2); &raw const x; }; | ^^^^^^^^^^^^ + | + = note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information + = help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability --> $DIR/const-address-of-interior-mut.rs:13:13 | LL | let y = &raw const x; | ^^^^^^^^^^^^ + | + = note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information + = help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0492`. +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/consts/const-multi-ref.rs b/src/test/ui/consts/const-multi-ref.rs index 18645efc887..7e0f1a812fd 100644 --- a/src/test/ui/consts/const-multi-ref.rs +++ b/src/test/ui/consts/const-multi-ref.rs @@ -13,7 +13,7 @@ const _: i32 = { const _: std::cell::Cell<i32> = { let mut a = std::cell::Cell::new(5); - let p = &a; //~ ERROR cannot borrow a constant which may contain interior mutability + let p = &a; //~ ERROR borrowed element may contain interior mutability let reborrow = {p}; let pp = &reborrow; diff --git a/src/test/ui/consts/const-multi-ref.stderr b/src/test/ui/consts/const-multi-ref.stderr index 9a7914b4588..c0a320d46cb 100644 --- a/src/test/ui/consts/const-multi-ref.stderr +++ b/src/test/ui/consts/const-multi-ref.stderr @@ -4,13 +4,16 @@ error[E0764]: mutable references are not allowed in constants LL | let p = &mut a; | ^^^^^^ `&mut` is only allowed in `const fn` -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability --> $DIR/const-multi-ref.rs:16:13 | LL | let p = &a; | ^^ + | + = note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information + = help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable error: aborting due to 2 previous errors -Some errors have detailed explanations: E0492, E0764. -For more information about an error, try `rustc --explain E0492`. +Some errors have detailed explanations: E0658, E0764. +For more information about an error, try `rustc --explain E0658`. diff --git a/src/test/ui/consts/partial_qualif.rs b/src/test/ui/consts/partial_qualif.rs index 32c68e69f4b..7c28b8b8a62 100644 --- a/src/test/ui/consts/partial_qualif.rs +++ b/src/test/ui/consts/partial_qualif.rs @@ -3,7 +3,7 @@ use std::cell::Cell; const FOO: &(Cell<usize>, bool) = { let mut a = (Cell::new(0), false); a.1 = true; // sets `qualif(a)` to `qualif(a) | qualif(true)` - &{a} //~ ERROR cannot borrow a constant which may contain interior mutability + &{a} //~ ERROR cannot refer to interior mutable }; fn main() {} diff --git a/src/test/ui/consts/partial_qualif.stderr b/src/test/ui/consts/partial_qualif.stderr index 221e449b6f9..32c25be2173 100644 --- a/src/test/ui/consts/partial_qualif.stderr +++ b/src/test/ui/consts/partial_qualif.stderr @@ -1,8 +1,8 @@ -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0492]: constants cannot refer to interior mutable data --> $DIR/partial_qualif.rs:6:5 | LL | &{a} - | ^^^^ + | ^^^^ this borrow of an interior mutable value may end up in the final value error: aborting due to previous error diff --git a/src/test/ui/consts/promotion.rs b/src/test/ui/consts/promotion.rs index e6f5c3d27ca..b6e7127a9b7 100644 --- a/src/test/ui/consts/promotion.rs +++ b/src/test/ui/consts/promotion.rs @@ -4,12 +4,23 @@ fn foo(_: &'static [&'static str]) {} fn bar(_: &'static [&'static str; 3]) {} -fn baz_i32(_: &'static i32) {} -fn baz_u32(_: &'static u32) {} +const fn baz_i32(_: &'static i32) {} +const fn baz_u32(_: &'static u32) {} + +const fn fail() -> i32 { 1/0 } +const C: i32 = { + // Promoted that fails to evaluate in dead code -- this must work + // (for backwards compatibility reasons). + if false { + baz_i32(&fail()); + } + 42 +}; fn main() { foo(&["a", "b", "c"]); bar(&["d", "e", "f"]); + assert_eq!(C, 42); // make sure that these do not cause trouble despite overflowing baz_u32(&(0-1)); diff --git a/src/test/ui/consts/qualif_overwrite.rs b/src/test/ui/consts/qualif_overwrite.rs index 430eea37de7..aae4e41ffd7 100644 --- a/src/test/ui/consts/qualif_overwrite.rs +++ b/src/test/ui/consts/qualif_overwrite.rs @@ -7,7 +7,7 @@ use std::cell::Cell; const FOO: &Option<Cell<usize>> = { let mut a = Some(Cell::new(0)); a = None; // sets `qualif(a)` to `qualif(a) | qualif(None)` - &{a} //~ ERROR cannot borrow a constant which may contain interior mutability + &{a} //~ ERROR cannot refer to interior mutable }; fn main() {} diff --git a/src/test/ui/consts/qualif_overwrite.stderr b/src/test/ui/consts/qualif_overwrite.stderr index fbaae711d7c..86a669c433d 100644 --- a/src/test/ui/consts/qualif_overwrite.stderr +++ b/src/test/ui/consts/qualif_overwrite.stderr @@ -1,8 +1,8 @@ -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0492]: constants cannot refer to interior mutable data --> $DIR/qualif_overwrite.rs:10:5 | LL | &{a} - | ^^^^ + | ^^^^ this borrow of an interior mutable value may end up in the final value error: aborting due to previous error diff --git a/src/test/ui/consts/qualif_overwrite_2.rs b/src/test/ui/consts/qualif_overwrite_2.rs index fa79b5c14a7..1819d9a6d20 100644 --- a/src/test/ui/consts/qualif_overwrite_2.rs +++ b/src/test/ui/consts/qualif_overwrite_2.rs @@ -5,7 +5,7 @@ use std::cell::Cell; const FOO: &Option<Cell<usize>> = { let mut a = (Some(Cell::new(0)),); a.0 = None; // sets `qualif(a)` to `qualif(a) | qualif(None)` - &{a.0} //~ ERROR cannot borrow a constant which may contain interior mutability + &{a.0} //~ ERROR cannot refer to interior mutable }; fn main() {} diff --git a/src/test/ui/consts/qualif_overwrite_2.stderr b/src/test/ui/consts/qualif_overwrite_2.stderr index a393c4e336d..9eb123d0b01 100644 --- a/src/test/ui/consts/qualif_overwrite_2.stderr +++ b/src/test/ui/consts/qualif_overwrite_2.stderr @@ -1,8 +1,8 @@ -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0492]: constants cannot refer to interior mutable data --> $DIR/qualif_overwrite_2.rs:8:5 | LL | &{a.0} - | ^^^^^^ + | ^^^^^^ this borrow of an interior mutable value may end up in the final value error: aborting due to previous error diff --git a/src/test/ui/consts/std/cell.rs b/src/test/ui/consts/std/cell.rs index cf6c0f2379d..f1ef541319a 100644 --- a/src/test/ui/consts/std/cell.rs +++ b/src/test/ui/consts/std/cell.rs @@ -1,18 +1,31 @@ +#![feature(const_refs_to_cell)] + use std::cell::*; -// not ok, because this would create a silent constant with interior mutability. -// the rules could be relaxed in the future +// not ok, because this creates a dangling pointer, just like `let x = Cell::new(42).as_ptr()` would static FOO: Wrap<*mut u32> = Wrap(Cell::new(42).as_ptr()); -//~^ ERROR cannot borrow a constant which may contain interior mutability +//~^ ERROR encountered dangling pointer +const FOO_CONST: Wrap<*mut u32> = Wrap(Cell::new(42).as_ptr()); +//~^ ERROR encountered dangling pointer +// Ok, these are just base values and it is the `Wrap` author's job to uphold `Send` and `Sync` +// invariants, since they used `unsafe impl`. static FOO3: Wrap<Cell<u32>> = Wrap(Cell::new(42)); -// ok +const FOO3_CONST: Wrap<Cell<u32>> = Wrap(Cell::new(42)); + +// ok, we are referring to the memory of another static item. static FOO4: Wrap<*mut u32> = Wrap(FOO3.0.as_ptr()); -// not ok, because the `as_ptr` call takes a reference to a type with interior mutability -// which is not allowed in constants +// not ok, the use of a constant here is equivalent to an inline declaration of the value, so +// its memory will get freed before the constant is finished evaluating, thus creating a dangling +// pointer. This would happen exactly the same at runtime. +const FOO4_CONST: Wrap<*mut u32> = Wrap(FOO3_CONST.0.as_ptr()); +//~^ ERROR encountered dangling pointer + +// not ok, because the `as_ptr` call takes a reference to a temporary that will get freed +// before the constant is finished evaluating. const FOO2: *mut u32 = Cell::new(42).as_ptr(); -//~^ ERROR cannot borrow a constant which may contain interior mutability +//~^ ERROR encountered dangling pointer struct IMSafeTrustMe(UnsafeCell<u32>); unsafe impl Send for IMSafeTrustMe {} @@ -21,10 +34,13 @@ unsafe impl Sync for IMSafeTrustMe {} static BAR: IMSafeTrustMe = IMSafeTrustMe(UnsafeCell::new(5)); + struct Wrap<T>(T); unsafe impl<T> Send for Wrap<T> {} unsafe impl<T> Sync for Wrap<T> {} static BAR_PTR: Wrap<*mut u32> = Wrap(BAR.0.get()); +const fn fst_ref<T, U>(x: &(T, U)) -> &T { &x.0 } + fn main() {} diff --git a/src/test/ui/consts/std/cell.stderr b/src/test/ui/consts/std/cell.stderr index f75aadff6d5..355c326f0b6 100644 --- a/src/test/ui/consts/std/cell.stderr +++ b/src/test/ui/consts/std/cell.stderr @@ -1,15 +1,26 @@ -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead - --> $DIR/cell.rs:5:35 +error: encountered dangling pointer in final constant + --> $DIR/cell.rs:6:1 | LL | static FOO: Wrap<*mut u32> = Wrap(Cell::new(42).as_ptr()); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead - --> $DIR/cell.rs:14:24 +error: encountered dangling pointer in final constant + --> $DIR/cell.rs:8:1 + | +LL | const FOO_CONST: Wrap<*mut u32> = Wrap(Cell::new(42).as_ptr()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered dangling pointer in final constant + --> $DIR/cell.rs:22:1 + | +LL | const FOO4_CONST: Wrap<*mut u32> = Wrap(FOO3_CONST.0.as_ptr()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: encountered dangling pointer in final constant + --> $DIR/cell.rs:27:1 | LL | const FOO2: *mut u32 = Cell::new(42).as_ptr(); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0492`. diff --git a/src/test/ui/doc-alias-crate-level.rs b/src/test/ui/doc-alias-crate-level.rs index 9b596ece5b5..c7783aae5ea 100644 --- a/src/test/ui/doc-alias-crate-level.rs +++ b/src/test/ui/doc-alias-crate-level.rs @@ -1,7 +1,5 @@ // compile-flags: -Zdeduplicate-diagnostics=no -#![feature(doc_alias)] - #![crate_type = "lib"] #![doc(alias = "not working!")] //~ ERROR diff --git a/src/test/ui/doc-alias-crate-level.stderr b/src/test/ui/doc-alias-crate-level.stderr index b6437fad5d0..c0467514ae1 100644 --- a/src/test/ui/doc-alias-crate-level.stderr +++ b/src/test/ui/doc-alias-crate-level.stderr @@ -1,11 +1,11 @@ error: '\'' character isn't allowed in `#[doc(alias = "...")]` - --> $DIR/doc-alias-crate-level.rs:9:15 + --> $DIR/doc-alias-crate-level.rs:7:15 | LL | #[doc(alias = "shouldn't work!")] | ^^^^^^^^^^^^^^^^^ error: `#![doc(alias = "...")]` isn't allowed as a crate level attribute - --> $DIR/doc-alias-crate-level.rs:7:8 + --> $DIR/doc-alias-crate-level.rs:5:8 | LL | #![doc(alias = "not working!")] | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/doc-alias-same-name.rs b/src/test/ui/doc-alias-same-name.rs new file mode 100644 index 00000000000..da97c267618 --- /dev/null +++ b/src/test/ui/doc-alias-same-name.rs @@ -0,0 +1,4 @@ +#![crate_type = "lib"] + +#[doc(alias = "Foo")] //~ ERROR +pub struct Foo; diff --git a/src/test/ui/doc-alias-same-name.stderr b/src/test/ui/doc-alias-same-name.stderr new file mode 100644 index 00000000000..5ba09a2eae1 --- /dev/null +++ b/src/test/ui/doc-alias-same-name.stderr @@ -0,0 +1,8 @@ +error: `#[doc(alias = "...")]` is the same as the item's name + --> $DIR/doc-alias-same-name.rs:3:7 + | +LL | #[doc(alias = "Foo")] + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/error-codes/E0435.fixed b/src/test/ui/error-codes/E0435.fixed new file mode 100644 index 00000000000..fdf896d2dbb --- /dev/null +++ b/src/test/ui/error-codes/E0435.fixed @@ -0,0 +1,6 @@ +// run-rustfix +fn main () { + #[allow(non_upper_case_globals)] + const foo: usize = 42; + let _: [u8; foo]; //~ ERROR E0435 +} diff --git a/src/test/ui/error-codes/E0435.rs b/src/test/ui/error-codes/E0435.rs index 620dd30a23b..d9354efb8fd 100644 --- a/src/test/ui/error-codes/E0435.rs +++ b/src/test/ui/error-codes/E0435.rs @@ -1,4 +1,6 @@ +// run-rustfix fn main () { - let foo = 42u32; + #[allow(non_upper_case_globals)] + let foo: usize = 42; let _: [u8; foo]; //~ ERROR E0435 } diff --git a/src/test/ui/error-codes/E0435.stderr b/src/test/ui/error-codes/E0435.stderr index 349aa0d07c5..fc08fade91c 100644 --- a/src/test/ui/error-codes/E0435.stderr +++ b/src/test/ui/error-codes/E0435.stderr @@ -1,6 +1,8 @@ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/E0435.rs:3:17 + --> $DIR/E0435.rs:5:17 | +LL | let foo: usize = 42; + | ------- help: consider using `const` instead of `let`: `const foo` LL | let _: [u8; foo]; | ^^^ non-constant value diff --git a/src/test/ui/error-codes/E0492.rs b/src/test/ui/error-codes/E0492.rs index 2de4c12eb64..2c735fcc9f9 100644 --- a/src/test/ui/error-codes/E0492.rs +++ b/src/test/ui/error-codes/E0492.rs @@ -1,7 +1,10 @@ use std::sync::atomic::AtomicUsize; const A: AtomicUsize = AtomicUsize::new(0); -static B: &'static AtomicUsize = &A; //~ ERROR E0492 +const B: &'static AtomicUsize = &A; //~ ERROR E0492 +static C: &'static AtomicUsize = &A; //~ ERROR E0492 + +const NONE: &'static Option<AtomicUsize> = &None; fn main() { } diff --git a/src/test/ui/error-codes/E0492.stderr b/src/test/ui/error-codes/E0492.stderr index 5f337dd7f42..557c977e87d 100644 --- a/src/test/ui/error-codes/E0492.stderr +++ b/src/test/ui/error-codes/E0492.stderr @@ -1,9 +1,17 @@ -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead - --> $DIR/E0492.rs:4:34 +error[E0492]: constants cannot refer to interior mutable data + --> $DIR/E0492.rs:4:33 | -LL | static B: &'static AtomicUsize = &A; - | ^^ +LL | const B: &'static AtomicUsize = &A; + | ^^ this borrow of an interior mutable value may end up in the final value -error: aborting due to previous error +error[E0492]: statics cannot refer to interior mutable data + --> $DIR/E0492.rs:5:34 + | +LL | static C: &'static AtomicUsize = &A; + | ^^ this borrow of an interior mutable value may end up in the final value + | + = help: to fix this, the value can be extracted to a separate `static` item and then referenced + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0492`. diff --git a/src/test/ui/feature-gate/feature-gate-const_refs_to_cell.rs b/src/test/ui/feature-gate/feature-gate-const_refs_to_cell.rs new file mode 100644 index 00000000000..63159ed0553 --- /dev/null +++ b/src/test/ui/feature-gate/feature-gate-const_refs_to_cell.rs @@ -0,0 +1,12 @@ +// check-pass + +#![feature(const_refs_to_cell)] + +const FOO: () = { + let x = std::cell::Cell::new(42); + let y = &x; +}; + +fn main() { + FOO; +} diff --git a/src/test/ui/feature-gates/feature-gate-const_generics.stderr b/src/test/ui/feature-gates/feature-gate-const_generics.stderr index b2b7e4576bf..ed19109b38b 100644 --- a/src/test/ui/feature-gates/feature-gate-const_generics.stderr +++ b/src/test/ui/feature-gates/feature-gate-const_generics.stderr @@ -5,7 +5,7 @@ LL | fn foo<const X: ()>() {} | ^^ | = note: the only supported types are integers, `bool` and `char` - = help: more complex types are supported with `#[feature(const_generics)]` + = help: more complex types are supported with `#![feature(const_generics)]` error: aborting due to previous error diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index ef9c9ef48a8..128795f50f9 100644 --- a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -173,12 +173,6 @@ LL | #[deny(x5100)] impl S { } | ^^^^^ warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:438:1 - | -LL | #[macro_escape] - | ^^^^^^^^^^^^^^^ - -warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` --> $DIR/issue-43106-gating-of-builtin-attrs.rs:441:17 | LL | mod inner { #![macro_escape] } @@ -186,6 +180,12 @@ LL | mod inner { #![macro_escape] } | = help: try an outer attribute: `#[macro_use]` +warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:438:1 + | +LL | #[macro_escape] + | ^^^^^^^^^^^^^^^ + warning: use of deprecated attribute `plugin_registrar`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675 --> $DIR/issue-43106-gating-of-builtin-attrs.rs:228:17 | diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-macro_use.stderr b/src/test/ui/feature-gates/issue-43106-gating-of-macro_use.stderr index 52a682e4bfa..7f6d608038f 100644 --- a/src/test/ui/feature-gates/issue-43106-gating-of-macro_use.stderr +++ b/src/test/ui/feature-gates/issue-43106-gating-of-macro_use.stderr @@ -1,8 +1,8 @@ error: arguments to `macro_use` are not allowed here - --> $DIR/issue-43106-gating-of-macro_use.rs:6:1 + --> $DIR/issue-43106-gating-of-macro_use.rs:12:17 | -LL | #![macro_use(my_macro)] - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | mod inner { #![macro_use(my_macro)] } + | ^^^^^^^^^^^^^^^^^^^^^^^ error: arguments to `macro_use` are not allowed here --> $DIR/issue-43106-gating-of-macro_use.rs:9:1 @@ -11,10 +11,10 @@ LL | #[macro_use(my_macro)] | ^^^^^^^^^^^^^^^^^^^^^^ error: arguments to `macro_use` are not allowed here - --> $DIR/issue-43106-gating-of-macro_use.rs:12:17 + --> $DIR/issue-43106-gating-of-macro_use.rs:6:1 | -LL | mod inner { #![macro_use(my_macro)] } - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![macro_use(my_macro)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: malformed `macro_use` attribute input --> $DIR/issue-43106-gating-of-macro_use.rs:15:5 diff --git a/src/test/ui/impl-trait/bindings.stderr b/src/test/ui/impl-trait/bindings.stderr index e983fdecdba..4da49f4dc7d 100644 --- a/src/test/ui/impl-trait/bindings.stderr +++ b/src/test/ui/impl-trait/bindings.stderr @@ -2,25 +2,33 @@ error[E0435]: attempt to use a non-constant value in a constant --> $DIR/bindings.rs:5:29 | LL | const foo: impl Clone = x; - | ^ non-constant value + | --------- ^ non-constant value + | | + | help: consider using `let` instead of `const`: `let foo` error[E0435]: attempt to use a non-constant value in a constant --> $DIR/bindings.rs:11:33 | LL | const foo: impl Clone = x; - | ^ non-constant value + | --------- ^ non-constant value + | | + | help: consider using `let` instead of `const`: `let foo` error[E0435]: attempt to use a non-constant value in a constant --> $DIR/bindings.rs:18:33 | LL | const foo: impl Clone = x; - | ^ non-constant value + | --------- ^ non-constant value + | | + | help: consider using `let` instead of `const`: `let foo` error[E0435]: attempt to use a non-constant value in a constant --> $DIR/bindings.rs:25:33 | LL | const foo: impl Clone = x; - | ^ non-constant value + | --------- ^ non-constant value + | | + | help: consider using `let` instead of `const`: `let foo` warning: the feature `impl_trait_in_bindings` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/bindings.rs:1:12 diff --git a/src/test/ui/issues/issue-17718-const-borrow.rs b/src/test/ui/issues/issue-17718-const-borrow.rs index 8a31bd0c66a..89316dbd5c4 100644 --- a/src/test/ui/issues/issue-17718-const-borrow.rs +++ b/src/test/ui/issues/issue-17718-const-borrow.rs @@ -2,13 +2,13 @@ use std::cell::UnsafeCell; const A: UnsafeCell<usize> = UnsafeCell::new(1); const B: &'static UnsafeCell<usize> = &A; -//~^ ERROR: cannot borrow a constant which may contain interior mutability +//~^ ERROR: cannot refer to interior mutable struct C { a: UnsafeCell<usize> } const D: C = C { a: UnsafeCell::new(1) }; const E: &'static UnsafeCell<usize> = &D.a; -//~^ ERROR: cannot borrow a constant which may contain interior mutability +//~^ ERROR: cannot refer to interior mutable const F: &'static C = &D; -//~^ ERROR: cannot borrow a constant which may contain interior mutability +//~^ ERROR: cannot refer to interior mutable fn main() {} diff --git a/src/test/ui/issues/issue-17718-const-borrow.stderr b/src/test/ui/issues/issue-17718-const-borrow.stderr index b4330049689..e3ff6c923ad 100644 --- a/src/test/ui/issues/issue-17718-const-borrow.stderr +++ b/src/test/ui/issues/issue-17718-const-borrow.stderr @@ -1,20 +1,20 @@ -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0492]: constants cannot refer to interior mutable data --> $DIR/issue-17718-const-borrow.rs:4:39 | LL | const B: &'static UnsafeCell<usize> = &A; - | ^^ + | ^^ this borrow of an interior mutable value may end up in the final value -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0492]: constants cannot refer to interior mutable data --> $DIR/issue-17718-const-borrow.rs:9:39 | LL | const E: &'static UnsafeCell<usize> = &D.a; - | ^^^^ + | ^^^^ this borrow of an interior mutable value may end up in the final value -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0492]: constants cannot refer to interior mutable data --> $DIR/issue-17718-const-borrow.rs:11:23 | LL | const F: &'static C = &D; - | ^^ + | ^^ this borrow of an interior mutable value may end up in the final value error: aborting due to 3 previous errors diff --git a/src/test/ui/issues/issue-27433.fixed b/src/test/ui/issues/issue-27433.fixed new file mode 100644 index 00000000000..ce31f6bea4b --- /dev/null +++ b/src/test/ui/issues/issue-27433.fixed @@ -0,0 +1,7 @@ +// run-rustfix +fn main() { + let foo = 42u32; + #[allow(unused_variables, non_snake_case)] + let FOO : u32 = foo; + //~^ ERROR attempt to use a non-constant value in a constant +} diff --git a/src/test/ui/issues/issue-27433.rs b/src/test/ui/issues/issue-27433.rs index 156ae68efe2..01411a51c13 100644 --- a/src/test/ui/issues/issue-27433.rs +++ b/src/test/ui/issues/issue-27433.rs @@ -1,5 +1,7 @@ +// run-rustfix fn main() { let foo = 42u32; + #[allow(unused_variables, non_snake_case)] const FOO : u32 = foo; //~^ ERROR attempt to use a non-constant value in a constant } diff --git a/src/test/ui/issues/issue-27433.stderr b/src/test/ui/issues/issue-27433.stderr index e232d17e6d7..da751a64957 100644 --- a/src/test/ui/issues/issue-27433.stderr +++ b/src/test/ui/issues/issue-27433.stderr @@ -1,8 +1,10 @@ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/issue-27433.rs:3:23 + --> $DIR/issue-27433.rs:5:23 | LL | const FOO : u32 = foo; - | ^^^ non-constant value + | --------- ^^^ non-constant value + | | + | help: consider using `let` instead of `const`: `let FOO` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-34721.stderr b/src/test/ui/issues/issue-34721.stderr index b4cc1a0aa7e..b14faff8f7b 100644 --- a/src/test/ui/issues/issue-34721.stderr +++ b/src/test/ui/issues/issue-34721.stderr @@ -13,7 +13,7 @@ LL | }; LL | x.zero() | ^ value used here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `x` +note: this function takes ownership of the receiver `self`, which moves `x` --> $DIR/issue-34721.rs:4:13 | LL | fn zero(self) -> Self; diff --git a/src/test/ui/issues/issue-3521-2.fixed b/src/test/ui/issues/issue-3521-2.fixed new file mode 100644 index 00000000000..140c24b9395 --- /dev/null +++ b/src/test/ui/issues/issue-3521-2.fixed @@ -0,0 +1,9 @@ +// run-rustfix +fn main() { + let foo = 100; + + let y: isize = foo + 1; + //~^ ERROR attempt to use a non-constant value in a constant + + println!("{}", y); +} diff --git a/src/test/ui/issues/issue-3521-2.rs b/src/test/ui/issues/issue-3521-2.rs index 871394f9eae..f66efec45e5 100644 --- a/src/test/ui/issues/issue-3521-2.rs +++ b/src/test/ui/issues/issue-3521-2.rs @@ -1,3 +1,4 @@ +// run-rustfix fn main() { let foo = 100; diff --git a/src/test/ui/issues/issue-3521-2.stderr b/src/test/ui/issues/issue-3521-2.stderr index d54bbbcdc33..84c7a9efa35 100644 --- a/src/test/ui/issues/issue-3521-2.stderr +++ b/src/test/ui/issues/issue-3521-2.stderr @@ -1,8 +1,10 @@ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/issue-3521-2.rs:4:23 + --> $DIR/issue-3521-2.rs:5:23 | LL | static y: isize = foo + 1; - | ^^^ non-constant value + | -------- ^^^ non-constant value + | | + | help: consider using `let` instead of `static`: `let y` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-3521.fixed b/src/test/ui/issues/issue-3521.fixed new file mode 100644 index 00000000000..f76106dfff1 --- /dev/null +++ b/src/test/ui/issues/issue-3521.fixed @@ -0,0 +1,13 @@ +// run-rustfix +fn main() { + #[allow(non_upper_case_globals)] + const foo: isize = 100; + + #[derive(Debug)] + enum Stuff { + Bar = foo + //~^ ERROR attempt to use a non-constant value in a constant + } + + println!("{:?}", Stuff::Bar); +} diff --git a/src/test/ui/issues/issue-3521.rs b/src/test/ui/issues/issue-3521.rs index 9e72dd29a40..c425a22df91 100644 --- a/src/test/ui/issues/issue-3521.rs +++ b/src/test/ui/issues/issue-3521.rs @@ -1,5 +1,7 @@ +// run-rustfix fn main() { - let foo = 100; + #[allow(non_upper_case_globals)] + let foo: isize = 100; #[derive(Debug)] enum Stuff { diff --git a/src/test/ui/issues/issue-3521.stderr b/src/test/ui/issues/issue-3521.stderr index ae199875269..aa42772f12d 100644 --- a/src/test/ui/issues/issue-3521.stderr +++ b/src/test/ui/issues/issue-3521.stderr @@ -1,6 +1,9 @@ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/issue-3521.rs:6:15 + --> $DIR/issue-3521.rs:8:15 | +LL | let foo: isize = 100; + | ------- help: consider using `const` instead of `let`: `const foo` +... LL | Bar = foo | ^^^ non-constant value diff --git a/src/test/ui/issues/issue-3668-2.fixed b/src/test/ui/issues/issue-3668-2.fixed new file mode 100644 index 00000000000..a95781c6edc --- /dev/null +++ b/src/test/ui/issues/issue-3668-2.fixed @@ -0,0 +1,8 @@ +// run-rustfix +#![allow(unused_variables, dead_code)] +fn f(x:isize) { + let child: isize = x + 1; + //~^ ERROR attempt to use a non-constant value in a constant +} + +fn main() {} diff --git a/src/test/ui/issues/issue-3668-2.rs b/src/test/ui/issues/issue-3668-2.rs index 525f6f5684e..8aa0897ecb4 100644 --- a/src/test/ui/issues/issue-3668-2.rs +++ b/src/test/ui/issues/issue-3668-2.rs @@ -1,3 +1,5 @@ +// run-rustfix +#![allow(unused_variables, dead_code)] fn f(x:isize) { static child: isize = x + 1; //~^ ERROR attempt to use a non-constant value in a constant diff --git a/src/test/ui/issues/issue-3668-2.stderr b/src/test/ui/issues/issue-3668-2.stderr index d6a6e837960..ba965104154 100644 --- a/src/test/ui/issues/issue-3668-2.stderr +++ b/src/test/ui/issues/issue-3668-2.stderr @@ -1,8 +1,10 @@ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/issue-3668-2.rs:2:27 + --> $DIR/issue-3668-2.rs:4:27 | LL | static child: isize = x + 1; - | ^ non-constant value + | ------------ ^ non-constant value + | | + | help: consider using `let` instead of `static`: `let child` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-3668.stderr b/src/test/ui/issues/issue-3668.stderr index 98cd3631a53..edc49979c10 100644 --- a/src/test/ui/issues/issue-3668.stderr +++ b/src/test/ui/issues/issue-3668.stderr @@ -2,7 +2,9 @@ error[E0435]: attempt to use a non-constant value in a constant --> $DIR/issue-3668.rs:8:34 | LL | static childVal: Box<P> = self.child.get(); - | ^^^^ non-constant value + | --------------- ^^^^ non-constant value + | | + | help: consider using `let` instead of `static`: `let childVal` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-42060.stderr b/src/test/ui/issues/issue-42060.stderr index 72408c79194..effcbe4d7f3 100644 --- a/src/test/ui/issues/issue-42060.stderr +++ b/src/test/ui/issues/issue-42060.stderr @@ -1,12 +1,16 @@ error[E0435]: attempt to use a non-constant value in a constant --> $DIR/issue-42060.rs:3:23 | +LL | let thing = (); + | --------- help: consider using `const` instead of `let`: `const thing` LL | let other: typeof(thing) = thing; | ^^^^^ non-constant value error[E0435]: attempt to use a non-constant value in a constant --> $DIR/issue-42060.rs:9:13 | +LL | let q = 1; + | ----- help: consider using `const` instead of `let`: `const q` LL | <typeof(q)>::N | ^ non-constant value diff --git a/src/test/ui/issues/issue-44239.fixed b/src/test/ui/issues/issue-44239.fixed new file mode 100644 index 00000000000..e6c29cee97d --- /dev/null +++ b/src/test/ui/issues/issue-44239.fixed @@ -0,0 +1,11 @@ +// run-rustfix +#![allow(dead_code, non_upper_case_globals)] +fn main() { + const n: usize = 0; + + struct Foo; + impl Foo { + const N: usize = n; + //~^ ERROR attempt to use a non-constant value + } +} diff --git a/src/test/ui/issues/issue-44239.rs b/src/test/ui/issues/issue-44239.rs index 99a865f75a4..482ed194c7a 100644 --- a/src/test/ui/issues/issue-44239.rs +++ b/src/test/ui/issues/issue-44239.rs @@ -1,5 +1,7 @@ +// run-rustfix +#![allow(dead_code, non_upper_case_globals)] fn main() { - let n = 0; + let n: usize = 0; struct Foo; impl Foo { diff --git a/src/test/ui/issues/issue-44239.stderr b/src/test/ui/issues/issue-44239.stderr index bc5a6a03f03..2a245c92c48 100644 --- a/src/test/ui/issues/issue-44239.stderr +++ b/src/test/ui/issues/issue-44239.stderr @@ -1,6 +1,9 @@ error[E0435]: attempt to use a non-constant value in a constant - --> $DIR/issue-44239.rs:6:26 + --> $DIR/issue-44239.rs:8:26 | +LL | let n: usize = 0; + | ----- help: consider using `const` instead of `let`: `const n` +... LL | const N: usize = n; | ^ non-constant value diff --git a/src/test/ui/issues/issue-61108.stderr b/src/test/ui/issues/issue-61108.stderr index d6c289cbd66..fb242f738c8 100644 --- a/src/test/ui/issues/issue-61108.stderr +++ b/src/test/ui/issues/issue-61108.stderr @@ -12,7 +12,7 @@ LL | for l in bad_letters { LL | bad_letters.push('s'); | ^^^^^^^^^^^ value borrowed here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `bad_letters` +note: this function takes ownership of the receiver `self`, which moves `bad_letters` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; diff --git a/src/test/ui/issues/issue-64559.stderr b/src/test/ui/issues/issue-64559.stderr index 5b97e21b888..e0da3fd5195 100644 --- a/src/test/ui/issues/issue-64559.stderr +++ b/src/test/ui/issues/issue-64559.stderr @@ -13,7 +13,7 @@ LL | let _closure = || orig; | | | value used here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `orig` +note: this function takes ownership of the receiver `self`, which moves `orig` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; diff --git a/src/test/ui/issues/issue-80607.rs b/src/test/ui/issues/issue-80607.rs new file mode 100644 index 00000000000..63f4df359b8 --- /dev/null +++ b/src/test/ui/issues/issue-80607.rs @@ -0,0 +1,10 @@ +// This tests makes sure the diagnostics print the offending enum variant, not just the type. +pub enum Enum { + V1(i32), +} + +pub fn foo(x: i32) -> Enum { + Enum::V1 { x } //~ ERROR `Enum::V1` has no field named `x` +} + +fn main() {} diff --git a/src/test/ui/issues/issue-80607.stderr b/src/test/ui/issues/issue-80607.stderr new file mode 100644 index 00000000000..5375478942b --- /dev/null +++ b/src/test/ui/issues/issue-80607.stderr @@ -0,0 +1,14 @@ +error[E0559]: variant `Enum::V1` has no field named `x` + --> $DIR/issue-80607.rs:7:16 + | +LL | V1(i32), + | -- `Enum::V1` defined here +... +LL | Enum::V1 { x } + | -------- ^ field does not exist + | | + | `Enum::V1` is a tuple variant, use the appropriate syntax: `Enum::V1(/* fields */)` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0559`. diff --git a/src/test/ui/lint/lint-non-snake-case-identifiers-suggestion-reserved.rs b/src/test/ui/lint/lint-non-snake-case-identifiers-suggestion-reserved.rs new file mode 100644 index 00000000000..8cc4f976a4b --- /dev/null +++ b/src/test/ui/lint/lint-non-snake-case-identifiers-suggestion-reserved.rs @@ -0,0 +1,19 @@ +#![warn(unused)] +#![allow(dead_code)] +#![deny(non_snake_case)] + +mod Impl {} +//~^ ERROR module `Impl` should have a snake case name + +fn While() {} +//~^ ERROR function `While` should have a snake case name + +fn main() { + let Mod: usize = 0; + //~^ ERROR variable `Mod` should have a snake case name + //~^^ WARN unused variable: `Mod` + + let Super: usize = 0; + //~^ ERROR variable `Super` should have a snake case name + //~^^ WARN unused variable: `Super` +} diff --git a/src/test/ui/lint/lint-non-snake-case-identifiers-suggestion-reserved.stderr b/src/test/ui/lint/lint-non-snake-case-identifiers-suggestion-reserved.stderr new file mode 100644 index 00000000000..c179f4a25bd --- /dev/null +++ b/src/test/ui/lint/lint-non-snake-case-identifiers-suggestion-reserved.stderr @@ -0,0 +1,67 @@ +warning: unused variable: `Mod` + --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:12:9 + | +LL | let Mod: usize = 0; + | ^^^ help: if this is intentional, prefix it with an underscore: `_Mod` + | +note: the lint level is defined here + --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:1:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: unused variable: `Super` + --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:16:9 + | +LL | let Super: usize = 0; + | ^^^^^ help: if this is intentional, prefix it with an underscore: `_Super` + +error: module `Impl` should have a snake case name + --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:5:5 + | +LL | mod Impl {} + | ^^^^ + | +note: the lint level is defined here + --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:3:9 + | +LL | #![deny(non_snake_case)] + | ^^^^^^^^^^^^^^ +help: rename the identifier or convert it to a snake case raw identifier + | +LL | mod r#impl {} + | ^^^^^^ + +error: function `While` should have a snake case name + --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:8:4 + | +LL | fn While() {} + | ^^^^^ + | +help: rename the identifier or convert it to a snake case raw identifier + | +LL | fn r#while() {} + | ^^^^^^^ + +error: variable `Mod` should have a snake case name + --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:12:9 + | +LL | let Mod: usize = 0; + | ^^^ + | +help: rename the identifier or convert it to a snake case raw identifier + | +LL | let r#mod: usize = 0; + | ^^^^^ + +error: variable `Super` should have a snake case name + --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:16:9 + | +LL | let Super: usize = 0; + | ^^^^^ help: rename the identifier + | + = note: `super` cannot be used as a raw identifier + +error: aborting due to 4 previous errors; 2 warnings emitted + diff --git a/src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.rs b/src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.rs new file mode 100644 index 00000000000..2437155d981 --- /dev/null +++ b/src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.rs @@ -0,0 +1,21 @@ +// Ensures -Zmir-opt-level=2 (specifically, inlining) is not allowed with -Zinstrument-coverage. +// Regression test for issue #80060. +// +// needs-profiler-support +// build-pass +// compile-flags: -Zmir-opt-level=2 -Zinstrument-coverage +#[inline(never)] +fn foo() {} + +pub fn baz() { + bar(); +} + +#[inline(always)] +fn bar() { + foo(); +} + +fn main() { + bar(); +} diff --git a/src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.stderr b/src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.stderr new file mode 100644 index 00000000000..eb50e5075ca --- /dev/null +++ b/src/test/ui/mir/mir-inlining/inline-instrument-coverage-fail.stderr @@ -0,0 +1,2 @@ +warning: `-Z mir-opt-level=2` (or any level > 1) enables function inlining, which is incompatible with `-Z instrument-coverage`. Inlining will be disabled. + diff --git a/src/test/ui/moves/move-fn-self-receiver.rs b/src/test/ui/moves/move-fn-self-receiver.rs index 6107f53fa19..946642ef6f3 100644 --- a/src/test/ui/moves/move-fn-self-receiver.rs +++ b/src/test/ui/moves/move-fn-self-receiver.rs @@ -69,6 +69,11 @@ fn move_out(val: Container) { let container = Container(vec![]); for _val in container.custom_into_iter() {} container; //~ ERROR use of moved + + let foo2 = Foo; + loop { + foo2.use_self(); //~ ERROR use of moved + } } fn main() {} diff --git a/src/test/ui/moves/move-fn-self-receiver.stderr b/src/test/ui/moves/move-fn-self-receiver.stderr index dd263c1e906..eca6bb9296d 100644 --- a/src/test/ui/moves/move-fn-self-receiver.stderr +++ b/src/test/ui/moves/move-fn-self-receiver.stderr @@ -6,7 +6,7 @@ LL | val.0.into_iter().next(); LL | val.0; | ^^^^^ value used here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `val.0` +note: this function takes ownership of the receiver `self`, which moves `val.0` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; @@ -23,7 +23,7 @@ LL | foo.use_self(); LL | foo; | ^^^ value used here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `foo` +note: this function takes ownership of the receiver `self`, which moves `foo` --> $DIR/move-fn-self-receiver.rs:13:17 | LL | fn use_self(self) {} @@ -49,7 +49,7 @@ LL | boxed_foo.use_box_self(); LL | boxed_foo; | ^^^^^^^^^ value used here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `boxed_foo` +note: this function takes ownership of the receiver `self`, which moves `boxed_foo` --> $DIR/move-fn-self-receiver.rs:14:21 | LL | fn use_box_self(self: Box<Self>) {} @@ -65,7 +65,7 @@ LL | pin_box_foo.use_pin_box_self(); LL | pin_box_foo; | ^^^^^^^^^^^ value used here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `pin_box_foo` +note: this function takes ownership of the receiver `self`, which moves `pin_box_foo` --> $DIR/move-fn-self-receiver.rs:15:25 | LL | fn use_pin_box_self(self: Pin<Box<Self>>) {} @@ -91,7 +91,7 @@ LL | rc_foo.use_rc_self(); LL | rc_foo; | ^^^^^^ value used here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `rc_foo` +note: this function takes ownership of the receiver `self`, which moves `rc_foo` --> $DIR/move-fn-self-receiver.rs:16:20 | LL | fn use_rc_self(self: Rc<Self>) {} @@ -146,13 +146,22 @@ LL | for _val in container.custom_into_iter() {} LL | container; | ^^^^^^^^^ value used here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `container` +note: this function takes ownership of the receiver `self`, which moves `container` --> $DIR/move-fn-self-receiver.rs:23:25 | LL | fn custom_into_iter(self) -> impl Iterator<Item = bool> { | ^^^^ -error: aborting due to 11 previous errors +error[E0382]: use of moved value: `foo2` + --> $DIR/move-fn-self-receiver.rs:75:9 + | +LL | let foo2 = Foo; + | ---- move occurs because `foo2` has type `Foo`, which does not implement the `Copy` trait +LL | loop { +LL | foo2.use_self(); + | ^^^^ ---------- `foo2` moved due to this method call, in previous iteration of loop + +error: aborting due to 12 previous errors Some errors have detailed explanations: E0382, E0505. For more information about an error, try `rustc --explain E0382`. diff --git a/src/test/ui/moves/moves-based-on-type-access-to-field.stderr b/src/test/ui/moves/moves-based-on-type-access-to-field.stderr index 11e94569580..3cc8ca29144 100644 --- a/src/test/ui/moves/moves-based-on-type-access-to-field.stderr +++ b/src/test/ui/moves/moves-based-on-type-access-to-field.stderr @@ -8,7 +8,7 @@ LL | consume(x.into_iter().next().unwrap()); LL | touch(&x[0]); | ^ value borrowed here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `x` +note: this function takes ownership of the receiver `self`, which moves `x` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; diff --git a/src/test/ui/moves/moves-based-on-type-exprs.stderr b/src/test/ui/moves/moves-based-on-type-exprs.stderr index 46940cf4936..9bcec36740d 100644 --- a/src/test/ui/moves/moves-based-on-type-exprs.stderr +++ b/src/test/ui/moves/moves-based-on-type-exprs.stderr @@ -108,7 +108,7 @@ LL | let _y = x.into_iter().next().unwrap(); LL | touch(&x); | ^^ value borrowed here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `x` +note: this function takes ownership of the receiver `self`, which moves `x` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; @@ -124,7 +124,7 @@ LL | let _y = [x.into_iter().next().unwrap(); 1]; LL | touch(&x); | ^^ value borrowed here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `x` +note: this function takes ownership of the receiver `self`, which moves `x` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; diff --git a/src/test/ui/no-patterns-in-args-2.stderr b/src/test/ui/no-patterns-in-args-2.stderr index 21f4439d890..98932349a79 100644 --- a/src/test/ui/no-patterns-in-args-2.stderr +++ b/src/test/ui/no-patterns-in-args-2.stderr @@ -8,7 +8,7 @@ error: patterns aren't allowed in functions without bodies --> $DIR/no-patterns-in-args-2.rs:4:11 | LL | fn f1(mut arg: u8); - | ^^^^^^^ + | ^^^^^^^ help: remove `mut` from the parameter: `arg` | note: the lint level is defined here --> $DIR/no-patterns-in-args-2.rs:1:9 diff --git a/src/test/ui/non-constant-expr-for-arr-len.stderr b/src/test/ui/non-constant-expr-for-arr-len.stderr index b947cb7e19c..d684b8eaabd 100644 --- a/src/test/ui/non-constant-expr-for-arr-len.stderr +++ b/src/test/ui/non-constant-expr-for-arr-len.stderr @@ -1,8 +1,10 @@ error[E0435]: attempt to use a non-constant value in a constant --> $DIR/non-constant-expr-for-arr-len.rs:5:22 | +LL | fn bar(n: usize) { + | - this would need to be a `const` LL | let _x = [0; n]; - | ^ non-constant value + | ^ error: aborting due to previous error diff --git a/src/test/ui/overloaded-calls-nontuple.rs b/src/test/ui/overloaded-calls-nontuple.rs index 76b114c5592..07d44ff82b1 100644 --- a/src/test/ui/overloaded-calls-nontuple.rs +++ b/src/test/ui/overloaded-calls-nontuple.rs @@ -11,13 +11,13 @@ impl FnMut<isize> for S { extern "rust-call" fn call_mut(&mut self, z: isize) -> isize { self.x + self.y + z } - //~^^^ ERROR A function with the "rust-call" ABI must take a single non-self argument + //~^^^ ERROR functions with the "rust-call" ABI must take a single non-self argument } impl FnOnce<isize> for S { type Output = isize; extern "rust-call" fn call_once(mut self, z: isize) -> isize { self.call_mut(z) } - //~^ ERROR A function with the "rust-call" ABI must take a single non-self argument + //~^ ERROR functions with the "rust-call" ABI must take a single non-self argument } fn main() { diff --git a/src/test/ui/overloaded-calls-nontuple.stderr b/src/test/ui/overloaded-calls-nontuple.stderr index bdadb95db29..8f299bc9434 100644 --- a/src/test/ui/overloaded-calls-nontuple.stderr +++ b/src/test/ui/overloaded-calls-nontuple.stderr @@ -1,10 +1,10 @@ -error: A function with the "rust-call" ABI must take a single non-self argument that is a tuple +error: functions with the "rust-call" ABI must take a single non-self argument that is a tuple --> $DIR/overloaded-calls-nontuple.rs:11:5 | LL | extern "rust-call" fn call_mut(&mut self, z: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: A function with the "rust-call" ABI must take a single non-self argument that is a tuple +error: functions with the "rust-call" ABI must take a single non-self argument that is a tuple --> $DIR/overloaded-calls-nontuple.rs:19:5 | LL | extern "rust-call" fn call_once(mut self, z: isize) -> isize { self.call_mut(z) } diff --git a/src/test/ui/panic-handler/weak-lang-item.stderr b/src/test/ui/panic-handler/weak-lang-item.stderr index b7c040c7a85..68e3e21df3e 100644 --- a/src/test/ui/panic-handler/weak-lang-item.stderr +++ b/src/test/ui/panic-handler/weak-lang-item.stderr @@ -10,10 +10,10 @@ help: you can use `as` to change the binding name of the import LL | extern crate core as other_core; | -error: `#[panic_handler]` function required, but not found - error: language item required, but not found: `eh_personality` +error: `#[panic_handler]` function required, but not found + error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0259`. diff --git a/src/test/ui/parser/block-no-opening-brace.rs b/src/test/ui/parser/block-no-opening-brace.rs index e4bb39f6836..8a6599488b1 100644 --- a/src/test/ui/parser/block-no-opening-brace.rs +++ b/src/test/ui/parser/block-no-opening-brace.rs @@ -26,6 +26,6 @@ fn f4() { } fn f5() { - async //~ ERROR async closures are unstable + async let x = 0; //~ ERROR expected one of `move`, `|`, or `||`, found keyword `let` } diff --git a/src/test/ui/parser/block-no-opening-brace.stderr b/src/test/ui/parser/block-no-opening-brace.stderr index a88e4ac44cf..e32c8bdc73a 100644 --- a/src/test/ui/parser/block-no-opening-brace.stderr +++ b/src/test/ui/parser/block-no-opening-brace.stderr @@ -39,15 +39,5 @@ LL | async LL | let x = 0; | ^^^ unexpected token -error[E0658]: async closures are unstable - --> $DIR/block-no-opening-brace.rs:29:5 - | -LL | async - | ^^^^^ - | - = note: see issue #62290 <https://github.com/rust-lang/rust/issues/62290> for more information - = help: add `#![feature(async_closure)]` to the crate attributes to enable - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/parser/import-from-path.stderr b/src/test/ui/parser/import-from-path.stderr index 5842037fb80..93bdf82d0f5 100644 --- a/src/test/ui/parser/import-from-path.stderr +++ b/src/test/ui/parser/import-from-path.stderr @@ -3,6 +3,8 @@ error: expected `;`, found `::` | LL | use foo::{bar}::baz | ^^ expected `;` + | + = note: glob-like brace syntax must be last on the path error: aborting due to previous error diff --git a/src/test/ui/parser/import-from-rename.stderr b/src/test/ui/parser/import-from-rename.stderr index a966e993737..d78f6de9222 100644 --- a/src/test/ui/parser/import-from-rename.stderr +++ b/src/test/ui/parser/import-from-rename.stderr @@ -3,6 +3,8 @@ error: expected `;`, found keyword `as` | LL | use foo::{bar} as baz; | ^^ expected `;` + | + = note: glob-like brace syntax must be last on the path error: aborting due to previous error diff --git a/src/test/ui/parser/import-glob-path.stderr b/src/test/ui/parser/import-glob-path.stderr index ebca2db8305..a93ef255c94 100644 --- a/src/test/ui/parser/import-glob-path.stderr +++ b/src/test/ui/parser/import-glob-path.stderr @@ -3,6 +3,8 @@ error: expected `;`, found `::` | LL | use foo::*::bar | ^^ expected `;` + | + = note: the wildcard token must be last on the path error: aborting due to previous error diff --git a/src/test/ui/parser/import-glob-rename.stderr b/src/test/ui/parser/import-glob-rename.stderr index 28538732782..e1a026b639e 100644 --- a/src/test/ui/parser/import-glob-rename.stderr +++ b/src/test/ui/parser/import-glob-rename.stderr @@ -3,6 +3,8 @@ error: expected `;`, found keyword `as` | LL | use foo::* as baz; | ^^ expected `;` + | + = note: the wildcard token must be last on the path error: aborting due to previous error diff --git a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr index 0fa77fb73da..2ca774a48b6 100644 --- a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr +++ b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr @@ -6,6 +6,7 @@ LL | match uninhab_ref() { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `&!` + = note: references are always considered inhabited error[E0004]: non-exhaustive patterns: type `Foo` is non-empty --> $DIR/always-inhabited-union-ref.rs:27:11 diff --git a/src/test/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs b/src/test/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs new file mode 100644 index 00000000000..6c5a331b4b5 --- /dev/null +++ b/src/test/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs @@ -0,0 +1,11 @@ +enum A {} + //~^ NOTE `A` defined here + +fn f(a: &A) { + match a {} + //~^ ERROR non-exhaustive patterns: type `&A` is non-empty + //~| NOTE the matched value is of type `&A` + //~| NOTE references are always considered inhabited +} + +fn main() {} diff --git a/src/test/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr b/src/test/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr new file mode 100644 index 00000000000..e992632a91f --- /dev/null +++ b/src/test/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr @@ -0,0 +1,16 @@ +error[E0004]: non-exhaustive patterns: type `&A` is non-empty + --> $DIR/issue-78123-non-exhaustive-reference.rs:5:11 + | +LL | enum A {} + | --------- `A` defined here +... +LL | match a {} + | ^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + = note: the matched value is of type `&A` + = note: references are always considered inhabited + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0004`. 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 4c0810217bf..5f513684cfa 100644 --- a/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout +++ b/src/test/ui/proc-macro/issue-75930-derive-cfg.stdout @@ -1211,141 +1211,141 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "allow", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "dead_code", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_helper", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "b", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_helper", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "a", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "struct", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "Foo", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '<', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "B", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "second", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "bool", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "third", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "u8", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Brace, @@ -1353,58 +1353,58 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "struct", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "Inner", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "match", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "true", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Brace, @@ -1412,146 +1412,146 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "allow", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "warnings", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "false", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '=', spacing: Joint, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "_", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '=', spacing: Joint, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '>', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Brace, stream: TokenStream [], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_helper", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "c", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "fn", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "kept_fn", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Brace, @@ -1559,82 +1559,82 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "let", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "my_val", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '=', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "true", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "enum", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "TupleEnum", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Brace, stream: TokenStream [ Ident { ident: "Foo", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, @@ -1642,69 +1642,69 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "i32", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "u8", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "struct", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "TupleStruct", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, @@ -1712,120 +1712,120 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "cfg", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "not", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "FALSE", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "i32", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "u8", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ';', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Literal { kind: Integer, symbol: "0", suffix: None, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: '#', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "print_helper", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "d", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "fourth", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Ident { ident: "B", - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, Punct { ch: ',', spacing: Alone, - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64:2 (#0), }, ], - span: $DIR/issue-75930-derive-cfg.rs:1:1: 1:1 (#0), + span: $DIR/issue-75930-derive-cfg.rs:21:1: 64: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 c4ee44f6541..40da5aa93bf 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 @@ -34,11 +34,11 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ stream: TokenStream [ Ident { ident: "mod", - span: $DIR/issue-78675-captured-inner-attrs.rs:22:13: 22:18 (#4), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), }, Ident { ident: "bar", - span: $DIR/issue-78675-captured-inner-attrs.rs:22:13: 22:18 (#4), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), }, Group { delimiter: Brace, @@ -46,36 +46,36 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/issue-78675-captured-inner-attrs.rs:22:13: 22:18 (#4), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/issue-78675-captured-inner-attrs.rs:22:13: 22:18 (#4), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "doc", - span: $DIR/issue-78675-captured-inner-attrs.rs:22:13: 22:18 (#4), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), }, Punct { ch: '=', spacing: Alone, - span: $DIR/issue-78675-captured-inner-attrs.rs:22:13: 22:18 (#4), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), }, Literal { kind: StrRaw(0), symbol: " Foo", suffix: None, - span: $DIR/issue-78675-captured-inner-attrs.rs:22:13: 22:18 (#4), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), }, ], - span: $DIR/issue-78675-captured-inner-attrs.rs:22:13: 22:18 (#4), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), }, ], - span: $DIR/issue-78675-captured-inner-attrs.rs:22:13: 22:18 (#4), + span: $DIR/issue-78675-captured-inner-attrs.rs:27:5: 29:6 (#0), }, ], span: $DIR/issue-78675-captured-inner-attrs.rs:22:13: 22:18 (#4), diff --git a/src/test/ui/proc-macro/issue-80760-empty-stmt.rs b/src/test/ui/proc-macro/issue-80760-empty-stmt.rs new file mode 100644 index 00000000000..86865af0b52 --- /dev/null +++ b/src/test/ui/proc-macro/issue-80760-empty-stmt.rs @@ -0,0 +1,26 @@ +// check-pass +// aux-build:test-macros.rs +// compile-flags: -Z span-debug + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] +extern crate test_macros; + +macro_rules! empty_stmt { + ($s:stmt) => { + print_bang!($s); + + // Currently, all attributes are ignored + // on an empty statement + #[print_attr] + #[rustc_dummy(first)] + #[rustc_dummy(second)] + $s + } +} + +fn main() { + empty_stmt!(;); +} diff --git a/src/test/ui/proc-macro/issue-80760-empty-stmt.stdout b/src/test/ui/proc-macro/issue-80760-empty-stmt.stdout new file mode 100644 index 00000000000..4b7ed874307 --- /dev/null +++ b/src/test/ui/proc-macro/issue-80760-empty-stmt.stdout @@ -0,0 +1,14 @@ +PRINT-BANG INPUT (DISPLAY): ; +PRINT-BANG INPUT (DEBUG): TokenStream [ + Group { + delimiter: None, + stream: TokenStream [ + Punct { + ch: ';', + spacing: Alone, + span: $DIR/issue-80760-empty-stmt.rs:25:17: 25:18 (#0), + }, + ], + span: $DIR/issue-80760-empty-stmt.rs:13:21: 13:23 (#4), + }, +] diff --git a/src/test/ui/repeat_count.stderr b/src/test/ui/repeat_count.stderr index 5fcda348ab3..e90754e9118 100644 --- a/src/test/ui/repeat_count.stderr +++ b/src/test/ui/repeat_count.stderr @@ -1,6 +1,8 @@ error[E0435]: attempt to use a non-constant value in a constant --> $DIR/repeat_count.rs:5:17 | +LL | let n = 1; + | ----- help: consider using `const` instead of `let`: `const n` LL | let a = [0; n]; | ^ non-constant value diff --git a/src/test/ui/sanitize/unsupported-target.stderr b/src/test/ui/sanitize/unsupported-target.stderr index f5961a11b1f..093678707fb 100644 --- a/src/test/ui/sanitize/unsupported-target.stderr +++ b/src/test/ui/sanitize/unsupported-target.stderr @@ -1,4 +1,4 @@ -error: `-Zsanitizer=leak` only works with targets: aarch64-unknown-linux-gnu, x86_64-apple-darwin, x86_64-unknown-linux-gnu +error: `-Zsanitizer=leak` only works with targets: aarch64-apple-darwin, aarch64-unknown-linux-gnu, x86_64-apple-darwin, x86_64-unknown-linux-gnu error: aborting due to previous error diff --git a/src/test/ui/suggestions/borrow-for-loop-head.stderr b/src/test/ui/suggestions/borrow-for-loop-head.stderr index de342a969f4..28c319b6597 100644 --- a/src/test/ui/suggestions/borrow-for-loop-head.stderr +++ b/src/test/ui/suggestions/borrow-for-loop-head.stderr @@ -15,8 +15,14 @@ LL | for i in &a { LL | for j in a { | ^ | | - | value moved here, in previous iteration of loop + | `a` moved due to this implicit call to `.into_iter()`, in previous iteration of loop | help: consider borrowing to avoid moving into the for loop: `&a` + | +note: this function takes ownership of the receiver `self`, which moves `a` + --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + | +LL | fn into_iter(self) -> Self::IntoIter; + | ^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/terminal-width/tabs-trimming.rs b/src/test/ui/terminal-width/tabs-trimming.rs new file mode 100644 index 00000000000..ade21753b45 --- /dev/null +++ b/src/test/ui/terminal-width/tabs-trimming.rs @@ -0,0 +1,13 @@ +// Test for #78438: ensure underline alignment with many tabs on the left, long line on the right + +// ignore-tidy-linelength +// ignore-tidy-tab + + fn main() { + let money = 42i32; + match money { + v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT + //~^ ERROR variable `v` is not bound in all patterns + v => println!("Enough money {}", v), + } + } diff --git a/src/test/ui/terminal-width/tabs-trimming.stderr b/src/test/ui/terminal-width/tabs-trimming.stderr new file mode 100644 index 00000000000..6c8d9afc73b --- /dev/null +++ b/src/test/ui/terminal-width/tabs-trimming.stderr @@ -0,0 +1,12 @@ +error[E0408]: variable `v` is not bound in all patterns + --> $DIR/tabs-trimming.rs:9:16 + | +LL | ... v @ 1 | 2 | 3 => panic!("You gave me too little money {}", v), // Long text here: TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT... + | - ^ ^ pattern doesn't bind `v` + | | | + | | pattern doesn't bind `v` + | variable not in all patterns + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0408`. diff --git a/src/test/ui/type/type-dependent-def-issue-49241.stderr b/src/test/ui/type/type-dependent-def-issue-49241.stderr index c5dcfa7a431..64c7687f7a8 100644 --- a/src/test/ui/type/type-dependent-def-issue-49241.stderr +++ b/src/test/ui/type/type-dependent-def-issue-49241.stderr @@ -2,7 +2,9 @@ error[E0435]: attempt to use a non-constant value in a constant --> $DIR/type-dependent-def-issue-49241.rs:3:22 | LL | const l: usize = v.count(); - | ^ non-constant value + | ------- ^ non-constant value + | | + | help: consider using `let` instead of `const`: `let l` error: aborting due to previous error diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index 960c4792e65..7b999f50773 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -23,6 +23,7 @@ LL | let _ = match x {}; | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `&Void` + = note: references are always considered inhabited error[E0004]: non-exhaustive patterns: type `(Void,)` is non-empty --> $DIR/uninhabited-matches-feature-gated.rs:18:19 diff --git a/src/test/ui/unsafe/ranged_ints3_const.rs b/src/test/ui/unsafe/ranged_ints3_const.rs index 7b03d8eda93..c069ae7da02 100644 --- a/src/test/ui/unsafe/ranged_ints3_const.rs +++ b/src/test/ui/unsafe/ranged_ints3_const.rs @@ -9,13 +9,13 @@ fn main() {} const fn foo() -> NonZero<Cell<u32>> { let mut x = unsafe { NonZero(Cell::new(1)) }; - let y = &x.0; //~ ERROR cannot borrow a constant which may contain interior mutability + let y = &x.0; //~ ERROR the borrowed element may contain interior mutability //~^ ERROR borrow of layout constrained field with interior mutability unsafe { NonZero(Cell::new(1)) } } const fn bar() -> NonZero<Cell<u32>> { let mut x = unsafe { NonZero(Cell::new(1)) }; - let y = unsafe { &x.0 }; //~ ERROR cannot borrow a constant which may contain interior mut + let y = unsafe { &x.0 }; //~ ERROR the borrowed element may contain interior mutability unsafe { NonZero(Cell::new(1)) } } diff --git a/src/test/ui/unsafe/ranged_ints3_const.stderr b/src/test/ui/unsafe/ranged_ints3_const.stderr index d2eb3bc5360..215005571f6 100644 --- a/src/test/ui/unsafe/ranged_ints3_const.stderr +++ b/src/test/ui/unsafe/ranged_ints3_const.stderr @@ -1,14 +1,20 @@ -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability --> $DIR/ranged_ints3_const.rs:12:13 | LL | let y = &x.0; | ^^^^ + | + = note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information + = help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable -error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead +error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability --> $DIR/ranged_ints3_const.rs:19:22 | LL | let y = unsafe { &x.0 }; | ^^^^ + | + = note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information + = help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable error[E0133]: borrow of layout constrained field with interior mutability is unsafe and requires unsafe function or block --> $DIR/ranged_ints3_const.rs:12:13 @@ -20,5 +26,5 @@ LL | let y = &x.0; error: aborting due to 3 previous errors -Some errors have detailed explanations: E0133, E0492. +Some errors have detailed explanations: E0133, E0658. For more information about an error, try `rustc --explain E0133`. diff --git a/src/test/ui/unsized-locals/borrow-after-move.stderr b/src/test/ui/unsized-locals/borrow-after-move.stderr index 5934276cc1d..4f2bc06d4ab 100644 --- a/src/test/ui/unsized-locals/borrow-after-move.stderr +++ b/src/test/ui/unsized-locals/borrow-after-move.stderr @@ -51,7 +51,7 @@ LL | y.foo(); LL | println!("{}", &y); | ^^ value borrowed here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `y` +note: this function takes ownership of the receiver `self`, which moves `y` --> $DIR/borrow-after-move.rs:5:12 | LL | fn foo(self) -> String; diff --git a/src/test/ui/unsized-locals/double-move.stderr b/src/test/ui/unsized-locals/double-move.stderr index b897dbbc9a3..4bb2ad88faf 100644 --- a/src/test/ui/unsized-locals/double-move.stderr +++ b/src/test/ui/unsized-locals/double-move.stderr @@ -47,7 +47,7 @@ LL | y.foo(); LL | y.foo(); | ^ value used here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `y` +note: this function takes ownership of the receiver `self`, which moves `y` --> $DIR/double-move.rs:5:12 | LL | fn foo(self) -> String; diff --git a/src/test/ui/use/use-after-move-self-based-on-type.stderr b/src/test/ui/use/use-after-move-self-based-on-type.stderr index b9440f4de07..7fdc4ab251f 100644 --- a/src/test/ui/use/use-after-move-self-based-on-type.stderr +++ b/src/test/ui/use/use-after-move-self-based-on-type.stderr @@ -8,7 +8,7 @@ LL | self.bar(); LL | return self.x; | ^^^^^^ value used here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `self` +note: this function takes ownership of the receiver `self`, which moves `self` --> $DIR/use-after-move-self-based-on-type.rs:15:16 | LL | pub fn bar(self) {} diff --git a/src/test/ui/use/use-after-move-self.stderr b/src/test/ui/use/use-after-move-self.stderr index 3da53b024db..073deee63b9 100644 --- a/src/test/ui/use/use-after-move-self.stderr +++ b/src/test/ui/use/use-after-move-self.stderr @@ -8,7 +8,7 @@ LL | self.bar(); LL | return *self.x; | ^^^^^^^ value used here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `self` +note: this function takes ownership of the receiver `self`, which moves `self` --> $DIR/use-after-move-self.rs:13:16 | LL | pub fn bar(self) {} diff --git a/src/test/ui/walk-struct-literal-with.stderr b/src/test/ui/walk-struct-literal-with.stderr index ece63a2b819..cda08b0f4e0 100644 --- a/src/test/ui/walk-struct-literal-with.stderr +++ b/src/test/ui/walk-struct-literal-with.stderr @@ -8,7 +8,7 @@ LL | let end = Mine{other_val:1, ..start.make_string_bar()}; LL | println!("{}", start.test); | ^^^^^^^^^^ value borrowed here after move | -note: this function consumes the receiver `self` by taking ownership of it, which moves `start` +note: this function takes ownership of the receiver `self`, which moves `start` --> $DIR/walk-struct-literal-with.rs:7:28 | LL | fn make_string_bar(mut self) -> Mine{ diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 75d5d8cffe3464631f82dcd3c470b78dc1dda8b +Subproject 329895f5b52a358e5d9ecb26215708b5cb31d90 diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md new file mode 100644 index 00000000000..f46828fec91 --- /dev/null +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.md @@ -0,0 +1,35 @@ +--- +name: Bug Report (False Negative) +about: Create a bug report about missing warnings from a lint +labels: L-bug, L-false-negative +--- +<!-- +Thank you for filing a bug report! 🐛 Please provide a short summary of the bug, +along with any information you feel relevant to replicating the bug. +--> +Lint name: + + +I tried this code: + +```rust +<code> +``` + +I expected to see this happen: *explanation* + +Instead, this happened: *explanation* + +### Meta + +- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) +- `rustc -Vv`: + ``` + rustc 1.46.0-nightly (f455e46ea 2020-06-20) + binary: rustc + commit-hash: f455e46eae1a227d735091091144601b467e1565 + commit-date: 2020-06-20 + host: x86_64-unknown-linux-gnu + release: 1.46.0-nightly + LLVM version: 10.0 + ``` diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md new file mode 100644 index 00000000000..92a7373fc27 --- /dev/null +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.md @@ -0,0 +1,35 @@ +--- +name: Bug Report (False Positive) +about: Create a bug report about a wrongly emitted lint warning +labels: L-bug, L-false-positive +--- +<!-- +Thank you for filing a bug report! 🐛 Please provide a short summary of the bug, +along with any information you feel relevant to replicating the bug. +--> +Lint name: + + +I tried this code: + +```rust +<code> +``` + +I expected to see this happen: *explanation* + +Instead, this happened: *explanation* + +### Meta + +- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) +- `rustc -Vv`: + ``` + rustc 1.46.0-nightly (f455e46ea 2020-06-20) + binary: rustc + commit-hash: f455e46eae1a227d735091091144601b467e1565 + commit-date: 2020-06-20 + host: x86_64-unknown-linux-gnu + release: 1.46.0-nightly + LLVM version: 10.0 + ``` diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index 530e60001f7..9d5e12aac5f 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -50,6 +50,9 @@ jobs: - name: Build run: cargo build --features deny-warnings,internal-lints + - name: Test "--fix -Zunstable-options" + run: cargo run --features deny-warnings,internal-lints --bin cargo-clippy -- clippy --fix -Zunstable-options + - name: Test run: cargo test --features deny-warnings,internal-lints diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index af3b1c1db2a..de8da99cdee 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -1841,6 +1841,7 @@ Released 2018-09-13 [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect +[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index a765390c603..e60aa472846 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.0.212" +version = "0.1.51" authors = [ "Manish Goregaokar <manishsmail@gmail.com>", "Andre Bogus <bogusandre@gmail.com>", @@ -29,7 +29,7 @@ path = "src/driver.rs" [dependencies] # begin automatic update -clippy_lints = { version = "0.0.212", path = "clippy_lints" } +clippy_lints = { version = "0.1.50", path = "clippy_lints" } # end automatic update semver = "0.11" rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index aaa55e11c7d..a4928e17e6a 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -10,16 +10,16 @@ A collection of lints to catch common mistakes and improve your [Rust](https://g Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. -Category | Description | Default level --- | -- | -- -`clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny** -`clippy::correctness` | code that is outright wrong or very useless | **deny** -`clippy::style` | code that should be written in a more idiomatic way | **warn** -`clippy::complexity` | code that does something simple but in a complex way | **warn** -`clippy::perf` | code that can be written to run faster | **warn** -`clippy::pedantic` | lints which are rather strict or might have false positives | allow -`clippy::nursery` | new lints that are still under development | allow -`clippy::cargo` | lints for the cargo manifest | allow +| Category | Description | Default level | +| --------------------- | ----------------------------------------------------------------------- | ------------- | +| `clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny** | +| `clippy::correctness` | code that is outright wrong or very useless | **deny** | +| `clippy::style` | code that should be written in a more idiomatic way | **warn** | +| `clippy::complexity` | code that does something simple but in a complex way | **warn** | +| `clippy::perf` | code that can be written to run faster | **warn** | +| `clippy::pedantic` | lints which are rather strict or might have false positives | allow | +| `clippy::nursery` | new lints that are still under development | allow | +| `clippy::cargo` | lints for the cargo manifest | allow | More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas! @@ -98,17 +98,6 @@ If you want to run Clippy **only** on the given crate, use the `--no-deps` optio cargo clippy -p example -- --no-deps ``` -### Running Clippy from the command line without installing it - -To have cargo compile your crate with Clippy without Clippy installation -in your code, you can use: - -```terminal -cargo run --bin cargo-clippy --manifest-path=path_to_clippys_Cargo.toml -``` - -*Note:* Be sure that Clippy was compiled with the same version of rustc that cargo invokes here! - ### Travis CI You can add Clippy to Travis CI in the same way you use it locally: @@ -130,18 +119,6 @@ script: # etc. ``` -If you are on nightly, It might happen that Clippy is not available for a certain nightly release. -In this case you can try to conditionally install Clippy from the Git repo. - -```yaml -language: rust -rust: - - nightly -before_script: - - rustup component add clippy --toolchain=nightly || cargo install --git https://github.com/rust-lang/rust-clippy/ --force clippy - # etc. -``` - Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code. That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command diff --git a/src/tools/clippy/clippy_dev/src/ra_setup.rs b/src/tools/clippy/clippy_dev/src/ra_setup.rs index 40bf4a9505a..5f5048e79e7 100644 --- a/src/tools/clippy/clippy_dev/src/ra_setup.rs +++ b/src/tools/clippy/clippy_dev/src/ra_setup.rs @@ -3,7 +3,7 @@ use std::fs; use std::fs::File; use std::io::prelude::*; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; // This module takes an absolute path to a rustc repo and alters the dependencies to point towards // the respective rustc subcrates instead of using extern crate xyz. @@ -44,7 +44,7 @@ pub fn run(rustc_path: Option<&str>) { } fn inject_deps_into_manifest( - rustc_source_dir: &PathBuf, + rustc_source_dir: &Path, manifest_path: &str, cargo_toml: &str, lib_rs: &str, diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 7697eba650a..a9516560a61 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin automatic update -version = "0.0.212" +version = "0.1.51" # end automatic update authors = [ "Manish Goregaokar <manishsmail@gmail.com>", diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index f69f6f1412a..b0d7c7b3baa 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Adt, Ty}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -103,18 +103,41 @@ impl LateLintPass<'_> for Default { } fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { - // find all binding statements like `let mut _ = T::default()` where `T::default()` is the - // `default` method of the `Default` trait, and store statement index in current block being - // checked and the name of the bound variable - let binding_statements_using_default = enumerate_bindings_using_default(cx, block); - // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding - for (stmt_idx, binding_name, binding_type, span) in binding_statements_using_default { - // the last statement of a block cannot trigger the lint - if stmt_idx == block.stmts.len() - 1 { - break; - } + let stmts_head = match block.stmts { + // Skip the last statement since there cannot possibly be any following statements that re-assign fields. + [head @ .., _] if !head.is_empty() => head, + _ => return, + }; + for (stmt_idx, stmt) in stmts_head.iter().enumerate() { + // find all binding statements like `let mut _ = T::default()` where `T::default()` is the + // `default` method of the `Default` trait, and store statement index in current block being + // checked and the name of the bound variable + let (local, variant, binding_name, binding_type, span) = if_chain! { + // only take `let ...` statements + if let StmtKind::Local(local) = stmt.kind; + if let Some(expr) = local.init; + // only take bindings to identifiers + if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; + // only when assigning `... = Default::default()` + if is_expr_default(expr, cx); + let binding_type = cx.typeck_results().node_type(binding_id); + if let Some(adt) = binding_type.ty_adt_def(); + if adt.is_struct(); + let variant = adt.non_enum_variant(); + if adt.did.is_local() || !variant.is_field_list_non_exhaustive(); + let module_did = cx.tcx.parent_module(stmt.hir_id).to_def_id(); + if variant + .fields + .iter() + .all(|field| field.vis.is_accessible_from(module_did, cx.tcx)); + then { + (local, variant, ident.name, binding_type, expr.span) + } else { + continue; + } + }; // find all "later statement"'s where the fields of the binding set as // Default::default() get reassigned, unless the reassignment refers to the original binding @@ -122,15 +145,8 @@ impl LateLintPass<'_> for Default { let mut assigned_fields = Vec::new(); let mut cancel_lint = false; for consecutive_statement in &block.stmts[stmt_idx + 1..] { - // interrupt if the statement is a let binding (`Local`) that shadows the original - // binding - if stmt_shadows_binding(consecutive_statement, binding_name) { - break; - } // find out if and which field was set by this `consecutive_statement` - else if let Some((field_ident, assign_rhs)) = - field_reassigned_by_stmt(consecutive_statement, binding_name) - { + if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) { // interrupt and cancel lint if assign_rhs references the original binding if contains_name(binding_name, assign_rhs) { cancel_lint = true; @@ -152,7 +168,7 @@ impl LateLintPass<'_> for Default { first_assign = Some(consecutive_statement); } } - // interrupt also if no field was assigned, since we only want to look at consecutive statements + // interrupt if no field was assigned, since we only want to look at consecutive statements else { break; } @@ -161,55 +177,45 @@ impl LateLintPass<'_> for Default { // if there are incorrectly assigned fields, do a span_lint_and_note to suggest // construction using `Ty { fields, ..Default::default() }` if !assigned_fields.is_empty() && !cancel_lint { - // take the original assignment as span - let stmt = &block.stmts[stmt_idx]; - - if let StmtKind::Local(preceding_local) = &stmt.kind { - // filter out fields like `= Default::default()`, because the FRU already covers them - let assigned_fields = assigned_fields - .into_iter() - .filter(|(_, rhs)| !is_expr_default(rhs, cx)) - .collect::<Vec<(Symbol, &Expr<'_>)>>(); + // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion. + let ext_with_default = !variant + .fields + .iter() + .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name)); - // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion. - let ext_with_default = !fields_of_type(binding_type) - .iter() - .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name)); + let field_list = assigned_fields + .into_iter() + .map(|(field, rhs)| { + // extract and store the assigned value for help message + let value_snippet = snippet(cx, rhs.span, ".."); + format!("{}: {}", field, value_snippet) + }) + .collect::<Vec<String>>() + .join(", "); - let field_list = assigned_fields - .into_iter() - .map(|(field, rhs)| { - // extract and store the assigned value for help message - let value_snippet = snippet(cx, rhs.span, ".."); - format!("{}: {}", field, value_snippet) - }) - .collect::<Vec<String>>() - .join(", "); - - let sugg = if ext_with_default { - if field_list.is_empty() { - format!("{}::default()", binding_type) - } else { - format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) - } + let sugg = if ext_with_default { + if field_list.is_empty() { + format!("{}::default()", binding_type) } else { - format!("{} {{ {} }}", binding_type, field_list) - }; + format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) + } + } else { + format!("{} {{ {} }}", binding_type, field_list) + }; - // span lint once per statement that binds default - span_lint_and_note( - cx, - FIELD_REASSIGN_WITH_DEFAULT, - first_assign.unwrap().span, - "field assignment outside of initializer for an instance created with Default::default()", - Some(preceding_local.span), - &format!( - "consider initializing the variable with `{}` and removing relevant reassignments", - sugg - ), - ); - self.reassigned_linted.insert(span); - } + // span lint once per statement that binds default + span_lint_and_note( + cx, + FIELD_REASSIGN_WITH_DEFAULT, + first_assign.unwrap().span, + "field assignment outside of initializer for an instance created with Default::default()", + Some(local.span), + &format!( + "consider initializing the variable with `{}` and removing relevant reassignments", + sugg + ), + ); + self.reassigned_linted.insert(span); } } } @@ -230,47 +236,6 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool } } -/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except -/// for when the pattern type is a tuple. -fn enumerate_bindings_using_default<'tcx>( - cx: &LateContext<'tcx>, - block: &Block<'tcx>, -) -> Vec<(usize, Symbol, Ty<'tcx>, Span)> { - block - .stmts - .iter() - .enumerate() - .filter_map(|(idx, stmt)| { - if_chain! { - // only take `let ...` statements - if let StmtKind::Local(ref local) = stmt.kind; - // only take bindings to identifiers - if let PatKind::Binding(_, _, ident, _) = local.pat.kind; - // that are not tuples - let ty = cx.typeck_results().pat_ty(local.pat); - if !matches!(ty.kind(), ty::Tuple(_)); - // only when assigning `... = Default::default()` - if let Some(ref expr) = local.init; - if is_expr_default(expr, cx); - then { - Some((idx, ident.name, ty, expr.span)) - } else { - None - } - } - }) - .collect() -} - -fn stmt_shadows_binding(this: &Stmt<'_>, shadowed: Symbol) -> bool { - if let StmtKind::Local(local) = &this.kind { - if let PatKind::Binding(_, _, ident, _) = local.pat.kind { - return ident.name == shadowed; - } - } - false -} - /// Returns the reassigned field and the assigning expression (right-hand side of assign). fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { if_chain! { @@ -290,14 +255,3 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op } } } - -/// Returns the vec of fields for a struct and an empty vec for non-struct ADTs. -fn fields_of_type(ty: Ty<'_>) -> Vec<Ident> { - if let Adt(adt, _) = ty.kind() { - if adt.is_struct() { - let variant = &adt.non_enum_variant(); - return variant.fields.iter().map(|f| f.ident).collect(); - } - } - vec![] -} diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs new file mode 100644 index 00000000000..1e7e5f53cc2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -0,0 +1,83 @@ +use crate::utils::paths::INTO; +use crate::utils::{match_def_path, meets_msrv, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); + +declare_clippy_lint! { + /// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead. + /// + /// **Why is this bad?** According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct StringWrapper(String); + /// + /// impl Into<StringWrapper> for String { + /// fn into(self) -> StringWrapper { + /// StringWrapper(self) + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct StringWrapper(String); + /// + /// impl From<String> for StringWrapper { + /// fn from(s: String) -> StringWrapper { + /// StringWrapper(s) + /// } + /// } + /// ``` + pub FROM_OVER_INTO, + style, + "Warns on implementations of `Into<..>` to use `From<..>`" +} + +pub struct FromOverInto { + msrv: Option<RustcVersion>, +} + +impl FromOverInto { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + FromOverInto { msrv } + } +} + +impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); + +impl LateLintPass<'_> for FromOverInto { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) { + return; + } + + let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id); + if_chain! { + if let hir::ItemKind::Impl{ .. } = &item.kind; + if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id); + if match_def_path(cx, impl_trait_ref.def_id, &INTO); + + then { + span_lint_and_help( + cx, + FROM_OVER_INTO, + item.span, + "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", + None, + "consider to implement `From` instead", + ); + } + } + } + + extract_msrv_attr!(LateContext); +} diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs index 3c7880d74ee..ad9b4f357a7 100644 --- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs +++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs @@ -4,6 +4,7 @@ use crate::utils::{snippet_opt, span_lint_and_then}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_target::abi::LayoutOf; @@ -58,6 +59,9 @@ impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if in_external_macro(cx.tcx.sess, item.span) { + return; + } let did = cx.tcx.hir().local_def_id(item.hir_id); if let ItemKind::Enum(ref def, _) = item.kind { let ty = cx.tcx.type_of(did); diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 02ba422a2f5..35b057d7b6a 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -207,6 +207,7 @@ mod float_literal; mod floating_point_arithmetic; mod format; mod formatting; +mod from_over_into; mod functions; mod future_not_send; mod get_last_with_len; @@ -614,6 +615,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, &formatting::SUSPICIOUS_ELSE_FORMATTING, &formatting::SUSPICIOUS_UNARY_OP_FORMATTING, + &from_over_into::FROM_OVER_INTO, &functions::DOUBLE_MUST_USE, &functions::MUST_USE_CANDIDATE, &functions::MUST_USE_UNIT, @@ -1014,6 +1016,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv)); store.register_late_pass(move || box mem_replace::MemReplace::new(msrv)); store.register_late_pass(move || box ranges::Ranges::new(msrv)); + store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); store.register_late_pass(move || box use_self::UseSelf::new(msrv)); store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); @@ -1417,6 +1420,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(&from_over_into::FROM_OVER_INTO), LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), @@ -1663,6 +1667,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING), LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), + LintId::of(&from_over_into::FROM_OVER_INTO), LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::RESULT_UNIT_ERR), diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index b4b4b3dc18d..bb52888883a 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -105,7 +105,7 @@ impl MacroUseImports { impl<'tcx> LateLintPass<'tcx> for MacroUseImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { if_chain! { - if cx.sess().opts.edition == Edition::Edition2018; + if cx.sess().opts.edition >= Edition::Edition2018; if let hir::ItemKind::Use(path, _kind) = &item.kind; if let Some(mac_attr) = item .attrs diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 7b3b450ef93..29439e52c48 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { |diag| { if_chain! { if let Some(header_snip) = snippet_opt(cx, header_span); - if let Some(ret_pos) = position_before_rarrow(header_snip.clone()); + if let Some(ret_pos) = position_before_rarrow(&header_snip); if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); then { let help = format!("make the function `async` and {}", ret_sugg); diff --git a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/map_err_ignore.rs index f3c0515b9bc..76fe8e776ea 100644 --- a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs +++ b/src/tools/clippy/clippy_lints/src/map_err_ignore.rs @@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for instances of `map_err(|_| Some::Enum)` /// - /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to contain and report the cause of the error + /// **Why is this bad?** This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error /// /// **Known problems:** None. /// @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { body_span, "`map_err(|_|...` wildcard pattern discards the original error", None, - "Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", + "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", ); } } diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index dcb643a28ae..c494a713631 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -182,20 +182,6 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: if let ty::Ref(_, ty, Mutability::Not) = ty.kind() { if is_type_diagnostic_item(cx, ty, sym::vec_type) { - let mut ty_snippet = None; - if_chain! { - if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; - if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last(); - then { - let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }).collect(); - if types.len() == 1 { - ty_snippet = snippet_opt(cx, types[0].span); - } - } - }; if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) { span_lint_and_then( cx, @@ -204,7 +190,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \ with non-Vec-based slices.", |diag| { - if let Some(ref snippet) = ty_snippet { + if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) { diag.span_suggestion( arg.span, "change this to", @@ -247,6 +233,33 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: }, ); } + } else if match_type(cx, ty, &paths::PATH_BUF) { + if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_path_buf()"), ("as_path", "")]) { + span_lint_and_then( + cx, + PTR_ARG, + arg.span, + "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.", + |diag| { + diag.span_suggestion( + arg.span, + "change this to", + "&Path".into(), + Applicability::Unspecified, + ); + for (clonespan, suggestion) in spans { + diag.span_suggestion_short( + clonespan, + &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| { + Cow::Owned(format!("change `{}` to", x)) + }), + suggestion.into(), + Applicability::Unspecified, + ); + } + }, + ); + } } else if match_type(cx, ty, &paths::COW) { if_chain! { if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind; @@ -309,6 +322,23 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } } +fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option<String> { + if_chain! { + if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; + if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last(); + let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }).collect(); + if types.len() == 1; + then { + snippet_opt(cx, types[0].span) + } else { + None + } + } +} + fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { if let TyKind::Rptr(ref lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index 35b38eca14d..1fc4ff5c2e6 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -40,7 +40,7 @@ impl EarlyLintPass for SingleComponentPathImports { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if_chain! { if !in_macro(item.span); - if cx.sess.opts.edition == Edition::Edition2018; + if cx.sess.opts.edition >= Edition::Edition2018; if !item.vis.kind.is_pub(); if let ItemKind::Use(use_tree) = &item.kind; if let segments = &use_tree.prefix.segments; diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs index f61fd2ecd73..a31cd5fda84 100644 --- a/src/tools/clippy/clippy_lints/src/unused_unit.rs +++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs @@ -120,7 +120,7 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - position_before_rarrow(fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { ( #[allow(clippy::cast_possible_truncation)] ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs index 940573e4caa..eac5d0aa3ee 100644 --- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs +++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs @@ -501,8 +501,18 @@ pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool { && match (&l.kind, &r.kind) { (Lifetime, Lifetime) => true, (Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)), - (Const { ty: lt, kw_span: _ , default: ld}, Const { ty: rt, kw_span: _, default: rd }) => - eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)), + ( + Const { + ty: lt, + kw_span: _, + default: ld, + }, + Const { + ty: rt, + kw_span: _, + default: rd, + }, + ) => eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)), _ => false, } && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) @@ -546,7 +556,7 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool { match (l, r) { (Empty, Empty) => true, (Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts), - (Eq(_, lts), Eq(_, rts)) => lts.eq_unspanned(rts), + (Eq(_, lt), Eq(_, rt)) => lt.kind == rt.kind, _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index 424856090f2..1c68e837c4a 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -788,8 +788,7 @@ pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> { /// fn into3(self) -> () {} /// ^ /// ``` -#[allow(clippy::needless_pass_by_value)] -pub fn position_before_rarrow(s: String) -> Option<usize> { +pub fn position_before_rarrow(s: &str) -> Option<usize> { s.rfind("->").map(|rpos| { let mut rpos = rpos; let chars: Vec<char> = s.chars().collect(); diff --git a/src/tools/clippy/clippy_lints/src/utils/ptr.rs b/src/tools/clippy/clippy_lints/src/utils/ptr.rs index bd2c619f000..b330f3d890e 100644 --- a/src/tools/clippy/clippy_lints/src/utils/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/utils/ptr.rs @@ -72,7 +72,6 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> { } } } - return; } walk_expr(self, expr); } diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index d2e84132f4e..c579beeae89 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2020-12-20" +channel = "nightly-2021-01-02" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs index f985a15eda2..18324823468 100644 --- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -84,3 +84,13 @@ macro_rules! as_conv { 0u32 as u64 }; } + +#[macro_export] +macro_rules! large_enum_variant { + () => { + enum LargeEnumInMacro { + A(i32), + B([i32; 8000]), + } + }; +} diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.rs b/src/tools/clippy/tests/ui/field_reassign_with_default.rs index 79a30c22f95..3e0921022b4 100644 --- a/src/tools/clippy/tests/ui/field_reassign_with_default.rs +++ b/src/tools/clippy/tests/ui/field_reassign_with_default.rs @@ -107,4 +107,16 @@ fn main() { x.i = side_effect.next(); x.j = 2; x.i = side_effect.next(); + + // don't lint - some private fields + let mut x = m::F::default(); + x.a = 1; +} + +mod m { + #[derive(Default)] + pub struct F { + pub a: u64, + b: u64, + } } diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr index c788ebae552..9a2bc778c3f 100644 --- a/src/tools/clippy/tests/ui/field_reassign_with_default.stderr +++ b/src/tools/clippy/tests/ui/field_reassign_with_default.stderr @@ -53,7 +53,7 @@ error: field assignment outside of initializer for an instance created with Defa LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A::default()` and removing relevant reassignments +note: consider initializing the variable with `A { i: Default::default(), ..Default::default() }` and removing relevant reassignments --> $DIR/field_reassign_with_default.rs:90:5 | LL | let mut a: A = Default::default(); @@ -65,7 +65,7 @@ error: field assignment outside of initializer for an instance created with Defa LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A { j: 45, ..Default::default() }` and removing relevant reassignments +note: consider initializing the variable with `A { i: Default::default(), j: 45 }` and removing relevant reassignments --> $DIR/field_reassign_with_default.rs:94:5 | LL | let mut a: A = Default::default(); diff --git a/src/tools/clippy/tests/ui/from_over_into.rs b/src/tools/clippy/tests/ui/from_over_into.rs new file mode 100644 index 00000000000..292d0924fb1 --- /dev/null +++ b/src/tools/clippy/tests/ui/from_over_into.rs @@ -0,0 +1,21 @@ +#![warn(clippy::from_over_into)] + +// this should throw an error +struct StringWrapper(String); + +impl Into<StringWrapper> for String { + fn into(self) -> StringWrapper { + StringWrapper(self) + } +} + +// this is fine +struct A(String); + +impl From<String> for A { + fn from(s: String) -> A { + A(s) + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/from_over_into.stderr b/src/tools/clippy/tests/ui/from_over_into.stderr new file mode 100644 index 00000000000..18f56f85432 --- /dev/null +++ b/src/tools/clippy/tests/ui/from_over_into.stderr @@ -0,0 +1,15 @@ +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true + --> $DIR/from_over_into.rs:6:1 + | +LL | / impl Into<StringWrapper> for String { +LL | | fn into(self) -> StringWrapper { +LL | | StringWrapper(self) +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::from-over-into` implied by `-D warnings` + = help: consider to implement `From` instead + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/large_enum_variant.rs b/src/tools/clippy/tests/ui/large_enum_variant.rs index 852ef5fec0e..d22fee3f27b 100644 --- a/src/tools/clippy/tests/ui/large_enum_variant.rs +++ b/src/tools/clippy/tests/ui/large_enum_variant.rs @@ -1,7 +1,12 @@ +// aux-build:macro_rules.rs + #![allow(dead_code)] #![allow(unused_variables)] #![warn(clippy::large_enum_variant)] +#[macro_use] +extern crate macro_rules; + enum LargeEnum { A(i32), B([i32; 8000]), @@ -51,4 +56,6 @@ enum LargeEnumOk { LargeB([i32; 8001]), } -fn main() {} +fn main() { + large_enum_variant!(); +} diff --git a/src/tools/clippy/tests/ui/large_enum_variant.stderr b/src/tools/clippy/tests/ui/large_enum_variant.stderr index 8ce641a81f2..d39a4d462aa 100644 --- a/src/tools/clippy/tests/ui/large_enum_variant.stderr +++ b/src/tools/clippy/tests/ui/large_enum_variant.stderr @@ -1,12 +1,12 @@ error: large size difference between variants - --> $DIR/large_enum_variant.rs:7:5 + --> $DIR/large_enum_variant.rs:12:5 | LL | B([i32; 8000]), | ^^^^^^^^^^^^^^ this variant is 32000 bytes | = note: `-D clippy::large-enum-variant` implied by `-D warnings` note: and the second-largest variant is 4 bytes: - --> $DIR/large_enum_variant.rs:6:5 + --> $DIR/large_enum_variant.rs:11:5 | LL | A(i32), | ^^^^^^ @@ -16,13 +16,13 @@ LL | B(Box<[i32; 8000]>), | ^^^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:31:5 + --> $DIR/large_enum_variant.rs:36:5 | LL | ContainingLargeEnum(LargeEnum), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes | note: and the second-largest variant is 8 bytes: - --> $DIR/large_enum_variant.rs:30:5 + --> $DIR/large_enum_variant.rs:35:5 | LL | VariantOk(i32, u32), | ^^^^^^^^^^^^^^^^^^^ @@ -32,30 +32,30 @@ LL | ContainingLargeEnum(Box<LargeEnum>), | ^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:41:5 + --> $DIR/large_enum_variant.rs:46:5 | LL | StructLikeLarge { x: [i32; 8000], y: i32 }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes | note: and the second-largest variant is 8 bytes: - --> $DIR/large_enum_variant.rs:40:5 + --> $DIR/large_enum_variant.rs:45:5 | LL | VariantOk(i32, u32), | ^^^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:41:5 + --> $DIR/large_enum_variant.rs:46:5 | LL | StructLikeLarge { x: [i32; 8000], y: i32 }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:46:5 + --> $DIR/large_enum_variant.rs:51:5 | LL | StructLikeLarge2 { x: [i32; 8000] }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes | note: and the second-largest variant is 8 bytes: - --> $DIR/large_enum_variant.rs:45:5 + --> $DIR/large_enum_variant.rs:50:5 | LL | VariantOk(i32, u32), | ^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/map_err.stderr b/src/tools/clippy/tests/ui/map_err.stderr index 8ee2941790d..37e87e64de2 100644 --- a/src/tools/clippy/tests/ui/map_err.stderr +++ b/src/tools/clippy/tests/ui/map_err.stderr @@ -5,7 +5,7 @@ LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ | = note: `-D clippy::map-err-ignore` implied by `-D warnings` - = help: Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`) + = help: consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`) error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs index 3848bca3207..0f47f1cbc40 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs @@ -57,6 +57,14 @@ pub fn checked_conversion() { let _ = value <= (u32::MAX as i64) && value >= 0; } +pub struct FromOverInto(String); + +impl Into<FromOverInto> for String { + fn into(self) -> FromOverInto { + FromOverInto(self) + } +} + pub fn filter_map_next() { let a = ["1", "lol", "3", "NaN", "5"]; diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr index 34805263104..e3e3b335cbe 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:142:24 + --> $DIR/min_rust_version_attr.rs:150:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:141:9 + --> $DIR/min_rust_version_attr.rs:149:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL | assert_eq!(<stripped>.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:154:24 + --> $DIR/min_rust_version_attr.rs:162:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:153:9 + --> $DIR/min_rust_version_attr.rs:161:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs index 541225e6351..06370dfce65 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.rs +++ b/src/tools/clippy/tests/ui/ptr_arg.rs @@ -2,6 +2,7 @@ #![warn(clippy::ptr_arg)] use std::borrow::Cow; +use std::path::PathBuf; fn do_vec(x: &Vec<i64>) { //Nothing here @@ -21,6 +22,15 @@ fn do_str_mut(x: &mut String) { //Nothing here either } +fn do_path(x: &PathBuf) { + //Nothing here either +} + +fn do_path_mut(x: &mut PathBuf) { + // no error here + //Nothing here either +} + fn main() {} trait Foo { @@ -55,6 +65,14 @@ fn str_cloned(x: &String) -> String { x.clone() } +fn path_cloned(x: &PathBuf) -> PathBuf { + let a = x.clone(); + let b = x.clone(); + let c = b.clone(); + let d = a.clone().clone().clone(); + x.clone() +} + fn false_positive_capacity(x: &Vec<u8>, y: &String) { let a = x.capacity(); let b = y.clone(); @@ -87,10 +105,12 @@ impl Foo2 for String { // Check that the allow attribute on parameters is honored mod issue_5644 { use std::borrow::Cow; + use std::path::PathBuf; fn allowed( #[allow(clippy::ptr_arg)] _v: &Vec<u32>, #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _p: &PathBuf, #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, ) { } @@ -100,6 +120,7 @@ mod issue_5644 { fn allowed( #[allow(clippy::ptr_arg)] _v: &Vec<u32>, #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _p: &PathBuf, #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, ) { } @@ -109,8 +130,28 @@ mod issue_5644 { fn allowed( #[allow(clippy::ptr_arg)] _v: &Vec<u32>, #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _p: &PathBuf, #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, ) { } } } + +mod issue6509 { + use std::path::PathBuf; + + fn foo_vec(vec: &Vec<u8>) { + let _ = vec.clone().pop(); + let _ = vec.clone().clone(); + } + + fn foo_path(path: &PathBuf) { + let _ = path.clone().pop(); + let _ = path.clone().clone(); + } + + fn foo_str(str: &PathBuf) { + let _ = str.clone().pop(); + let _ = str.clone().clone(); + } +} diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr index 314f23497f9..708318bbe29 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.stderr +++ b/src/tools/clippy/tests/ui/ptr_arg.stderr @@ -1,5 +1,5 @@ error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. - --> $DIR/ptr_arg.rs:6:14 + --> $DIR/ptr_arg.rs:7:14 | LL | fn do_vec(x: &Vec<i64>) { | ^^^^^^^^^ help: change this to: `&[i64]` @@ -7,19 +7,25 @@ LL | fn do_vec(x: &Vec<i64>) { = note: `-D clippy::ptr-arg` implied by `-D warnings` error: writing `&String` instead of `&str` involves a new object where a slice will do. - --> $DIR/ptr_arg.rs:15:14 + --> $DIR/ptr_arg.rs:16:14 | LL | fn do_str(x: &String) { | ^^^^^^^ help: change this to: `&str` +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:25:15 + | +LL | fn do_path(x: &PathBuf) { + | ^^^^^^^^ help: change this to: `&Path` + error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. - --> $DIR/ptr_arg.rs:28:18 + --> $DIR/ptr_arg.rs:38:18 | LL | fn do_vec(x: &Vec<i64>); | ^^^^^^^^^ help: change this to: `&[i64]` error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. - --> $DIR/ptr_arg.rs:41:14 + --> $DIR/ptr_arg.rs:51:14 | LL | fn cloned(x: &Vec<u8>) -> Vec<u8> { | ^^^^^^^^ @@ -38,7 +44,7 @@ LL | x.to_owned() | error: writing `&String` instead of `&str` involves a new object where a slice will do. - --> $DIR/ptr_arg.rs:50:18 + --> $DIR/ptr_arg.rs:60:18 | LL | fn str_cloned(x: &String) -> String { | ^^^^^^^ @@ -60,8 +66,31 @@ help: change `x.clone()` to LL | x.to_string() | +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:68:19 + | +LL | fn path_cloned(x: &PathBuf) -> PathBuf { + | ^^^^^^^^ + | +help: change this to + | +LL | fn path_cloned(x: &Path) -> PathBuf { + | ^^^^^ +help: change `x.clone()` to + | +LL | let a = x.to_path_buf(); + | ^^^^^^^^^^^^^^^ +help: change `x.clone()` to + | +LL | let b = x.to_path_buf(); + | ^^^^^^^^^^^^^^^ +help: change `x.clone()` to + | +LL | x.to_path_buf() + | + error: writing `&String` instead of `&str` involves a new object where a slice will do. - --> $DIR/ptr_arg.rs:58:44 + --> $DIR/ptr_arg.rs:76:44 | LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) { | ^^^^^^^ @@ -80,10 +109,67 @@ LL | let c = y; | ^ error: using a reference to `Cow` is not recommended. - --> $DIR/ptr_arg.rs:72:25 + --> $DIR/ptr_arg.rs:90:25 | LL | fn test_cow_with_ref(c: &Cow<[i32]>) {} | ^^^^^^^^^^^ help: change this to: `&[i32]` -error: aborting due to 7 previous errors +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. + --> $DIR/ptr_arg.rs:143:21 + | +LL | fn foo_vec(vec: &Vec<u8>) { + | ^^^^^^^^ + | +help: change this to + | +LL | fn foo_vec(vec: &[u8]) { + | ^^^^^ +help: change `vec.clone()` to + | +LL | let _ = vec.to_owned().pop(); + | ^^^^^^^^^^^^^^ +help: change `vec.clone()` to + | +LL | let _ = vec.to_owned().clone(); + | ^^^^^^^^^^^^^^ + +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:148:23 + | +LL | fn foo_path(path: &PathBuf) { + | ^^^^^^^^ + | +help: change this to + | +LL | fn foo_path(path: &Path) { + | ^^^^^ +help: change `path.clone()` to + | +LL | let _ = path.to_path_buf().pop(); + | ^^^^^^^^^^^^^^^^^^ +help: change `path.clone()` to + | +LL | let _ = path.to_path_buf().clone(); + | ^^^^^^^^^^^^^^^^^^ + +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:153:21 + | +LL | fn foo_str(str: &PathBuf) { + | ^^^^^^^^ + | +help: change this to + | +LL | fn foo_str(str: &Path) { + | ^^^^^ +help: change `str.clone()` to + | +LL | let _ = str.to_path_buf().pop(); + | ^^^^^^^^^^^^^^^^^ +help: change `str.clone()` to + | +LL | let _ = str.to_path_buf().clone(); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/unused_unit.fixed b/src/tools/clippy/tests/ui/unused_unit.fixed index 7afc5361356..a192ebde3eb 100644 --- a/src/tools/clippy/tests/ui/unused_unit.fixed +++ b/src/tools/clippy/tests/ui/unused_unit.fixed @@ -11,6 +11,7 @@ #![deny(clippy::unused_unit)] #![allow(dead_code)] +#![allow(clippy::from_over_into)] struct Unitter; impl Unitter { diff --git a/src/tools/clippy/tests/ui/unused_unit.rs b/src/tools/clippy/tests/ui/unused_unit.rs index 96cef1ed5a5..96041a7dd85 100644 --- a/src/tools/clippy/tests/ui/unused_unit.rs +++ b/src/tools/clippy/tests/ui/unused_unit.rs @@ -11,6 +11,7 @@ #![deny(clippy::unused_unit)] #![allow(dead_code)] +#![allow(clippy::from_over_into)] struct Unitter; impl Unitter { diff --git a/src/tools/clippy/tests/ui/unused_unit.stderr b/src/tools/clippy/tests/ui/unused_unit.stderr index c45634c2b6d..02038b5fb6b 100644 --- a/src/tools/clippy/tests/ui/unused_unit.stderr +++ b/src/tools/clippy/tests/ui/unused_unit.stderr @@ -1,5 +1,5 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:18:28 + --> $DIR/unused_unit.rs:19:28 | LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` @@ -11,109 +11,109 @@ LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:18 + --> $DIR/unused_unit.rs:20:18 | LL | where G: Fn() -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:18:58 + --> $DIR/unused_unit.rs:19:58 | LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:20:26 + --> $DIR/unused_unit.rs:21:26 | LL | let _y: &dyn Fn() -> () = &f; | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:27:18 + --> $DIR/unused_unit.rs:28:18 | LL | fn into(self) -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:28:9 + --> $DIR/unused_unit.rs:29:9 | LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:33:29 + --> $DIR/unused_unit.rs:34:29 | LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H) | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:35:19 + --> $DIR/unused_unit.rs:36:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:36:16 + --> $DIR/unused_unit.rs:37:16 | LL | H: Fn() -> (); | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:40:29 + --> $DIR/unused_unit.rs:41:29 | LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H) | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:42:19 + --> $DIR/unused_unit.rs:43:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:43:16 + --> $DIR/unused_unit.rs:44:16 | LL | H: Fn() -> () {} | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:46:17 + --> $DIR/unused_unit.rs:47:17 | LL | fn return_unit() -> () { () } | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:46:26 + --> $DIR/unused_unit.rs:47:26 | LL | fn return_unit() -> () { () } | ^^ help: remove the final `()` error: unneeded `()` - --> $DIR/unused_unit.rs:56:14 + --> $DIR/unused_unit.rs:57:14 | LL | break(); | ^^ help: remove the `()` error: unneeded `()` - --> $DIR/unused_unit.rs:58:11 + --> $DIR/unused_unit.rs:59:11 | LL | return(); | ^^ help: remove the `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:75:10 + --> $DIR/unused_unit.rs:76:10 | LL | fn test()->(){} | ^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:78:11 + --> $DIR/unused_unit.rs:79:11 | LL | fn test2() ->(){} | ^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:81:11 + --> $DIR/unused_unit.rs:82:11 | LL | fn test3()-> (){} | ^^^^^ help: remove the `-> ()` diff --git a/src/tools/clippy/tests/versioncheck.rs b/src/tools/clippy/tests/versioncheck.rs index f5d03c645df..76b6126c76c 100644 --- a/src/tools/clippy/tests/versioncheck.rs +++ b/src/tools/clippy/tests/versioncheck.rs @@ -1,3 +1,6 @@ +#![allow(clippy::single_match_else)] +use rustc_tools_util::VersionInfo; + #[test] fn check_that_clippy_lints_has_the_same_version_as_clippy() { let clippy_meta = cargo_metadata::MetadataCommand::new() @@ -17,3 +20,54 @@ fn check_that_clippy_lints_has_the_same_version_as_clippy() { } } } + +#[test] +fn check_that_clippy_has_the_same_major_version_as_rustc() { + let clippy_version = rustc_tools_util::get_version_info!(); + let clippy_major = clippy_version.major; + let clippy_minor = clippy_version.minor; + let clippy_patch = clippy_version.patch; + + // get the rustc version either from the rustc installed with the toolchain file or from + // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`. + let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string()); + let rustc_version = String::from_utf8( + std::process::Command::new(&rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`") + .stdout, + ) + .unwrap(); + // extract "1 XX 0" from "rustc 1.XX.0-nightly (<commit> <date>)" + let vsplit: Vec<&str> = rustc_version + .split(' ') + .nth(1) + .unwrap() + .split('-') + .next() + .unwrap() + .split('.') + .collect(); + match vsplit.as_slice() { + [rustc_major, rustc_minor, _rustc_patch] => { + // clippy 0.1.XX should correspond to rustc 1.XX.0 + assert_eq!(clippy_major, 0); // this will probably stay the same for a long time + assert_eq!( + clippy_minor.to_string(), + *rustc_major, + "clippy minor version does not equal rustc major version" + ); + assert_eq!( + clippy_patch.to_string(), + *rustc_minor, + "clippy patch version does not equal rustc minor version" + ); + // do not check rustc_patch because when a stable-patch-release is made (like 1.50.2), + // we don't want our tests failing suddenly + }, + _ => { + panic!("Failed to parse rustc version: {:?}", vsplit); + }, + }; +} diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html index 428708136cb..1852fb6640e 100644 --- a/src/tools/clippy/util/gh-pages/index.html +++ b/src/tools/clippy/util/gh-pages/index.html @@ -77,7 +77,7 @@ <div class="col-md-12 form-horizontal"> <div class="input-group"> <label class="input-group-addon" id="filter-label" for="filter-input">Filter:</label> - <input type="text" class="form-control" placeholder="Keywords or search string" id="filter-input" ng-model="search" /> + <input type="text" class="form-control" placeholder="Keywords or search string" id="filter-input" ng-model="search" ng-model-options="{debounce: 50}"/> <span class="input-group-btn"> <button class="btn btn-default" type="button" ng-click="search = ''"> Clear @@ -119,6 +119,7 @@ {{title}} </h4> <div class="list-group-item-text" ng-bind-html="text | markdown"></div> + <a ng-if="title == 'Known problems'" href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+{{lint.id}}">Search on GitHub</a> </li> </ul> </article> @@ -180,6 +181,22 @@ } } + function searchLint(lint, term) { + for (const field in lint.docs) { + // Continue if it's not a property + if (!lint.docs.hasOwnProperty(field)) { + continue; + } + + // Return if not found + if (lint.docs[field].toLowerCase().indexOf(term) !== -1) { + return true; + } + } + + return false; + } + angular.module("clippy", []) .filter('markdown', function ($sce) { return function (text) { @@ -216,40 +233,31 @@ }; $scope.bySearch = function (lint, index, array) { - let search_str = $scope.search; + let searchStr = $scope.search; // It can be `null` I haven't missed this value - if (search_str == null || search_str.length == 0) { + if (searchStr == null || searchStr.length < 3) { return true; } - search_str = search_str.toLowerCase(); + searchStr = searchStr.toLowerCase(); // Search by id - let id_search = search_str.trim().replace(/(\-| )/g, "_"); - if (lint.id.includes(id_search)) { + if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) { return true; } // Search the description // The use of `for`-loops instead of `foreach` enables us to return early - let search_lint = (lint, therm) => { - for (const field in lint.docs) { - // Continue if it's not a property - if (!lint.docs.hasOwnProperty(field)) { - continue; - } - - // Return if not found - if (lint.docs[field].toLowerCase().includes(therm)) { - return true; - } + let terms = searchStr.split(" "); + for (index = 0; index < terms.length; index++) { + if (lint.id.indexOf(terms[index]) !== -1) { + continue; } - return false; - }; - let therms = search_str.split(" "); - for (index = 0; index < therms.length; index++) { - if (!search_lint(lint, therms[index])) { - return false; + + if (searchLint(lint, terms[index])) { + continue; } + + return false; } return true; diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 4f77e719fba..1647df8044c 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -82,6 +82,7 @@ const ARCH_TABLE: &[(&str, &str)] = &[ ]; pub const ASAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-apple-darwin", "aarch64-fuchsia", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", @@ -90,13 +91,18 @@ pub const ASAN_SUPPORTED_TARGETS: &[&str] = &[ "x86_64-unknown-linux-gnu", ]; -pub const LSAN_SUPPORTED_TARGETS: &[&str] = - &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]; +pub const LSAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu", +]; pub const MSAN_SUPPORTED_TARGETS: &[&str] = &["aarch64-unknown-linux-gnu", "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu"]; pub const TSAN_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-freebsd", diff --git a/src/tools/miri b/src/tools/miri -Subproject 2065b52dfef3cd5a5216e65c21a056a69574bdd +Subproject a09f8b0c06c6bb051bd1e104c6be56fbe51f3d8 diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml index 99310157a64..8e17f291908 100644 --- a/src/tools/rustbook/Cargo.toml +++ b/src/tools/rustbook/Cargo.toml @@ -10,6 +10,6 @@ clap = "2.25.0" env_logger = "0.7.1" [dependencies.mdbook] -version = "0.4.3" +version = "0.4.5" default-features = false features = ["search"] |
