diff options
860 files changed, 15099 insertions, 7117 deletions
diff --git a/Cargo.lock b/Cargo.lock index ef4800a2261..f5aacba20cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -448,9 +448,10 @@ dependencies = [ "lazy_static", "remove_dir_all", "serde_json", + "snapbox", "tar", "termcolor", - "toml_edit 0.13.4", + "toml_edit 0.14.3", "url 2.2.2", ] @@ -654,6 +655,7 @@ dependencies = [ name = "clippy_dev" version = "0.0.1" dependencies = [ + "aho-corasick", "clap 2.34.0", "indoc", "itertools", @@ -827,6 +829,7 @@ name = "core" version = "0.0.0" dependencies = [ "rand 0.7.3", + "rand_xorshift", ] [[package]] @@ -2070,9 +2073,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.121" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" dependencies = [ "rustc-std-workspace-core", ] @@ -4171,7 +4174,6 @@ name = "rustc_parse_format" version = "0.0.0" dependencies = [ "rustc_lexer", - "rustc_span", ] [[package]] @@ -4183,6 +4185,7 @@ dependencies = [ "rustc_attr", "rustc_data_structures", "rustc_errors", + "rustc_expand", "rustc_feature", "rustc_hir", "rustc_index", @@ -4245,6 +4248,7 @@ dependencies = [ "rustc_serialize", "rustc_session", "rustc_span", + "tracing", ] [[package]] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 1a18d1964c9..e8cc9d9d291 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -23,7 +23,7 @@ pub use GenericArgs::*; pub use UnsafeSource::*; use crate::ptr::P; -use crate::token::{self, CommentKind, Delimiter, Token}; +use crate::token::{self, CommentKind, Delimiter, Token, TokenKind}; use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -573,7 +573,7 @@ pub struct Block { pub span: Span, pub tokens: Option<LazyTokenStream>, /// The following *isn't* a parse error, but will cause multiple errors in following stages. - /// ``` + /// ```compile_fail /// let x = { /// foo: var /// }; @@ -1532,7 +1532,7 @@ impl MacCall { } /// Arguments passed to an attribute or a function-like macro. -#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug)] pub enum MacArgs { /// No arguments - `#[attr]`. Empty, @@ -1542,11 +1542,20 @@ pub enum MacArgs { Eq( /// Span of the `=` token. Span, - /// "value" as a nonterminal token. - Token, + /// The "value". + MacArgsEq, ), } +// The RHS of a `MacArgs::Eq` starts out as an expression. Once macro expansion +// is completed, all cases end up either as a literal, which is the form used +// after lowering to HIR, or as an error. +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum MacArgsEq { + Ast(P<Expr>), + Hir(Lit), +} + impl MacArgs { pub fn delim(&self) -> Option<Delimiter> { match self { @@ -1559,7 +1568,10 @@ impl MacArgs { match self { MacArgs::Empty => None, MacArgs::Delimited(dspan, ..) => Some(dspan.entire()), - MacArgs::Eq(eq_span, token) => Some(eq_span.to(token.span)), + MacArgs::Eq(eq_span, MacArgsEq::Ast(expr)) => Some(eq_span.to(expr.span)), + MacArgs::Eq(_, MacArgsEq::Hir(lit)) => { + unreachable!("in literal form when getting span: {:?}", lit); + } } } @@ -1569,7 +1581,23 @@ impl MacArgs { match self { MacArgs::Empty => TokenStream::default(), MacArgs::Delimited(.., tokens) => tokens.clone(), - MacArgs::Eq(.., token) => TokenTree::Token(token.clone()).into(), + MacArgs::Eq(_, MacArgsEq::Ast(expr)) => { + // Currently only literals are allowed here. If more complex expression kinds are + // allowed in the future, then `nt_to_tokenstream` should be used to extract the + // token stream. This will require some cleverness, perhaps with a function + // pointer, because `nt_to_tokenstream` is not directly usable from this crate. + // It will also require changing the `parse_expr` call in `parse_mac_args_common` + // to `parse_expr_force_collect`. + if let ExprKind::Lit(lit) = &expr.kind { + let token = Token::new(TokenKind::Literal(lit.token), lit.span); + TokenTree::Token(token).into() + } else { + unreachable!("couldn't extract literal when getting inner tokens: {:?}", expr) + } + } + MacArgs::Eq(_, MacArgsEq::Hir(lit)) => { + unreachable!("in literal form when getting inner tokens: {:?}", lit) + } } } @@ -1580,6 +1608,30 @@ impl MacArgs { } } +impl<CTX> HashStable<CTX> for MacArgs +where + CTX: crate::HashStableContext, +{ + fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { + mem::discriminant(self).hash_stable(ctx, hasher); + match self { + MacArgs::Empty => {} + MacArgs::Delimited(dspan, delim, tokens) => { + dspan.hash_stable(ctx, hasher); + delim.hash_stable(ctx, hasher); + tokens.hash_stable(ctx, hasher); + } + MacArgs::Eq(_eq_span, MacArgsEq::Ast(expr)) => { + unreachable!("hash_stable {:?}", expr); + } + MacArgs::Eq(eq_span, MacArgsEq::Hir(lit)) => { + eq_span.hash_stable(ctx, hasher); + lit.hash_stable(ctx, hasher); + } + } + } +} + #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum MacDelimiter { Parenthesis, diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index b14367aa1c2..84654a9f737 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -3,14 +3,16 @@ use crate::ast; use crate::ast::{AttrId, AttrItem, AttrKind, AttrStyle, Attribute}; use crate::ast::{Lit, LitKind}; -use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem}; +use crate::ast::{MacArgs, MacArgsEq, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem}; use crate::ast::{Path, PathSegment}; +use crate::ptr::P; use crate::token::{self, CommentKind, Delimiter, Token}; use crate::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree}; use crate::tokenstream::{DelimSpan, Spacing, TokenTree, TreeAndSpacing}; use crate::tokenstream::{LazyTokenStream, TokenStream}; use crate::util::comments; +use rustc_data_structures::thin_vec::ThinVec; use rustc_index::bit_set::GrowableBitSet; use rustc_span::source_map::BytePos; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -475,7 +477,16 @@ impl MetaItemKind { pub fn mac_args(&self, span: Span) -> MacArgs { match self { MetaItemKind::Word => MacArgs::Empty, - MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.to_token()), + MetaItemKind::NameValue(lit) => { + let expr = P(ast::Expr { + id: ast::DUMMY_NODE_ID, + kind: ast::ExprKind::Lit(lit.clone()), + span: lit.span, + attrs: ThinVec::new(), + tokens: None, + }); + MacArgs::Eq(span, MacArgsEq::Ast(expr)) + } MetaItemKind::List(list) => { let mut tts = Vec::new(); for (i, item) in list.iter().enumerate() { @@ -552,12 +563,16 @@ impl MetaItemKind { fn from_mac_args(args: &MacArgs) -> Option<MetaItemKind> { match args { + MacArgs::Empty => Some(MetaItemKind::Word), MacArgs::Delimited(_, MacDelimiter::Parenthesis, tokens) => { MetaItemKind::list_from_tokens(tokens.clone()) } MacArgs::Delimited(..) => None, - MacArgs::Eq(_, token) => Lit::from_token(token).ok().map(MetaItemKind::NameValue), - MacArgs::Empty => Some(MetaItemKind::Word), + MacArgs::Eq(_, MacArgsEq::Ast(expr)) => match &expr.kind { + ast::ExprKind::Lit(lit) => Some(MetaItemKind::NameValue(lit.clone())), + _ => None, + }, + MacArgs::Eq(_, MacArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())), } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 4bf3d483f73..b425b5e2cca 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -370,21 +370,12 @@ 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, token) => { + MacArgs::Eq(eq_span, MacArgsEq::Ast(expr)) => { vis.visit_span(eq_span); - if T::VISIT_TOKENS { - 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), - } - } + vis.visit_expr(expr); + } + MacArgs::Eq(_, MacArgsEq::Hir(lit)) => { + unreachable!("in literal form when visiting mac args eq: {:?}", lit) } } } @@ -738,7 +729,7 @@ pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) { } token::Interpolated(nt) => { let mut nt = Lrc::make_mut(nt); - visit_interpolated(&mut nt, vis); + visit_nonterminal(&mut nt, vis); } _ => {} } @@ -769,7 +760,7 @@ pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) { // contain multiple items, but decided against it when I looked at // `parse_item_or_view_item` and tried to figure out what I would do with // multiple items there.... -pub fn visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) { +pub fn visit_nonterminal<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) { match nt { token::NtItem(item) => visit_clobber(item, |item| { // This is probably okay, because the only visitors likely to diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 1589a882f08..35eca23a116 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -50,11 +50,12 @@ pub enum Delimiter { Brace, /// `[ ... ]` Bracket, - /// `Ø ... Ø` + /// `/*«*/ ... /*»*/` /// An invisible delimiter, that may, for example, appear around tokens coming from a /// "macro variable" `$var`. It is important to preserve operator priorities in cases like /// `$var * 3` where `$var` is `1 + 2`. - /// Invisible delimiters might not survive roundtrip of a token stream through a string. + /// Invisible delimiters are not directly writable in normal Rust code except as comments. + /// Therefore, they might not survive a roundtrip of a token stream through a string. Invisible, } @@ -237,6 +238,15 @@ pub enum TokenKind { /// treat regular and interpolated lifetime identifiers in the same way. Lifetime(Symbol), + /// An embedded AST node, as produced by a macro. This only exists for + /// historical reasons. We'd like to get rid of it, for multiple reasons. + /// - It's conceptually very strange. Saying a token can contain an AST + /// node is like saying, in natural language, that a word can contain a + /// sentence. + /// - It requires special handling in a bunch of places in the parser. + /// - It prevents `Token` from implementing `Copy`. + /// It adds complexity and likely slows things down. Please don't add new + /// occurrences of this token kind! Interpolated(Lrc<Nonterminal>), /// A doc comment token. @@ -475,19 +485,29 @@ impl Token { } /// Returns an identifier if this token is an identifier. + #[inline] pub fn ident(&self) -> Option<(Ident, /* is_raw */ bool)> { - let token = self.uninterpolate(); - match token.kind { - Ident(name, is_raw) => Some((Ident::new(name, token.span), is_raw)), + // We avoid using `Token::uninterpolate` here because it's slow. + match &self.kind { + &Ident(name, is_raw) => Some((Ident::new(name, self.span), is_raw)), + Interpolated(nt) => match **nt { + NtIdent(ident, is_raw) => Some((ident, is_raw)), + _ => None, + }, _ => None, } } /// Returns a lifetime identifier if this token is a lifetime. + #[inline] pub fn lifetime(&self) -> Option<Ident> { - let token = self.uninterpolate(); - match token.kind { - Lifetime(name) => Some(Ident::new(name, token.span)), + // We avoid using `Token::uninterpolate` here because it's slow. + match &self.kind { + &Lifetime(name) => Some(Ident::new(name, self.span)), + Interpolated(nt) => match **nt { + NtLifetime(ident) => Some(ident), + _ => None, + }, _ => None, } } @@ -521,7 +541,7 @@ impl Token { /// (which happens while parsing the result of macro expansion)? pub fn is_whole_expr(&self) -> bool { if let Interpolated(ref nt) = self.kind - && let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtIdent(..) | NtBlock(_) = **nt + && let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = **nt { return true; } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index fa26716083f..cc772ac74f2 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -14,7 +14,6 @@ //! those that are created by the expansion of a macro. use crate::ast::*; -use crate::token; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -940,14 +939,9 @@ pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) { match args { MacArgs::Empty => {} 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, 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), - }, + MacArgs::Eq(_eq_span, MacArgsEq::Ast(expr)) => visitor.visit_expr(expr), + MacArgs::Eq(_, MacArgsEq::Hir(lit)) => { + unreachable!("in literal form when walking mac args eq: {:?}", lit) + } } } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 5c3e3be2116..410ed3f0bdc 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -616,7 +616,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } /// Desugar `<expr>.await` into: - /// ```rust + /// ```ignore (pseudo-rust) /// match ::std::future::IntoFuture::into_future(<expr>) { /// mut __awaitee => loop { /// match unsafe { ::std::future::Future::poll( @@ -1020,6 +1020,28 @@ impl<'hir> LoweringContext<'_, 'hir> { None } + /// If the given expression is a path to a unit struct, returns that path. + /// It is not a complete check, but just tries to reject most paths early + /// if they are not unit structs. + /// Type checking will take care of the full validation later. + fn extract_unit_struct_path<'a>( + &mut self, + expr: &'a Expr, + ) -> Option<(&'a Option<QSelf>, &'a Path)> { + if let ExprKind::Path(qself, path) = &expr.kind { + // Does the path resolve to something disallowed in a unit struct/variant pattern? + if let Some(partial_res) = self.resolver.get_partial_res(expr.id) { + if partial_res.unresolved_segments() == 0 + && !partial_res.base_res().expected_in_unit_struct_pat() + { + return None; + } + } + return Some((qself, path)); + } + None + } + /// Convert the LHS of a destructuring assignment to a pattern. /// Each sub-assignment is recorded in `assignments`. fn destructure_assign( @@ -1080,6 +1102,21 @@ impl<'hir> LoweringContext<'_, 'hir> { return self.pat_without_dbm(lhs.span, tuple_struct_pat); } } + // Unit structs and enum variants. + ExprKind::Path(..) => { + if let Some((qself, path)) = self.extract_unit_struct_path(lhs) { + let qpath = self.lower_qpath( + lhs.id, + qself, + path, + ParamMode::Optional, + ImplTraitContext::Disallowed(ImplTraitPosition::Path), + ); + // Destructure like a unit struct. + let unit_struct_pat = hir::PatKind::Path(qpath); + return self.pat_without_dbm(lhs.span, unit_struct_pat); + } + } // Structs. ExprKind::Struct(se) => { let field_pats = self.arena.alloc_from_iter(se.fields.iter().map(|f| { @@ -1326,7 +1363,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } /// Desugar `ExprForLoop` from: `[opt_ident]: for <pat> in <head> <body>` into: - /// ```rust + /// ```ignore (pseudo-rust) /// { /// let result = match IntoIterator::into_iter(<head>) { /// mut iter => { @@ -1437,7 +1474,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } /// Desugar `ExprKind::Try` from: `<expr>?` into: - /// ```rust + /// ```ignore (pseudo-rust) /// match Try::branch(<expr>) { /// ControlFlow::Continue(val) => #[allow(unreachable_code)] val,, /// ControlFlow::Break(residual) => diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d433775f85c..54b081085cd 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -38,8 +38,7 @@ #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] -use rustc_ast::token::{Delimiter, Token}; -use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree}; +use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream}; use rustc_ast::visit; use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust; @@ -884,37 +883,24 @@ 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 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 != Delimiter::Invisible { - sess.diagnostic().delay_span_bug( - span, - "unexpected delimiter in key-value attribute's value", - ); - } - unwrap_single_token(sess, tokens, span) - } - None => Token::dummy(), + // after it gets lowered to HIR. Therefore, we can extract literals to handle + // nonterminals in `#[doc]` (e.g. `#[doc = $e]`). + MacArgs::Eq(eq_span, MacArgsEq::Ast(ref expr)) => { + // In valid code the value always ends up as a single literal. Otherwise, a dummy + // literal suffices because the error is handled elsewhere. + let lit = if let ExprKind::Lit(lit) = &expr.kind { + lit.clone() + } else { + Lit { + token: token::Lit::new(token::LitKind::Err, kw::Empty, None), + kind: LitKind::Err(kw::Empty), + span: DUMMY_SP, } - } - - let tokens = FlattenNonterminals { - parse_sess: &self.sess.parse_sess, - synthesize_tokens: CanSynthesizeMissingTokens::Yes, - nt_to_tokenstream: self.nt_to_tokenstream, - } - .process_token(token.clone()); - MacArgs::Eq(eq_span, unwrap_single_token(self.sess, tokens, token.span)) + }; + MacArgs::Eq(eq_span, MacArgsEq::Hir(lit)) + } + MacArgs::Eq(_, MacArgsEq::Hir(ref lit)) => { + unreachable!("in literal form when lowering mac args eq: {:?}", lit) } } } @@ -934,7 +920,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// Given an associated type constraint like one of these: /// - /// ``` + /// ```ignore (illustrative) /// T: Iterator<Item: Debug> /// ^^^^^^^^^^^ /// T: Iterator<Item = Debug> diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs index 6bd543ff150..517ab30b2a4 100644 --- a/compiler/rustc_ast_pretty/src/lib.rs +++ b/compiler/rustc_ast_pretty/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(box_patterns)] #![recursion_limit = "256"] diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs index ddce86f2165..c93022308a3 100644 --- a/compiler/rustc_ast_pretty/src/pp.rs +++ b/compiler/rustc_ast_pretty/src/pp.rs @@ -68,20 +68,20 @@ //! will be made to flow subsequent breaks together onto lines. Inconsistent //! is the opposite. Inconsistent breaking example would be, say: //! -//! ``` +//! ```ignore (illustrative) //! foo(hello, there, good, friends) //! ``` //! //! breaking inconsistently to become //! -//! ``` +//! ```ignore (illustrative) //! foo(hello, there, //! good, friends); //! ``` //! //! whereas a consistent breaking would yield: //! -//! ``` +//! ```ignore (illustrative) //! foo(hello, //! there, //! good, @@ -153,14 +153,14 @@ enum IndentStyle { /// Vertically aligned under whatever column this block begins at. /// /// fn demo(arg1: usize, - /// arg2: usize); + /// arg2: usize) {} Visual, /// Indented relative to the indentation level of the previous line. /// /// fn demo( /// arg1: usize, /// arg2: usize, - /// ); + /// ) {} Block { offset: isize }, } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a2ebe3048ce..c02cdc29561 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -13,7 +13,7 @@ use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle}; use rustc_ast::util::parser; use rustc_ast::{self as ast, BlockCheckMode, PatKind, RangeEnd, RangeSyntax}; use rustc_ast::{attr, Term}; -use rustc_ast::{GenericArg, MacArgs}; +use rustc_ast::{GenericArg, MacArgs, MacArgsEq}; use rustc_ast::{GenericBound, SelfKind, TraitBoundModifier}; use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; @@ -469,14 +469,22 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere true, span, ), - MacArgs::Empty | MacArgs::Eq(..) => { + MacArgs::Empty => { self.print_path(&item.path, false, 0); - if let MacArgs::Eq(_, token) = &item.args { - self.space(); - self.word_space("="); - let token_str = self.token_to_string_ext(token, true); - self.word(token_str); - } + } + MacArgs::Eq(_, MacArgsEq::Ast(expr)) => { + self.print_path(&item.path, false, 0); + self.space(); + self.word_space("="); + let token_str = self.expr_to_string(expr); + self.word(token_str); + } + MacArgs::Eq(_, MacArgsEq::Hir(lit)) => { + self.print_path(&item.path, false, 0); + self.space(); + self.word_space("="); + let token_str = self.literal_to_string(lit); + self.word(token_str); } } self.end(); @@ -582,15 +590,29 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere self.nbsp(); } self.word("{"); - if !tts.is_empty() { + let empty = tts.is_empty(); + if !empty { self.space(); } self.ibox(0); self.print_tts(tts, convert_dollar_crate); self.end(); - let empty = tts.is_empty(); self.bclose(span, empty); } + Some(Delimiter::Invisible) => { + self.word("/*«*/"); + let empty = tts.is_empty(); + if !empty { + self.space(); + } + self.ibox(0); + self.print_tts(tts, convert_dollar_crate); + self.end(); + if !empty { + self.space(); + } + self.word("/*»*/"); + } Some(delim) => { let token_str = self.token_kind_to_string(&token::OpenDelim(delim)); self.word(token_str); @@ -764,9 +786,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere token::CloseDelim(Delimiter::Bracket) => "]".into(), token::OpenDelim(Delimiter::Brace) => "{".into(), token::CloseDelim(Delimiter::Brace) => "}".into(), - token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) => { - "".into() - } + token::OpenDelim(Delimiter::Invisible) => "/*«*/".into(), + token::CloseDelim(Delimiter::Invisible) => "/*»*/".into(), token::Pound => "#".into(), token::Dollar => "$".into(), token::Question => "?".into(), @@ -817,6 +838,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere Self::to_string(|s| s.print_expr(e)) } + fn literal_to_string(&self, lit: &ast::Lit) -> String { + Self::to_string(|s| s.print_literal(lit)) + } + fn tt_to_string(&self, tt: &TokenTree) -> String { Self::to_string(|s| s.print_tt(tt, false)) } diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 4a9904891ec..7c921b85058 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -167,10 +167,6 @@ impl<'tcx> BorrowSet<'tcx> { crate fn get_index_of(&self, location: &Location) -> Option<BorrowIndex> { self.location_map.get_index_of(location).map(BorrowIndex::from) } - - crate fn contains(&self, location: &Location) -> bool { - self.location_map.contains_key(location) - } } struct GatherBorrows<'a, 'tcx> { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 3b4e9e95b0e..bbb631730d3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -896,20 +896,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is /// attempted while a shared borrow is live, then this function will return: - /// - /// ("x", "", "") - /// + /// ``` + /// ("x", "", "") + /// # ; + /// ``` /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while /// a shared borrow of another field `x.y`, then this function will return: - /// - /// ("x", "x.z", "x.y") - /// + /// ``` + /// ("x", "x.z", "x.y") + /// # ; + /// ``` /// In the more complex union case, where the union is a field of a struct, then if a mutable /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of /// another field `x.u.y`, then this function will return: - /// - /// ("x.u", "x.u.z", "x.u.y") - /// + /// ``` + /// ("x.u", "x.u.z", "x.u.y") + /// # ; + /// ``` /// This is used when creating error messages like below: /// /// ```text diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 368c0be794b..05d29503180 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1,5 +1,6 @@ //! Borrow checker diagnostics. +use itertools::Itertools; use rustc_const_eval::util::{call_kind, CallDesugaringKind}; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; @@ -161,158 +162,103 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } /// End-user visible description of `place` if one can be found. - /// If the place is a temporary for instance, None will be returned. + /// If the place is a temporary for instance, `None` will be returned. pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> { self.describe_place_with_options(place_ref, IncludingDowncast(false)) } - /// End-user visible description of `place` if one can be found. If the - /// place is a temporary for instance, None will be returned. - /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is + /// End-user visible description of `place` if one can be found. If the place is a temporary + /// for instance, `None` will be returned. + /// `IncludingDowncast` parameter makes the function return `None` if `ProjectionElem` is /// `Downcast` and `IncludingDowncast` is true pub(super) fn describe_place_with_options( &self, place: PlaceRef<'tcx>, including_downcast: IncludingDowncast, ) -> Option<String> { + let local = place.local; + let mut autoderef_index = None; let mut buf = String::new(); - match self.append_place_to_string(place, &mut buf, false, &including_downcast) { - Ok(()) => Some(buf), - Err(()) => None, - } - } - - /// Appends end-user visible description of `place` to `buf`. - fn append_place_to_string( - &self, - place: PlaceRef<'tcx>, - buf: &mut String, - mut autoderef: bool, - including_downcast: &IncludingDowncast, - ) -> Result<(), ()> { - match place { - PlaceRef { local, projection: [] } => { - self.append_local_to_string(local, buf)?; - } - PlaceRef { local, projection: [ProjectionElem::Deref] } - if self.body.local_decls[local].is_ref_for_guard() => - { - self.append_place_to_string( - PlaceRef { local, projection: &[] }, - buf, - autoderef, - &including_downcast, - )?; - } - PlaceRef { local, projection: [ProjectionElem::Deref] } - if self.body.local_decls[local].is_ref_to_static() => - { - let local_info = &self.body.local_decls[local].local_info; - if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info { - buf.push_str(self.infcx.tcx.item_name(def_id).as_str()); - } else { - unreachable!(); - } - } - PlaceRef { local, projection: [proj_base @ .., elem] } => { - match elem { - ProjectionElem::Deref => { - let upvar_field_projection = self.is_upvar_field_projection(place); - if let Some(field) = upvar_field_projection { - let var_index = field.index(); - let name = self.upvars[var_index].place.to_string(self.infcx.tcx); - if self.upvars[var_index].by_ref { - buf.push_str(&name); - } else { - buf.push('*'); - buf.push_str(&name); - } - } else { - if autoderef { - // FIXME turn this recursion into iteration - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - } else { - buf.push('*'); - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - } + let mut ok = self.append_local_to_string(local, &mut buf); + + for (index, elem) in place.projection.into_iter().enumerate() { + match elem { + ProjectionElem::Deref => { + if index == 0 { + if self.body.local_decls[local].is_ref_for_guard() { + continue; } - } - ProjectionElem::Downcast(..) => { - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - if including_downcast.0 { - return Err(()); + if let Some(box LocalInfo::StaticRef { def_id, .. }) = + &self.body.local_decls[local].local_info + { + buf.push_str(self.infcx.tcx.item_name(*def_id).as_str()); + ok = Ok(()); + continue; } } - ProjectionElem::Field(field, _ty) => { - autoderef = true; - - // FIXME(project-rfc_2229#36): print capture precisely here. - let upvar_field_projection = self.is_upvar_field_projection(place); - if let Some(field) = upvar_field_projection { - let var_index = field.index(); - let name = self.upvars[var_index].place.to_string(self.infcx.tcx); - buf.push_str(&name); - } else { - let field_name = self - .describe_field(PlaceRef { local, projection: proj_base }, *field); - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - buf.push('.'); - buf.push_str(&field_name); + if let Some(field) = self.is_upvar_field_projection(PlaceRef { + local, + projection: place.projection.split_at(index + 1).0, + }) { + let var_index = field.index(); + buf = self.upvars[var_index].place.to_string(self.infcx.tcx); + ok = Ok(()); + if !self.upvars[var_index].by_ref { + buf.insert(0, '*'); } - } - ProjectionElem::Index(index) => { - autoderef = true; - - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - buf.push('['); - if self.append_local_to_string(*index, buf).is_err() { - buf.push('_'); + } else { + if autoderef_index.is_none() { + autoderef_index = + match place.projection.into_iter().rev().find_position(|elem| { + !matches!( + elem, + ProjectionElem::Deref | ProjectionElem::Downcast(..) + ) + }) { + Some((index, _)) => Some(place.projection.len() - index), + None => Some(0), + }; + } + if index >= autoderef_index.unwrap() { + buf.insert(0, '*'); } - buf.push(']'); } - ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { - autoderef = true; - // Since it isn't possible to borrow an element on a particular index and - // then use another while the borrow is held, don't output indices details - // to avoid confusing the end-user - self.append_place_to_string( - PlaceRef { local, projection: proj_base }, - buf, - autoderef, - &including_downcast, - )?; - buf.push_str("[..]"); + } + ProjectionElem::Downcast(..) if including_downcast.0 => return None, + ProjectionElem::Downcast(..) => (), + ProjectionElem::Field(field, _ty) => { + // FIXME(project-rfc_2229#36): print capture precisely here. + if let Some(field) = self.is_upvar_field_projection(PlaceRef { + local, + projection: place.projection.split_at(index + 1).0, + }) { + buf = self.upvars[field.index()].place.to_string(self.infcx.tcx); + ok = Ok(()); + } else { + let field_name = self.describe_field( + PlaceRef { local, projection: place.projection.split_at(index).0 }, + *field, + ); + buf.push('.'); + buf.push_str(&field_name); } - }; + } + ProjectionElem::Index(index) => { + buf.push('['); + if self.append_local_to_string(*index, &mut buf).is_err() { + buf.push('_'); + } + buf.push(']'); + } + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { + // Since it isn't possible to borrow an element on a particular index and + // then use another while the borrow is held, don't output indices details + // to avoid confusing the end-user + buf.push_str("[..]"); + } } } - - Ok(()) + ok.ok().map(|_| buf) } /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 30fe4ea8662..6f1c8daf42e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -259,7 +259,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// Report an error because the universal region `fr` was required to outlive /// `outlived_fr` but it is not known to do so. For example: /// - /// ``` + /// ```compile_fail,E0312 /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } /// ``` /// diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 459d4a783e4..c4cef5710ae 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -210,7 +210,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// Suppose we are trying to give a name to the lifetime of the /// reference `x`: /// - /// ``` + /// ```ignore (pseudo-rust) /// fn foo(x: &u32) { .. } /// ``` /// @@ -746,7 +746,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// e.g. given the function: /// /// ``` - /// async fn foo() -> i32 {} + /// async fn foo() -> i32 { 2 } /// ``` /// /// this function, given the lowered return type of `foo`, an [`OpaqueDef`] that implements `Future<Output=i32>`, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 0c0676f93ad..4661805bb1c 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1,7 +1,6 @@ //! This query borrow-checks the MIR to (further) ensure it is not broken. #![allow(rustc::potential_query_instability)] -#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(crate_visibility_modifier)] #![feature(let_chains)] @@ -35,14 +34,13 @@ use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, Stat use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt}; -use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; -use rustc_span::{Span, Symbol, DUMMY_SP}; +use rustc_session::lint::builtin::UNUSED_MUT; +use rustc_span::{Span, Symbol}; use either::Either; use smallvec::SmallVec; use std::cell::RefCell; use std::collections::BTreeMap; -use std::mem; use std::rc::Rc; use rustc_mir_dataflow::impls::{ @@ -313,7 +311,6 @@ fn do_mir_borrowck<'a, 'tcx>( locals_are_invalidated_at_exit, access_place_error_reported: Default::default(), reservation_error_reported: Default::default(), - reservation_warnings: Default::default(), uninitialized_error_reported: Default::default(), regioncx: regioncx.clone(), used_mut: Default::default(), @@ -345,7 +342,6 @@ fn do_mir_borrowck<'a, 'tcx>( fn_self_span_reported: Default::default(), access_place_error_reported: Default::default(), reservation_error_reported: Default::default(), - reservation_warnings: Default::default(), uninitialized_error_reported: Default::default(), regioncx: Rc::clone(®ioncx), used_mut: Default::default(), @@ -378,34 +374,6 @@ fn do_mir_borrowck<'a, 'tcx>( &mut mbcx, ); - // Convert any reservation warnings into lints. - let reservation_warnings = mem::take(&mut mbcx.reservation_warnings); - for (_, (place, span, location, bk, borrow)) in reservation_warnings { - let initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow); - - let scope = mbcx.body.source_info(location).scope; - let lint_root = match &mbcx.body.source_scopes[scope].local_data { - ClearCrossCrate::Set(data) => data.lint_root, - _ => tcx.hir().local_def_id_to_hir_id(def.did), - }; - - // Span and message don't matter; we overwrite them below anyway - mbcx.infcx.tcx.struct_span_lint_hir( - MUTABLE_BORROW_RESERVATION_CONFLICT, - lint_root, - DUMMY_SP, - |lint| { - let mut diag = lint.build(""); - - diag.message = initial_diag.styled_message().clone(); - diag.span = initial_diag.span.clone(); - - mbcx.buffer_non_error_diag(diag); - }, - ); - initial_diag.cancel(); - } - // For each non-user used mutable variable, check if it's been assigned from // a user-declared local. If so, then put that local into the used_mut set. // Note that this set is expected to be small - only upvars from closures @@ -540,11 +508,6 @@ struct MirBorrowckCtxt<'cx, 'tcx> { /// used to report extra information for `FnSelfUse`, to avoid /// unnecessarily verbose errors. fn_self_span_reported: FxHashSet<Span>, - /// Migration warnings to be reported for #56254. We delay reporting these - /// so that we can suppress the warning if there's a corresponding error - /// for the activation of the borrow. - reservation_warnings: - FxHashMap<BorrowIndex, (Place<'tcx>, Span, Location, BorrowKind, BorrowData<'tcx>)>, /// This field keeps track of errors reported in the checking of uninitialized variables, /// so that we don't report seemingly duplicate errors. uninitialized_error_reported: FxHashSet<PlaceRef<'tcx>>, @@ -996,12 +959,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let conflict_error = self.check_access_for_conflict(location, place_span, sd, rw, flow_state); - if let (Activation(_, borrow_idx), true) = (kind.1, conflict_error) { - // Suppress this warning when there's an error being emitted for the - // same borrow: fixing the error is likely to fix the warning. - self.reservation_warnings.remove(&borrow_idx); - } - if conflict_error || mutability_error { debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind); self.access_place_error_reported.insert((place_span.0, place_span.1)); @@ -1068,6 +1025,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { BorrowKind::Unique | BorrowKind::Mut { .. }, ) => Control::Continue, + (Reservation(_), BorrowKind::Shallow | BorrowKind::Shared) => { + // This used to be a future compatibility warning (to be + // disallowed on NLL). See rust-lang/rust#56254 + Control::Continue + } + (Write(WriteKind::Move), BorrowKind::Shallow) => { // Handled by initialization checks. Control::Continue @@ -1096,27 +1059,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Control::Break } - ( - Reservation(WriteKind::MutableBorrow(bk)), - BorrowKind::Shallow | BorrowKind::Shared, - ) if { tcx.migrate_borrowck() && this.borrow_set.contains(&location) } => { - let bi = this.borrow_set.get_index_of(&location).unwrap(); - debug!( - "recording invalid reservation of place: {:?} with \ - borrow index {:?} as warning", - place_span.0, bi, - ); - // rust-lang/rust#56254 - This was previously permitted on - // the 2018 edition so we emit it as a warning. We buffer - // these separately so that we only emit a warning if borrow - // checking was otherwise successful. - this.reservation_warnings - .insert(bi, (place_span.0, place_span.1, location, bk, borrow.clone())); - - // Don't suppress actual errors. - Control::Continue - } - (Reservation(kind) | Activation(kind, _) | Write(kind), _) => { match rw { Reservation(..) => { diff --git a/compiler/rustc_borrowck/src/member_constraints.rs b/compiler/rustc_borrowck/src/member_constraints.rs index 0fe44328fd9..f920d9d5c3f 100644 --- a/compiler/rustc_borrowck/src/member_constraints.rs +++ b/compiler/rustc_borrowck/src/member_constraints.rs @@ -169,7 +169,7 @@ where /// Returns the "choice regions" for a given member /// constraint. This is the `R1..Rn` from a constraint like: /// - /// ``` + /// ```text /// R0 member of [R1..Rn] /// ``` crate fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] { @@ -195,14 +195,14 @@ where /// /// Before: /// -/// ``` +/// ```text /// target_list: A -> B -> C -> (None) /// source_list: D -> E -> F -> (None) /// ``` /// /// After: /// -/// ``` +/// ```text /// target_list: A -> B -> C -> D -> E -> F -> (None) /// ``` fn append_list( diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index cf03f34a4ec..b2fa16ce125 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -436,14 +436,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// minimum values. /// /// For example: - /// - /// fn foo<'a, 'b>(..) where 'a: 'b - /// + /// ``` + /// fn foo<'a, 'b>( /* ... */ ) where 'a: 'b { /* ... */ } + /// ``` /// would initialize two variables like so: - /// - /// R0 = { CFG, R0 } // 'a - /// R1 = { CFG, R0, R1 } // 'b - /// + /// ```ignore (illustrative) + /// R0 = { CFG, R0 } // 'a + /// R1 = { CFG, R0, R1 } // 'b + /// ``` /// Here, R0 represents `'a`, and it contains (a) the entire CFG /// and (b) any universally quantified regions that it outlives, /// which in this case is just itself. R1 (`'b`) in contrast also @@ -1310,9 +1310,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// whether any of the constraints were too strong. In particular, /// we want to check for a case where a universally quantified /// region exceeded its bounds. Consider: - /// - /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } - /// + /// ```compile_fail,E0312 + /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } + /// ``` /// In this case, returning `x` requires `&'a u32 <: &'b u32` /// and hence we establish (transitively) a constraint that /// `'a: 'b`. The `propagate_constraints` code above will @@ -1366,9 +1366,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// <https://smallcultfollowing.com/babysteps/blog/2019/01/17/polonius-and-region-errors/> /// /// In the canonical example - /// - /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } - /// + /// ```compile_fail,E0312 + /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } + /// ``` /// returning `x` requires `&'a u32 <: &'b u32` and hence we establish (transitively) a /// constraint that `'a: 'b`. It is an error that we have no evidence that this /// constraint holds. diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index c4a190b44cb..4b905c23e15 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -971,7 +971,7 @@ pub enum Locations { /// things like the type of the return slot. Consider this /// example: /// - /// ``` + /// ```compile_fail,E0515 /// fn foo<'a>(x: &'a u32) -> &'a u32 { /// let y = 22; /// return &y; // error diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index e26adba0d30..c06fe881410 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -187,12 +187,12 @@ pub enum RegionClassification { /// /// Consider this example: /// - /// ``` + /// ```ignore (pseudo-rust) /// fn foo<'a, 'b>(a: &'a u32, b: &'b u32, c: &'static u32) { /// let closure = for<'x> |x: &'x u32| { .. }; - /// ^^^^^^^ pretend this were legal syntax - /// for declaring a late-bound region in - /// a closure signature + /// // ^^^^^^^ pretend this were legal syntax + /// // for declaring a late-bound region in + /// // a closure signature /// } /// ``` /// diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index e9e3307ca95..c95d7147176 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -626,7 +626,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl if !parser.errors.is_empty() { let err = parser.errors.remove(0); - let err_sp = template_span.from_inner(err.span); + let err_sp = template_span.from_inner(InnerSpan::new(err.span.start, err.span.end)); let msg = &format!("invalid asm template string: {}", err.description); let mut e = ecx.struct_span_err(err_sp, msg); e.span_label(err_sp, err.label + " in asm template string"); @@ -634,7 +634,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl e.note(¬e); } if let Some((label, span)) = err.secondary_label { - let err_sp = template_span.from_inner(span); + let err_sp = template_span.from_inner(InnerSpan::new(span.start, span.end)); e.span_label(err_sp, label); } e.emit(); @@ -643,7 +643,10 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl curarg = parser.curarg; - let mut arg_spans = parser.arg_places.iter().map(|span| template_span.from_inner(*span)); + let mut arg_spans = parser + .arg_places + .iter() + .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))); for piece in unverified_pieces { match piece { parse::Piece::String(s) => { @@ -699,14 +702,21 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl Some(idx) } } - parse::ArgumentNamed(name, span) => match args.named_args.get(&name) { - Some(&idx) => Some(idx), - None => { - let msg = format!("there is no argument named `{}`", name); - ecx.struct_span_err(template_span.from_inner(span), &msg).emit(); - None + parse::ArgumentNamed(name, span) => { + match args.named_args.get(&Symbol::intern(name)) { + Some(&idx) => Some(idx), + None => { + let msg = format!("there is no argument named `{}`", name); + ecx.struct_span_err( + template_span + .from_inner(InnerSpan::new(span.start, span.end)), + &msg, + ) + .emit(); + None + } } - }, + } }; let mut chars = arg.format.ty.chars(); @@ -715,7 +725,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl let span = arg .format .ty_span - .map(|sp| template_sp.from_inner(sp)) + .map(|sp| template_sp.from_inner(InnerSpan::new(sp.start, sp.end))) .unwrap_or(template_sp); ecx.struct_span_err( span, @@ -741,7 +751,12 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl let template_num_lines = 1 + template_str.matches('\n').count(); line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines)); } else { - line_spans.extend(parser.line_spans.iter().map(|span| template_span.from_inner(*span))); + line_spans.extend( + parser + .line_spans + .iter() + .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))), + ); }; } diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index c5f3a9d3379..6151a80a56d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -5,14 +5,14 @@ //! //! For example, a type like: //! -//! ``` +//! ```ignore (old code) //! #[derive(RustcEncodable, RustcDecodable)] //! struct Node { id: usize } //! ``` //! //! would generate two implementations like: //! -//! ``` +//! ```ignore (old code) //! # struct Node { id: usize } //! impl<S: Encoder<E>, E> Encodable<S, E> for Node { //! fn encode(&self, s: &mut S) -> Result<(), E> { @@ -40,7 +40,7 @@ //! Other interesting scenarios are when the item has type parameters or //! references other non-built-in types. A type definition like: //! -//! ``` +//! ```ignore (old code) //! # #[derive(RustcEncodable, RustcDecodable)] //! # struct Span; //! #[derive(RustcEncodable, RustcDecodable)] @@ -49,7 +49,7 @@ //! //! would yield functions like: //! -//! ``` +//! ```ignore (old code) //! # #[derive(RustcEncodable, RustcDecodable)] //! # struct Span; //! # struct Spanned<T> { node: T, span: Span } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index f87f4726d1c..0fd23fd281e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -1006,9 +1006,11 @@ impl<'a> MethodDef<'a> { /// } /// } /// } - /// - /// // or if A is repr(packed) - note fields are matched by-value - /// // instead of by-reference. + /// ``` + /// or if A is repr(packed) - note fields are matched by-value + /// instead of by-reference. + /// ``` + /// # struct A { x: i32, y: i32 } /// impl PartialEq for A { /// fn eq(&self, other: &A) -> bool { /// match *self { @@ -1126,14 +1128,15 @@ impl<'a> MethodDef<'a> { /// // is equivalent to /// /// impl PartialEq for A { - /// fn eq(&self, other: &A) -> ::bool { + /// fn eq(&self, other: &A) -> bool { + /// use A::*; /// match (&*self, &*other) { /// (&A1, &A1) => true, /// (&A2(ref self_0), /// &A2(ref __arg_1_0)) => (*self_0).eq(&(*__arg_1_0)), /// _ => { - /// let __self_vi = match *self { A1(..) => 0, A2(..) => 1 }; - /// let __arg_1_vi = match *other { A1(..) => 0, A2(..) => 1 }; + /// let __self_vi = match *self { A1 => 0, A2(..) => 1 }; + /// let __arg_1_vi = match *other { A1 => 0, A2(..) => 1 }; /// false /// } /// } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 138e1fa0176..60b96399b5e 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -242,7 +242,7 @@ impl<'a, 'b> Context<'a, 'b> { fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) { // NOTE: the `unwrap_or` branch is needed in case of invalid format // arguments, e.g., `format_args!("{foo}")`. - let lookup = |s: Symbol| *self.names.get(&s).unwrap_or(&0); + let lookup = |s: &str| *self.names.get(&Symbol::intern(s)).unwrap_or(&0); match *p { parse::String(_) => {} @@ -276,7 +276,9 @@ impl<'a, 'b> Context<'a, 'b> { // it's written second, so it should come after width/precision. let pos = match arg.position { parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i), - parse::ArgumentNamed(s, span) => Named(s, span), + parse::ArgumentNamed(s, span) => { + Named(Symbol::intern(s), InnerSpan::new(span.start, span.end)) + } }; let ty = Placeholder(match arg.format.ty { @@ -291,7 +293,10 @@ impl<'a, 'b> Context<'a, 'b> { "X" => "UpperHex", _ => { let fmtsp = self.fmtsp; - let sp = arg.format.ty_span.map(|sp| fmtsp.from_inner(sp)); + let sp = arg + .format + .ty_span + .map(|sp| fmtsp.from_inner(InnerSpan::new(sp.start, sp.end))); let mut err = self.ecx.struct_span_err( sp.unwrap_or(fmtsp), &format!("unknown format trait `{}`", arg.format.ty), @@ -340,14 +345,17 @@ impl<'a, 'b> Context<'a, 'b> { } } - fn verify_count(&mut self, c: parse::Count) { + fn verify_count(&mut self, c: parse::Count<'_>) { match c { parse::CountImplied | parse::CountIs(..) => {} parse::CountIsParam(i) => { self.verify_arg_type(Exact(i), Count); } parse::CountIsName(s, span) => { - self.verify_arg_type(Named(s, span), Count); + self.verify_arg_type( + Named(Symbol::intern(s), InnerSpan::new(span.start, span.end)), + Count, + ); } } } @@ -425,7 +433,7 @@ impl<'a, 'b> Context<'a, 'b> { for fmt in &self.arg_with_formatting { if let Some(span) = fmt.precision_span { - let span = self.fmtsp.from_inner(span); + let span = self.fmtsp.from_inner(InnerSpan::new(span.start, span.end)); match fmt.precision { parse::CountIsParam(pos) if pos > self.num_args() => { e.span_label( @@ -471,7 +479,7 @@ impl<'a, 'b> Context<'a, 'b> { } } if let Some(span) = fmt.width_span { - let span = self.fmtsp.from_inner(span); + let span = self.fmtsp.from_inner(InnerSpan::new(span.start, span.end)); match fmt.width { parse::CountIsParam(pos) if pos > self.num_args() => { e.span_label( @@ -610,7 +618,7 @@ impl<'a, 'b> Context<'a, 'b> { ecx.std_path(&[sym::fmt, sym::rt, sym::v1, s]) } - fn build_count(&self, c: parse::Count) -> P<ast::Expr> { + fn build_count(&self, c: parse::Count<'_>) -> P<ast::Expr> { let sp = self.macsp; let count = |c, arg| { let mut path = Context::rtpath(self.ecx, sym::Count); @@ -1033,7 +1041,7 @@ pub fn expand_preparsed_format_args( if !parser.errors.is_empty() { let err = parser.errors.remove(0); let sp = if efmt_kind_is_lit { - fmt_span.from_inner(err.span) + fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end)) } else { // The format string could be another macro invocation, e.g.: // format!(concat!("abc", "{}"), 4); @@ -1052,14 +1060,18 @@ pub fn expand_preparsed_format_args( } if let Some((label, span)) = err.secondary_label { if efmt_kind_is_lit { - e.span_label(fmt_span.from_inner(span), label); + e.span_label(fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label); } } e.emit(); return DummyResult::raw_expr(sp, true); } - let arg_spans = parser.arg_places.iter().map(|span| fmt_span.from_inner(*span)).collect(); + let arg_spans = parser + .arg_places + .iter() + .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end))) + .collect(); let named_pos: FxHashSet<usize> = names.values().cloned().collect(); diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index a05122394a0..468ca7d7aa9 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -4,7 +4,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(array_windows)] #![feature(box_patterns)] -#![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] #![feature(is_sorted)] diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 2817fce463b..8bf3a0799b6 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -3,17 +3,15 @@ use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; -use rustc_errors::PResult; use rustc_expand::base::{self, *}; use rustc_expand::module::DirOwnership; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_parse::{self, new_parser_from_file}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; use rustc_span::symbol::Symbol; -use rustc_span::{self, FileName, Pos, Span}; +use rustc_span::{self, Pos, Span}; use smallvec::SmallVec; -use std::path::PathBuf; use std::rc::Rc; // These macros all relate to the file system; they either return @@ -104,7 +102,7 @@ pub fn expand_include<'cx>( return DummyResult::any(sp); }; // The file will be added to the code map by the parser - let file = match resolve_path(cx, file.as_str(), sp) { + let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) { Ok(f) => f, Err(mut err) => { err.emit(); @@ -176,7 +174,7 @@ pub fn expand_include_str( let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_str!") else { return DummyResult::any(sp); }; - let file = match resolve_path(cx, file.as_str(), sp) { + let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) { Ok(f) => f, Err(mut err) => { err.emit(); @@ -210,7 +208,7 @@ pub fn expand_include_bytes( let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_bytes!") else { return DummyResult::any(sp); }; - let file = match resolve_path(cx, file.as_str(), sp) { + let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) { Ok(f) => f, Err(mut err) => { err.emit(); @@ -225,40 +223,3 @@ pub fn expand_include_bytes( } } } - -/// Resolves a `path` mentioned inside Rust code, returning an absolute path. -/// -/// This unifies the logic used for resolving `include_X!`. -fn resolve_path<'a>( - cx: &mut ExtCtxt<'a>, - path: impl Into<PathBuf>, - span: Span, -) -> PResult<'a, PathBuf> { - let path = path.into(); - - // Relative paths are resolved relative to the file in which they are found - // after macro expansion (that is, they are unhygienic). - if !path.is_absolute() { - let callsite = span.source_callsite(); - let mut result = match cx.source_map().span_to_filename(callsite) { - FileName::Real(name) => name - .into_local_path() - .expect("attempting to resolve a file path in an external file"), - FileName::DocTest(path, _) => path, - other => { - return Err(cx.struct_span_err( - span, - &format!( - "cannot resolve relative path in non-file source `{}`", - cx.source_map().filename_for_diagnostics(&other) - ), - )); - } - }; - result.pop(); - result.push(path); - Ok(result) - } else { - Ok(path) - } -} diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index e2553ab40ca..db8dce804a3 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -249,7 +249,7 @@ fn generate_test_harness( /// /// By default this expands to /// -/// ``` +/// ```ignore UNSOLVED (I think I still need guidance for this one. Is it correct? Do we try to make it run? How do we nicely fill it out?) /// #[rustc_main] /// pub fn main() { /// extern crate test; diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 497a28354d8..58996a9db78 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -139,14 +139,12 @@ impl CodegenBackend for GccCodegenBackend { } impl ExtraBackendMethods for GccCodegenBackend { - fn new_metadata<'tcx>(&self, _tcx: TyCtxt<'tcx>, _mod_name: &str) -> Self::Module { - GccContext { + fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) -> Self::Module { + let mut mods = GccContext { context: Context::default(), - } - } - - fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, mods: &mut Self::Module, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) { - unsafe { allocator::codegen(tcx, mods, module_name, kind, has_alloc_error_handler) } + }; + unsafe { allocator::codegen(tcx, &mut mods, module_name, kind, has_alloc_error_handler); } + mods } fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) { @@ -213,7 +211,7 @@ impl WriteBackendMethods for GccCodegenBackend { unimplemented!(); } }; - Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: vec![] }) + Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: vec![] }) } fn run_thin_lto(_cgcx: &CodegenContext<Self>, _modules: Vec<(String, Self::ThinBuffer)>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> { @@ -229,7 +227,12 @@ impl WriteBackendMethods for GccCodegenBackend { Ok(()) } - unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: &mut ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> { + fn optimize_fat(_cgcx: &CodegenContext<Self>, _module: &mut ModuleCodegen<Self::Module>) -> Result<(), FatalError> { + // TODO(antoyo) + Ok(()) + } + + unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> { unimplemented!(); } @@ -245,11 +248,6 @@ impl WriteBackendMethods for GccCodegenBackend { unimplemented!(); } - fn run_lto_pass_manager(_cgcx: &CodegenContext<Self>, _module: &ModuleCodegen<Self::Module>, _config: &ModuleConfig, _thin: bool) -> Result<(), FatalError> { - // TODO(antoyo) - Ok(()) - } - fn run_link(cgcx: &CodegenContext<Self>, diag_handler: &Handler, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> { back::write::link(cgcx, diag_handler, modules) } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 5c63bd8c1bd..d0724baf9e0 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -6,9 +6,7 @@ use crate::llvm::{self, build_string, False, True}; use crate::{llvm_util, LlvmCodegenBackend, ModuleLlvm}; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::symbol_export; -use rustc_codegen_ssa::back::write::{ - CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig, -}; +use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, TargetMachineFactoryConfig}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind}; use rustc_data_structures::fx::FxHashMap; @@ -353,7 +351,7 @@ fn fat_lto( } } - Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: serialized_bitcode }) + Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: serialized_bitcode }) } crate struct Linker<'a>(&'a mut llvm::Linker<'a>); @@ -396,15 +394,15 @@ impl Drop for Linker<'_> { /// /// At a high level Thin LTO looks like: /// -/// 1. Prepare a "summary" of each LLVM module in question which describes -/// the values inside, cost of the values, etc. -/// 2. Merge the summaries of all modules in question into one "index" -/// 3. Perform some global analysis on this index -/// 4. For each module, use the index and analysis calculated previously to -/// perform local transformations on the module, for example inlining -/// small functions from other modules. -/// 5. Run thin-specific optimization passes over each module, and then code -/// generate everything at the end. +/// 1. Prepare a "summary" of each LLVM module in question which describes +/// the values inside, cost of the values, etc. +/// 2. Merge the summaries of all modules in question into one "index" +/// 3. Perform some global analysis on this index +/// 4. For each module, use the index and analysis calculated previously to +/// perform local transformations on the module, for example inlining +/// small functions from other modules. +/// 5. Run thin-specific optimization passes over each module, and then code +/// generate everything at the end. /// /// The summary for each module is intended to be quite cheap, and the global /// index is relatively quite cheap to create as well. As a result, the goal of @@ -578,11 +576,11 @@ fn thin_lto( pub(crate) fn run_pass_manager( cgcx: &CodegenContext<LlvmCodegenBackend>, diag_handler: &Handler, - module: &ModuleCodegen<ModuleLlvm>, - config: &ModuleConfig, + module: &mut ModuleCodegen<ModuleLlvm>, thin: bool, ) -> Result<(), FatalError> { let _timer = cgcx.prof.extra_verbose_generic_activity("LLVM_lto_optimize", &*module.name); + let config = cgcx.config(module.kind); // Now we have one massive module inside of llmod. Time to run the // LTO-specific optimization passes that LLVM provides. @@ -726,7 +724,7 @@ impl Drop for ThinBuffer { } pub unsafe fn optimize_thin_module( - thin_module: &mut ThinModule<LlvmCodegenBackend>, + thin_module: ThinModule<LlvmCodegenBackend>, cgcx: &CodegenContext<LlvmCodegenBackend>, ) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> { let diag_handler = cgcx.create_diag_handler(); @@ -743,7 +741,7 @@ pub unsafe fn optimize_thin_module( // that LLVM Context and Module. let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); let llmod_raw = parse_module(llcx, module_name, thin_module.data(), &diag_handler)? as *const _; - let module = ModuleCodegen { + let mut module = ModuleCodegen { module_llvm: ModuleLlvm { llmod_raw, llcx, tm }, name: thin_module.name().to_string(), kind: ModuleKind::Regular, @@ -859,8 +857,7 @@ pub unsafe fn optimize_thin_module( // little differently. { info!("running thin lto passes over {}", module.name); - let config = cgcx.config(module.kind); - run_pass_manager(cgcx, &diag_handler, &module, config, true)?; + run_pass_manager(cgcx, &diag_handler, &mut module, true)?; save_temp_bitcode(cgcx, &module, "thin-lto-after-pm"); } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/doc.md b/compiler/rustc_codegen_llvm/src/debuginfo/doc.md index 5a8976c6166..aaec4e68c17 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/doc.md +++ b/compiler/rustc_codegen_llvm/src/debuginfo/doc.md @@ -27,9 +27,9 @@ The module is thus driven from an outside client with functions like Internally the module will try to reuse already created metadata by utilizing a cache. The way to get a shared metadata node when needed is thus to just call the corresponding function in this module: - - let file_metadata = file_metadata(cx, file); - +```ignore (illustrative) +let file_metadata = file_metadata(cx, file); +``` The function will take care of probing the cache for an existing node for that exact file path. @@ -63,7 +63,7 @@ struct List { will generate the following callstack with a naive DFS algorithm: -``` +```ignore (illustrative) describe(t = List) describe(t = i32) describe(t = Option<Box<List>>) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 3152c505af0..0bead4629a6 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -5,7 +5,6 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(let_chains)] #![feature(let_else)] @@ -104,19 +103,18 @@ impl Drop for TimeTraceProfiler { } impl ExtraBackendMethods for LlvmCodegenBackend { - fn new_metadata(&self, tcx: TyCtxt<'_>, mod_name: &str) -> ModuleLlvm { - ModuleLlvm::new_metadata(tcx, mod_name) - } - fn codegen_allocator<'tcx>( &self, tcx: TyCtxt<'tcx>, - module_llvm: &mut ModuleLlvm, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool, - ) { - unsafe { allocator::codegen(tcx, module_llvm, module_name, kind, has_alloc_error_handler) } + ) -> ModuleLlvm { + let mut module_llvm = ModuleLlvm::new_metadata(tcx, module_name); + unsafe { + allocator::codegen(tcx, &mut module_llvm, module_name, kind, has_alloc_error_handler); + } + module_llvm } fn compile_codegen_unit( &self, @@ -210,9 +208,16 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> Result<(), FatalError> { back::write::optimize(cgcx, diag_handler, module, config) } + fn optimize_fat( + cgcx: &CodegenContext<Self>, + module: &mut ModuleCodegen<Self::Module>, + ) -> Result<(), FatalError> { + let diag_handler = cgcx.create_diag_handler(); + back::lto::run_pass_manager(cgcx, &diag_handler, module, false) + } unsafe fn optimize_thin( cgcx: &CodegenContext<Self>, - thin: &mut ThinModule<Self>, + thin: ThinModule<Self>, ) -> Result<ModuleCodegen<Self::Module>, FatalError> { back::lto::optimize_thin_module(thin, cgcx) } @@ -230,15 +235,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) { (module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod())) } - fn run_lto_pass_manager( - cgcx: &CodegenContext<Self>, - module: &ModuleCodegen<Self::Module>, - config: &ModuleConfig, - thin: bool, - ) -> Result<(), FatalError> { - let diag_handler = cgcx.create_diag_handler(); - back::lto::run_pass_manager(cgcx, &diag_handler, module, config, thin) - } } unsafe impl Send for LlvmCodegenBackend {} // Llvm is on a per-thread basis diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 6887f0666a4..04ec1e7f3c1 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -5,7 +5,7 @@ use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{ErrorGuaranteed, Handler}; use rustc_fs_util::fix_windows_verbatim_for_gcc; -use rustc_hir::def_id::CrateNum; +use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip}; @@ -2099,8 +2099,14 @@ fn add_order_independent_options( // Pass optimization flags down to the linker. cmd.optimize(); + let debugger_visualizer_paths = if sess.target.is_like_msvc { + collect_debugger_visualizers(tmpdir, sess, &codegen_results.crate_info) + } else { + Vec::new() + }; + // Pass debuginfo and strip flags down to the linker. - cmd.debuginfo(strip_value(sess)); + cmd.debuginfo(strip_value(sess), &debugger_visualizer_paths); // We want to prevent the compiler from accidentally leaking in any system libraries, // so by default we tell linkers not to link to any default libraries. @@ -2119,6 +2125,47 @@ fn add_order_independent_options( add_rpath_args(cmd, sess, codegen_results, out_filename); } +// Write the debugger visualizer files for each crate to the temp directory and gather the file paths. +fn collect_debugger_visualizers( + tmpdir: &Path, + sess: &Session, + crate_info: &CrateInfo, +) -> Vec<PathBuf> { + let mut visualizer_paths = Vec::new(); + let debugger_visualizers = &crate_info.debugger_visualizers; + let mut index = 0; + + for (&cnum, visualizers) in debugger_visualizers { + let crate_name = if cnum == LOCAL_CRATE { + crate_info.local_crate_name.as_str() + } else { + crate_info.crate_name[&cnum].as_str() + }; + + for visualizer in visualizers { + let visualizer_out_file = tmpdir.join(format!("{}-{}.natvis", crate_name, index)); + + match fs::write(&visualizer_out_file, &visualizer.src) { + Ok(()) => { + visualizer_paths.push(visualizer_out_file.clone()); + index += 1; + } + Err(error) => { + sess.warn( + format!( + "Unable to write debugger visualizer file `{}`: {} ", + visualizer_out_file.display(), + error + ) + .as_str(), + ); + } + }; + } + } + visualizer_paths +} + /// # Native library linking /// /// User-supplied library search paths (-L on the command line). These are the same paths used to diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 50db2c22ae6..2a71377d2f1 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -183,7 +183,7 @@ pub trait Linker { fn optimize(&mut self); fn pgo_gen(&mut self); fn control_flow_guard(&mut self); - fn debuginfo(&mut self, strip: Strip); + fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]); fn no_crt_objects(&mut self); fn no_default_libraries(&mut self); fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]); @@ -611,7 +611,7 @@ impl<'a> Linker for GccLinker<'a> { fn control_flow_guard(&mut self) {} - fn debuginfo(&mut self, strip: Strip) { + fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) { // MacOS linker doesn't support stripping symbols directly anymore. if self.sess.target.is_like_osx { return; @@ -915,7 +915,7 @@ impl<'a> Linker for MsvcLinker<'a> { self.cmd.arg("/guard:cf"); } - fn debuginfo(&mut self, strip: Strip) { + fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]) { match strip { Strip::None => { // This will cause the Microsoft linker to generate a PDB file @@ -942,6 +942,13 @@ impl<'a> Linker for MsvcLinker<'a> { } } } + + // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file + for path in debugger_visualizers { + let mut arg = OsString::from("/NATVIS:"); + arg.push(path); + self.cmd.arg(arg); + } } Strip::Debuginfo | Strip::Symbols => { self.cmd.arg("/DEBUG:NONE"); @@ -1124,7 +1131,7 @@ impl<'a> Linker for EmLinker<'a> { fn control_flow_guard(&mut self) {} - fn debuginfo(&mut self, _strip: Strip) { + fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) { // Preserve names or generate source maps depending on debug info self.cmd.arg(match self.sess.opts.debuginfo { DebugInfo::None => "-g0", @@ -1315,7 +1322,7 @@ impl<'a> Linker for WasmLd<'a> { fn pgo_gen(&mut self) {} - fn debuginfo(&mut self, strip: Strip) { + fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) { match strip { Strip::None => {} Strip::Debuginfo => { @@ -1450,7 +1457,7 @@ impl<'a> Linker for L4Bender<'a> { fn pgo_gen(&mut self) {} - fn debuginfo(&mut self, strip: Strip) { + fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) { match strip { Strip::None => {} Strip::Debuginfo => { @@ -1600,7 +1607,7 @@ impl<'a> Linker for PtxLinker<'a> { self.cmd.arg("-L").arg(path); } - fn debuginfo(&mut self, _strip: Strip) { + fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) { self.cmd.arg("--debug"); } @@ -1699,7 +1706,7 @@ impl<'a> Linker for BpfLinker<'a> { self.cmd.arg("-L").arg(path); } - fn debuginfo(&mut self, _strip: Strip) { + fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) { self.cmd.arg("--debug"); } diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index d6ae689f254..cb6244050df 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -42,7 +42,7 @@ pub struct ThinShared<B: WriteBackendMethods> { pub enum LtoModuleCodegen<B: WriteBackendMethods> { Fat { - module: Option<ModuleCodegen<B::Module>>, + module: ModuleCodegen<B::Module>, _serialized_bitcode: Vec<SerializedModule<B::ModuleBuffer>>, }, @@ -64,19 +64,15 @@ impl<B: WriteBackendMethods> LtoModuleCodegen<B> { /// It's intended that the module returned is immediately code generated and /// dropped, and then this LTO module is dropped. pub unsafe fn optimize( - &mut self, + self, cgcx: &CodegenContext<B>, ) -> Result<ModuleCodegen<B::Module>, FatalError> { - match *self { - LtoModuleCodegen::Fat { ref mut module, .. } => { - let module = module.take().unwrap(); - { - let config = cgcx.config(module.kind); - B::run_lto_pass_manager(cgcx, &module, config, false)?; - } + match self { + LtoModuleCodegen::Fat { mut module, .. } => { + B::optimize_fat(cgcx, &mut module)?; Ok(module) } - LtoModuleCodegen::Thin(ref mut thin) => B::optimize_thin(cgcx, thin), + LtoModuleCodegen::Thin(thin) => B::optimize_thin(cgcx, thin), } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 98dc5fe8d64..88293dec01c 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -889,7 +889,7 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( fn execute_lto_work_item<B: ExtraBackendMethods>( cgcx: &CodegenContext<B>, - mut module: lto::LtoModuleCodegen<B>, + module: lto::LtoModuleCodegen<B>, module_config: &ModuleConfig, ) -> Result<WorkItemResult<B>, FatalError> { let module = unsafe { module.optimize(cgcx)? }; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 019c9c179d8..7b7e09208a2 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -575,15 +575,8 @@ pub fn codegen_crate<B: ExtraBackendMethods>( } else if let Some(kind) = tcx.allocator_kind(()) { let llmod_id = cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); - let mut module_llvm = backend.new_metadata(tcx, &llmod_id); - tcx.sess.time("write_allocator_module", || { - backend.codegen_allocator( - tcx, - &mut module_llvm, - &llmod_id, - kind, - tcx.lang_items().oom().is_some(), - ) + let module_llvm = tcx.sess.time("write_allocator_module", || { + backend.codegen_allocator(tcx, &llmod_id, kind, tcx.lang_items().oom().is_some()) }); Some(ModuleCodegen { name: llmod_id, module_llvm, kind: ModuleKind::Allocator }) @@ -854,7 +847,13 @@ impl CrateInfo { missing_lang_items: Default::default(), dependency_formats: tcx.dependency_formats(()).clone(), windows_subsystem, + debugger_visualizers: Default::default(), }; + let debugger_visualizers = tcx.debugger_visualizers(LOCAL_CRATE).clone(); + if !debugger_visualizers.is_empty() { + info.debugger_visualizers.insert(LOCAL_CRATE, debugger_visualizers); + } + let lang_items = tcx.lang_items(); let crates = tcx.crates(()); @@ -869,7 +868,9 @@ impl CrateInfo { info.native_libraries .insert(cnum, tcx.native_libraries(cnum).iter().map(Into::into).collect()); info.crate_name.insert(cnum, tcx.crate_name(cnum)); - info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum).clone()); + + let used_crate_source = tcx.used_crate_source(cnum); + info.used_crate_source.insert(cnum, used_crate_source.clone()); if tcx.is_compiler_builtins(cnum) { info.compiler_builtins = Some(cnum); } @@ -890,6 +891,14 @@ impl CrateInfo { let missing = missing.iter().cloned().filter(|&l| lang_items::required(tcx, l)).collect(); info.missing_lang_items.insert(cnum, missing); + + // Only include debugger visualizer files from crates that will be statically linked. + if used_crate_source.rlib.is_some() || used_crate_source.rmeta.is_some() { + let debugger_visualizers = tcx.debugger_visualizers(cnum).clone(); + if !debugger_visualizers.is_empty() { + info.debugger_visualizers.insert(cnum, debugger_visualizers); + } + } } info diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 05d32972dab..9e1fe588c53 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -1,5 +1,4 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(try_blocks)] #![feature(let_else)] @@ -36,6 +35,7 @@ use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT use rustc_session::cstore::{self, CrateSource}; use rustc_session::utils::NativeLibKind; use rustc_span::symbol::Symbol; +use rustc_span::DebuggerVisualizerFile; use std::path::{Path, PathBuf}; pub mod back; @@ -157,6 +157,7 @@ pub struct CrateInfo { pub missing_lang_items: FxHashMap<CrateNum, Vec<LangItem>>, pub dependency_formats: Lrc<Dependencies>, pub windows_subsystem: Option<String>, + pub debugger_visualizers: FxHashMap<CrateNum, Vec<DebuggerVisualizerFile>>, } #[derive(Encodable, Decodable)] diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index efb424af3ed..fa39e8dd247 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -40,9 +40,9 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } // If there exists a local definition that dominates all uses of that local, - // the definition should be visited first. Traverse blocks in preorder which + // the definition should be visited first. Traverse blocks in an order that // is a topological sort of dominance partial order. - for (bb, data) in traversal::preorder(&mir) { + for (bb, data) in traversal::reverse_postorder(&mir) { analyzer.visit_basic_block_data(bb, data); } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 856b7742583..1e53c73d1bb 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -114,15 +114,13 @@ pub trait CodegenBackend { } pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Send + Sync { - fn new_metadata(&self, sess: TyCtxt<'_>, mod_name: &str) -> Self::Module; fn codegen_allocator<'tcx>( &self, tcx: TyCtxt<'tcx>, - module_llvm: &mut Self::Module, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool, - ); + ) -> Self::Module; /// This generates the codegen unit and returns it along with /// a `u64` giving an estimate of the unit's processing cost. fn compile_codegen_unit( diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 93fbee2b49b..e54ec34f1ce 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -41,9 +41,13 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { module: &ModuleCodegen<Self::Module>, config: &ModuleConfig, ) -> Result<(), FatalError>; + fn optimize_fat( + cgcx: &CodegenContext<Self>, + llmod: &mut ModuleCodegen<Self::Module>, + ) -> Result<(), FatalError>; unsafe fn optimize_thin( cgcx: &CodegenContext<Self>, - thin: &mut ThinModule<Self>, + thin: ThinModule<Self>, ) -> Result<ModuleCodegen<Self::Module>, FatalError>; unsafe fn codegen( cgcx: &CodegenContext<Self>, @@ -53,12 +57,6 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { ) -> Result<CompiledModule, FatalError>; fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer); fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer); - fn run_lto_pass_manager( - cgcx: &CodegenContext<Self>, - llmod: &ModuleCodegen<Self::Module>, - config: &ModuleConfig, - thin: bool, - ) -> Result<(), FatalError>; } pub trait ThinBufferMethods: Send + Sync { diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index f2d833b3202..a8a5ac2f9d9 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -84,14 +84,18 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { } #[inline] - pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> { + pub fn to_scalar_or_uninit_pair(self) -> (ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>) { match self { - Immediate::ScalarPair(val1, val2) => Ok((val1.check_init()?, val2.check_init()?)), - Immediate::Scalar(..) => { - bug!("Got a scalar where a scalar pair was expected") - } + Immediate::ScalarPair(val1, val2) => (val1, val2), + Immediate::Scalar(..) => bug!("Got a scalar where a scalar pair was expected"), } } + + #[inline] + pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> { + let (val1, val2) = self.to_scalar_or_uninit_pair(); + Ok((val1.check_init()?, val2.check_init()?)) + } } // ScalarPair needs a type to interpret, so we often have an immediate and a type together @@ -248,9 +252,12 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> { impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`. /// Returns `None` if the layout does not permit loading this as a value. - fn try_read_immediate_from_mplace( + /// + /// This is an internal function; call `read_immediate` instead. + fn read_immediate_from_mplace_raw( &self, mplace: &MPlaceTy<'tcx, M::PointerTag>, + force: bool, ) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::PointerTag>>> { if mplace.layout.is_unsized() { // Don't touch unsized @@ -271,42 +278,61 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // case where some of the bytes are initialized and others are not. So, we need an extra // check that walks over the type of `mplace` to make sure it is truly correct to treat this // like a `Scalar` (or `ScalarPair`). - match mplace.layout.abi { - Abi::Scalar(abi::Scalar::Initialized { .. }) => { - let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?; - Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout })) - } + let scalar_layout = match mplace.layout.abi { + // `if` does not work nested inside patterns, making this a bit awkward to express. + Abi::Scalar(abi::Scalar::Initialized { value: s, .. }) => Some(s), + Abi::Scalar(s) if force => Some(s.primitive()), + _ => None, + }; + if let Some(_) = scalar_layout { + let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?; + return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout })); + } + let scalar_pair_layout = match mplace.layout.abi { Abi::ScalarPair( abi::Scalar::Initialized { value: a, .. }, abi::Scalar::Initialized { value: b, .. }, - ) => { - // We checked `ptr_align` above, so all fields will have the alignment they need. - // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, - // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. - let (a_size, b_size) = (a.size(self), b.size(self)); - let b_offset = a_size.align_to(b.align(self).abi); - assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields - let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?; - let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?; - Ok(Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout })) - } - _ => Ok(None), + ) => Some((a, b)), + Abi::ScalarPair(a, b) if force => Some((a.primitive(), b.primitive())), + _ => None, + }; + if let Some((a, b)) = scalar_pair_layout { + // We checked `ptr_align` above, so all fields will have the alignment they need. + // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, + // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. + let (a_size, b_size) = (a.size(self), b.size(self)); + let b_offset = a_size.align_to(b.align(self).abi); + assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields + let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?; + let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?; + return Ok(Some(ImmTy { + imm: Immediate::ScalarPair(a_val, b_val), + layout: mplace.layout, + })); } + // Neither a scalar nor scalar pair. + return Ok(None); } - /// Try returning an immediate for the operand. - /// If the layout does not permit loading this as an immediate, return where in memory - /// we can find the data. + /// Try returning an immediate for the operand. If the layout does not permit loading this as an + /// immediate, return where in memory we can find the data. /// Note that for a given layout, this operation will either always fail or always /// succeed! Whether it succeeds depends on whether the layout can be represented /// in an `Immediate`, not on which data is stored there currently. - pub fn try_read_immediate( + /// + /// If `force` is `true`, then even scalars with fields that can be ununit will be + /// read. This means the load is lossy and should not be written back! + /// This flag exists only for validity checking. + /// + /// This is an internal function that should not usually be used; call `read_immediate` instead. + pub fn read_immediate_raw( &self, src: &OpTy<'tcx, M::PointerTag>, + force: bool, ) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::PointerTag>, MPlaceTy<'tcx, M::PointerTag>>> { Ok(match src.try_as_mplace() { Ok(ref mplace) => { - if let Some(val) = self.try_read_immediate_from_mplace(mplace)? { + if let Some(val) = self.read_immediate_from_mplace_raw(mplace, force)? { Ok(val) } else { Err(*mplace) @@ -322,7 +348,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, op: &OpTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { - if let Ok(imm) = self.try_read_immediate(op)? { + if let Ok(imm) = self.read_immediate_raw(op, /*force*/ false)? { Ok(imm) } else { span_bug!(self.cur_span(), "primitive read failed for type: {:?}", op.layout.ty); diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 380eb526361..df6e05bb13c 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -720,7 +720,7 @@ where } trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); - // See if we can avoid an allocation. This is the counterpart to `try_read_immediate`, + // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`, // but not factored as a separate function. let mplace = match dest.place { Place::Local { frame, local } => { @@ -879,7 +879,7 @@ where } // Let us see if the layout is simple so we take a shortcut, avoid force_allocation. - let src = match self.try_read_immediate(src)? { + let src = match self.read_immediate_raw(src, /*force*/ false)? { Ok(src_val) => { assert!(!src.layout.is_unsized(), "cannot have unsized immediates"); // Yay, we got a value that we can write directly. diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 71d29be97d5..92e3ac04dc4 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -20,8 +20,8 @@ use rustc_target::abi::{Abi, Scalar as ScalarAbi, Size, VariantIdx, Variants, Wr use std::hash::Hash; use super::{ - alloc_range, CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine, - MemPlaceMeta, OpTy, Scalar, ScalarMaybeUninit, ValueVisitor, + alloc_range, CheckInAllocMsg, GlobalAlloc, Immediate, InterpCx, InterpResult, MPlaceTy, + Machine, MemPlaceMeta, OpTy, Scalar, ScalarMaybeUninit, ValueVisitor, }; macro_rules! throw_validation_failure { @@ -487,6 +487,17 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' )) } + fn read_immediate_forced( + &self, + op: &OpTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, Immediate<M::PointerTag>> { + Ok(*try_validation!( + self.ecx.read_immediate_raw(op, /*force*/ true), + self.path, + err_unsup!(ReadPointerAsBytes) => { "(potentially part of) a pointer" } expected { "plain (non-pointer) bytes" }, + ).unwrap()) + } + /// Check if this is a value of primitive type, and if yes check the validity of the value /// at that type. Return `true` if the type is indeed primitive. fn try_visit_primitive( @@ -626,18 +637,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' fn visit_scalar( &mut self, - op: &OpTy<'tcx, M::PointerTag>, + scalar: ScalarMaybeUninit<M::PointerTag>, scalar_layout: ScalarAbi, ) -> InterpResult<'tcx> { // We check `is_full_range` in a slightly complicated way because *if* we are checking // number validity, then we want to ensure that `Scalar::Initialized` is indeed initialized, // i.e. that we go over the `check_init` below. + let size = scalar_layout.size(self.ecx); let is_full_range = match scalar_layout { ScalarAbi::Initialized { valid_range, .. } => { if M::enforce_number_validity(self.ecx) { false // not "full" since uninit is not accepted } else { - valid_range.is_full_for(op.layout.size) + valid_range.is_full_for(size) } } ScalarAbi::Union { .. } => true, @@ -646,21 +658,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // Nothing to check return Ok(()); } - // We have something to check. + // We have something to check: it must at least be initialized. let valid_range = scalar_layout.valid_range(self.ecx); let WrappingRange { start, end } = valid_range; - let max_value = op.layout.size.unsigned_int_max(); + let max_value = size.unsigned_int_max(); assert!(end <= max_value); - // Determine the allowed range - let value = self.read_scalar(op)?; let value = try_validation!( - value.check_init(), + scalar.check_init(), self.path, - err_ub!(InvalidUninitBytes(None)) => { "{:x}", value } + err_ub!(InvalidUninitBytes(None)) => { "{:x}", scalar } expected { "something {}", wrapping_range_format(valid_range, max_value) }, ); let bits = match value.try_to_int() { - Ok(int) => int.assert_bits(op.layout.size), + Ok(int) => int.assert_bits(size), Err(_) => { // So this is a pointer then, and casting to an int failed. // Can only happen during CTFE. @@ -678,7 +688,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } else { return Ok(()); } - } else if scalar_layout.valid_range(self.ecx).is_full_for(op.layout.size) { + } else if scalar_layout.valid_range(self.ecx).is_full_for(size) { // Easy. (This is reachable if `enforce_number_validity` is set.) return Ok(()); } else { @@ -817,13 +827,23 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> ); } Abi::Scalar(scalar_layout) => { - self.visit_scalar(op, scalar_layout)?; + let scalar = self.read_immediate_forced(op)?.to_scalar_or_uninit(); + self.visit_scalar(scalar, scalar_layout)?; + } + Abi::ScalarPair(a_layout, b_layout) => { + // We would validate these things as we descend into the fields, + // but that can miss bugs in layout computation. Layout computation + // is subtle due to enums having ScalarPair layout, where one field + // is the discriminant. + if cfg!(debug_assertions) { + let (a, b) = self.read_immediate_forced(op)?.to_scalar_or_uninit_pair(); + self.visit_scalar(a, a_layout)?; + self.visit_scalar(b, b_layout)?; + } } - Abi::ScalarPair { .. } | Abi::Vector { .. } => { - // These have fields that we already visited above, so we already checked - // all their scalar-level restrictions. - // There is also no equivalent to `rustc_layout_scalar_valid_range_start` - // that would make skipping them here an issue. + Abi::Vector { .. } => { + // No checks here, we assume layout computation gets this right. + // (This is harder to check since Miri does not represent these as `Immediate`.) } Abi::Aggregate { .. } => { // Nothing to do. diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 34a00452519..1ab461a9421 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -5,7 +5,6 @@ Rust MIR: a lowered representation of Rust. */ #![feature(assert_matches)] -#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(crate_visibility_modifier)] diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index f71bc586b48..25209e20e99 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::storage::AlwaysLiveLocals; use rustc_mir_dataflow::{Analysis, ResultsCursor}; -use rustc_target::abi::Size; +use rustc_target::abi::{Size, VariantIdx}; #[derive(Copy, Clone, Debug)] enum EdgeKind { @@ -244,6 +244,60 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("bad index ({:?} != usize)", index_ty)) } } + if let ProjectionElem::Field(f, ty) = elem { + let parent = Place { local, projection: self.tcx.intern_place_elems(proj_base) }; + let parent_ty = parent.ty(&self.body.local_decls, self.tcx); + let fail_out_of_bounds = |this: &Self, location| { + this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty)); + }; + let check_equal = |this: &Self, location, f_ty| { + if !this.mir_assign_valid_types(ty, f_ty) { + this.fail( + location, + format!( + "Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is {:?}", + parent, f, ty, f_ty + ) + ) + } + }; + match parent_ty.ty.kind() { + ty::Tuple(fields) => { + let Some(f_ty) = fields.get(f.as_usize()) else { + fail_out_of_bounds(self, location); + return; + }; + check_equal(self, location, *f_ty); + } + ty::Adt(adt_def, substs) => { + let var = parent_ty.variant_index.unwrap_or(VariantIdx::from_u32(0)); + let Some(field) = adt_def.variant(var).fields.get(f.as_usize()) else { + fail_out_of_bounds(self, location); + return; + }; + check_equal(self, location, field.ty(self.tcx, substs)); + } + ty::Closure(_, substs) => { + let substs = substs.as_closure(); + let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else { + fail_out_of_bounds(self, location); + return; + }; + check_equal(self, location, f_ty); + } + ty::Generator(_, substs, _) => { + let substs = substs.as_generator(); + let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else { + fail_out_of_bounds(self, location); + return; + }; + check_equal(self, location, f_ty); + } + _ => { + self.fail(location, format!("{:?} does not have fields", parent_ty.ty)); + } + } + } self.super_projection_elem(local, proj_base, elem, context, location); } @@ -291,7 +345,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ty::Array(..) | ty::Slice(..) ); } - Rvalue::BinaryOp(op, vals) | Rvalue::CheckedBinaryOp(op, vals) => { + Rvalue::BinaryOp(op, vals) => { use BinOp::*; let a = vals.0.ty(&self.body.local_decls, self.tcx); let b = vals.1.ty(&self.body.local_decls, self.tcx); @@ -355,17 +409,55 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { for x in [a, b] { check_kinds!( x, - "Cannot perform op on type {:?}", + "Cannot perform arithmetic on type {:?}", ty::Uint(..) | ty::Int(..) | ty::Float(..) ) } if a != b { self.fail( location, - format!("Cannot perform op on unequal types {:?} and {:?}", a, b), + format!( + "Cannot perform arithmetic on unequal types {:?} and {:?}", + a, b + ), + ); + } + } + } + } + Rvalue::CheckedBinaryOp(op, vals) => { + use BinOp::*; + let a = vals.0.ty(&self.body.local_decls, self.tcx); + let b = vals.1.ty(&self.body.local_decls, self.tcx); + match op { + Add | Sub | Mul => { + for x in [a, b] { + check_kinds!( + x, + "Cannot perform checked arithmetic on type {:?}", + ty::Uint(..) | ty::Int(..) + ) + } + if a != b { + self.fail( + location, + format!( + "Cannot perform checked arithmetic on unequal types {:?} and {:?}", + a, b + ), ); } } + Shl | Shr => { + for x in [a, b] { + check_kinds!( + x, + "Cannot perform checked shift on non-integer type {:?}", + ty::Uint(..) | ty::Int(..) + ) + } + } + _ => self.fail(location, format!("There is no checked version of {:?}", op)), } } Rvalue::UnaryOp(op, operand) => { diff --git a/compiler/rustc_data_structures/src/frozen.rs b/compiler/rustc_data_structures/src/frozen.rs index 2daf5b04141..c81e1b124f0 100644 --- a/compiler/rustc_data_structures/src/frozen.rs +++ b/compiler/rustc_data_structures/src/frozen.rs @@ -23,7 +23,8 @@ //! `computed` does not change accidentally (e.g. somebody might accidentally call //! `foo.computed.mutate()`). This is what `Frozen` is for. We can do the following: //! -//! ```rust +//! ``` +//! # struct Bar {} //! use rustc_data_structures::frozen::Frozen; //! //! struct Foo { diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 30a3ddc6003..76ae17f28c6 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -10,7 +10,6 @@ #![feature(array_windows)] #![feature(associated_type_bounds)] #![feature(auto_traits)] -#![feature(bool_to_option)] #![feature(control_flow_enum)] #![feature(core_intrinsics)] #![feature(extend_one)] @@ -18,6 +17,7 @@ #![feature(generators)] #![feature(let_else)] #![feature(hash_raw_entry)] +#![feature(hasher_prefixfree_extras)] #![feature(maybe_uninit_uninit_array)] #![feature(min_specialization)] #![feature(never_type)] diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index 5fe2a1fb84b..74f432a7967 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -202,7 +202,7 @@ impl<O> Node<O> { /// with this node. /// /// The non-`Error` state transitions are as follows. -/// ``` +/// ```text /// (Pre-creation) /// | /// | register_obligation_at() (called by process_obligations() and diff --git a/compiler/rustc_data_structures/src/owning_ref/mod.rs b/compiler/rustc_data_structures/src/owning_ref/mod.rs index e7397bf13ba..ed5e566184f 100644 --- a/compiler/rustc_data_structures/src/owning_ref/mod.rs +++ b/compiler/rustc_data_structures/src/owning_ref/mod.rs @@ -25,9 +25,8 @@ of the reference because the backing allocation of the vector does not change. This library enables this safe usage by keeping the owner and the reference bundled together in a wrapper type that ensure that lifetime constraint: -```rust -# extern crate owning_ref; -# use owning_ref::OwningRef; +``` +# use rustc_data_structures::owning_ref::OwningRef; # fn main() { fn return_owned_and_referenced() -> OwningRef<Vec<u8>, [u8]> { let v = vec![1, 2, 3, 4]; @@ -56,8 +55,7 @@ See the documentation around `OwningHandle` for more details. ## Basics ``` -extern crate owning_ref; -use owning_ref::BoxRef; +use rustc_data_structures::owning_ref::BoxRef; fn main() { // Create an array owned by a Box. @@ -78,8 +76,7 @@ fn main() { ## Caching a reference to a struct field ``` -extern crate owning_ref; -use owning_ref::BoxRef; +use rustc_data_structures::owning_ref::BoxRef; fn main() { struct Foo { @@ -106,8 +103,7 @@ fn main() { ## Caching a reference to an entry in a vector ``` -extern crate owning_ref; -use owning_ref::VecRef; +use rustc_data_structures::owning_ref::VecRef; fn main() { let v = VecRef::new(vec![1, 2, 3, 4, 5]).map(|v| &v[3]); @@ -118,8 +114,7 @@ fn main() { ## Caching a subslice of a String ``` -extern crate owning_ref; -use owning_ref::StringRef; +use rustc_data_structures::owning_ref::StringRef; fn main() { let s = StringRef::new("hello world".to_owned()) @@ -132,8 +127,7 @@ fn main() { ## Reference counted slices that share ownership of the backing storage ``` -extern crate owning_ref; -use owning_ref::RcRef; +use rustc_data_structures::owning_ref::RcRef; use std::rc::Rc; fn main() { @@ -155,8 +149,7 @@ fn main() { ## Atomic reference counted slices that share ownership of the backing storage ``` -extern crate owning_ref; -use owning_ref::ArcRef; +use rustc_data_structures::owning_ref::ArcRef; use std::sync::Arc; fn main() { @@ -188,8 +181,7 @@ fn main() { ## References into RAII locks ``` -extern crate owning_ref; -use owning_ref::RefRef; +use rustc_data_structures::owning_ref::RefRef; use std::cell::{RefCell, Ref}; fn main() { @@ -219,8 +211,7 @@ When the owned container implements `DerefMut`, it is also possible to make a _mutable owning reference_. (e.g., with `Box`, `RefMut`, `MutexGuard`) ``` -extern crate owning_ref; -use owning_ref::RefMutRefMut; +use rustc_data_structures::owning_ref::RefMutRefMut; use std::cell::{RefCell, RefMut}; fn main() { @@ -326,8 +317,7 @@ impl<O, T: ?Sized> OwningRef<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRef; + /// use rustc_data_structures::owning_ref::OwningRef; /// /// fn main() { /// let owning_ref = OwningRef::new(Box::new(42)); @@ -362,8 +352,7 @@ impl<O, T: ?Sized> OwningRef<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRef; + /// use rustc_data_structures::owning_ref::OwningRef; /// /// fn main() { /// let owning_ref = OwningRef::new(Box::new([1, 2, 3, 4])); @@ -390,8 +379,7 @@ impl<O, T: ?Sized> OwningRef<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRef; + /// use rustc_data_structures::owning_ref::OwningRef; /// /// fn main() { /// let owning_ref = OwningRef::new(Box::new([1, 2, 3, 4])); @@ -441,8 +429,7 @@ impl<O, T: ?Sized> OwningRef<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::{OwningRef, Erased}; + /// use rustc_data_structures::owning_ref::{OwningRef, Erased}; /// /// fn main() { /// // N.B., using the concrete types here for explicitness. @@ -460,7 +447,7 @@ impl<O, T: ?Sized> OwningRef<O, T> { /// let owning_ref_b: OwningRef<Box<Vec<(i32, bool)>>, i32> /// = owning_ref_b.map(|a| &a[1].0); /// - /// let owning_refs: [OwningRef<Box<Erased>, i32>; 2] + /// let owning_refs: [OwningRef<Box<dyn Erased>, i32>; 2] /// = [owning_ref_a.erase_owner(), owning_ref_b.erase_owner()]; /// /// assert_eq!(*owning_refs[0], 1); @@ -516,8 +503,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRefMut; + /// use rustc_data_structures::owning_ref::OwningRefMut; /// /// fn main() { /// let owning_ref_mut = OwningRefMut::new(Box::new(42)); @@ -552,8 +538,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRefMut; + /// use rustc_data_structures::owning_ref::OwningRefMut; /// /// fn main() { /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); @@ -580,8 +565,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRefMut; + /// use rustc_data_structures::owning_ref::OwningRefMut; /// /// fn main() { /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); @@ -608,8 +592,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRefMut; + /// use rustc_data_structures::owning_ref::OwningRefMut; /// /// fn main() { /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); @@ -638,8 +621,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::OwningRefMut; + /// use rustc_data_structures::owning_ref::OwningRefMut; /// /// fn main() { /// let owning_ref_mut = OwningRefMut::new(Box::new([1, 2, 3, 4])); @@ -689,8 +671,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// /// # Example /// ``` - /// extern crate owning_ref; - /// use owning_ref::{OwningRefMut, Erased}; + /// use rustc_data_structures::owning_ref::{OwningRefMut, Erased}; /// /// fn main() { /// // N.B., using the concrete types here for explicitness. @@ -708,7 +689,7 @@ impl<O, T: ?Sized> OwningRefMut<O, T> { /// let owning_ref_mut_b: OwningRefMut<Box<Vec<(i32, bool)>>, i32> /// = owning_ref_mut_b.map_mut(|a| &mut a[1].0); /// - /// let owning_refs_mut: [OwningRefMut<Box<Erased>, i32>; 2] + /// let owning_refs_mut: [OwningRefMut<Box<dyn Erased>, i32>; 2] /// = [owning_ref_mut_a.erase_owner(), owning_ref_mut_b.erase_owner()]; /// /// assert_eq!(*owning_refs_mut[0], 1); diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 3f7a90a8467..bf7924a81ff 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -737,11 +737,30 @@ impl Drop for VerboseTimingGuard<'_> { fn drop(&mut self) { if let Some((start_time, start_rss, ref message)) = self.start_and_message { let end_rss = get_resident_set_size(); - print_time_passes_entry(&message, start_time.elapsed(), start_rss, end_rss); + let dur = start_time.elapsed(); + + if should_print_passes(dur, start_rss, end_rss) { + print_time_passes_entry(&message, dur, start_rss, end_rss); + } } } } +fn should_print_passes(dur: Duration, start_rss: Option<usize>, end_rss: Option<usize>) -> bool { + if dur.as_millis() > 5 { + return true; + } + + if let (Some(start_rss), Some(end_rss)) = (start_rss, end_rss) { + let change_rss = end_rss.abs_diff(start_rss); + if change_rss > 0 { + return true; + } + } + + false +} + pub fn print_time_passes_entry( what: &str, dur: Duration, diff --git a/compiler/rustc_data_structures/src/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs index 6e5c0617bf3..abd25f46ad5 100644 --- a/compiler/rustc_data_structures/src/sip128.rs +++ b/compiler/rustc_data_structures/src/sip128.rs @@ -462,6 +462,14 @@ impl Hasher for SipHasher128 { self.slice_write(msg); } + #[inline] + fn write_str(&mut self, s: &str) { + // This hasher works byte-wise, and `0xFF` cannot show up in a `str`, + // so just hashing the one extra byte is enough to be prefix-free. + self.write(s.as_bytes()); + self.write_u8(0xFF); + } + fn finish(&self) -> u64 { panic!("SipHasher128 cannot provide valid 64 bit hashes") } diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 25353290fd5..c8bb4fc5e6a 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -74,6 +74,17 @@ impl Hasher for StableHasher { } #[inline] + fn write_str(&mut self, s: &str) { + self.state.write_str(s); + } + + #[inline] + fn write_length_prefix(&mut self, len: usize) { + // Our impl for `usize` will extend it if needed. + self.write_usize(len); + } + + #[inline] fn write_u8(&mut self, i: u8) { self.state.write_u8(i); } diff --git a/compiler/rustc_data_structures/src/tagged_ptr.rs b/compiler/rustc_data_structures/src/tagged_ptr.rs index 324a8624dd0..651bc556c98 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr.rs @@ -45,7 +45,8 @@ pub unsafe trait Pointer: Deref { /// case you'll need to manually figure out what the right type to pass to /// align_of is. /// - /// ```rust + /// ```ignore UNSOLVED (what to do about the Self) + /// # use std::ops::Deref; /// std::mem::align_of::<<Self as Deref>::Target>().trailing_zeros() as usize; /// ``` const BITS: usize; diff --git a/compiler/rustc_data_structures/src/transitive_relation.rs b/compiler/rustc_data_structures/src/transitive_relation.rs index 780753ed200..0ff64969b07 100644 --- a/compiler/rustc_data_structures/src/transitive_relation.rs +++ b/compiler/rustc_data_structures/src/transitive_relation.rs @@ -282,7 +282,7 @@ impl<T: Eq + Hash + Copy> TransitiveRelation<T> { /// (where the relation is encoding the `<=` relation for the lattice). /// So e.g., if the relation is `->` and we have /// - /// ``` + /// ```text /// a -> b -> d -> f /// | ^ /// +--> c -> e ---+ diff --git a/compiler/rustc_error_messages/locales/en-US/typeck.ftl b/compiler/rustc_error_messages/locales/en-US/typeck.ftl index 6a3235fc772..aef18fcafaa 100644 --- a/compiler/rustc_error_messages/locales/en-US/typeck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/typeck.ftl @@ -90,3 +90,14 @@ typeck-add-return-type-missing-here = a return type might be missing here typeck-expected-default-return-type = expected `()` because of default return type typeck-expected-return-type = expected `{$expected}` because of return type + +typeck-unconstrained-opaque-type = unconstrained opaque type + .note = `{$name}` must be used in combination with a concrete type within the same module + +typeck-explicit-generic-args-with-impl-trait = + cannot provide explicit generic arguments when `impl Trait` is used in argument position + .label = explicit generic argument not allowed + .note = see issue #83701 <https://github.com/rust-lang/rust/issues/83701> for more information + +typeck-explicit-generic-args-with-impl-trait-feature = + add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index df41fc00714..f83fa68ced0 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -100,7 +100,7 @@ pub struct CodeSuggestion { /// `foo.bar` might be replaced with `a.b` or `x.y` by replacing /// `foo` and `bar` on their own: /// - /// ``` + /// ```ignore (illustrative) /// vec![ /// Substitution { parts: vec![(0..3, "a"), (4..7, "b")] }, /// Substitution { parts: vec![(0..3, "x"), (4..7, "y")] }, @@ -109,7 +109,7 @@ pub struct CodeSuggestion { /// /// or by replacing the entire span: /// - /// ``` + /// ```ignore (illustrative) /// vec![ /// Substitution { parts: vec![(0..7, "a.b")] }, /// Substitution { parts: vec![(0..7, "x.y")] }, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index ae1b50a4176..2b30ec601a0 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -10,7 +10,7 @@ use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, Stability}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{self, Lrc}; -use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult}; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; use rustc_lint_defs::BuiltinLintDiagnostics; use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS}; @@ -20,7 +20,7 @@ use rustc_span::edition::Edition; use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{FileName, Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::default::Default; @@ -1136,6 +1136,43 @@ impl<'a> ExtCtxt<'a> { } } +/// Resolves a `path` mentioned inside Rust code, returning an absolute path. +/// +/// This unifies the logic used for resolving `include_X!`. +pub fn resolve_path( + parse_sess: &ParseSess, + path: impl Into<PathBuf>, + span: Span, +) -> PResult<'_, PathBuf> { + let path = path.into(); + + // Relative paths are resolved relative to the file in which they are found + // after macro expansion (that is, they are unhygienic). + if !path.is_absolute() { + let callsite = span.source_callsite(); + let mut result = match parse_sess.source_map().span_to_filename(callsite) { + FileName::Real(name) => name + .into_local_path() + .expect("attempting to resolve a file path in an external file"), + FileName::DocTest(path, _) => path, + other => { + return Err(parse_sess.span_diagnostic.struct_span_err( + span, + &format!( + "cannot resolve relative path in non-file source `{}`", + parse_sess.source_map().filename_for_diagnostics(&other) + ), + )); + } + }; + result.pop(); + result.push(path); + Ok(result) + } else { + Ok(path) + } +} + /// Extracts a string literal from the macro expanded version of `expr`, /// returning a diagnostic error of `err_msg` if `expr` is not a string literal. /// The returned bool indicates whether an applicable suggestion has already been diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 35b5e0d0f2f..8994a2f7891 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -4,7 +4,7 @@ //! //! ## Meta-variables must not be bound twice //! -//! ``` +//! ```compile_fail //! macro_rules! foo { ($x:tt $x:tt) => { $x }; } //! ``` //! @@ -604,9 +604,9 @@ fn check_ops_is_prefix( /// Kleene operators of its binder as a prefix. /// /// Consider $i in the following example: -/// -/// ( $( $i:ident = $($j:ident),+ );* ) => { $($( $i += $j; )+)* } -/// +/// ```ignore (illustrative) +/// ( $( $i:ident = $($j:ident),+ );* ) => { $($( $i += $j; )+)* } +/// ``` /// It occurs under the Kleene stack ["*", "+"] and is bound under ["*"] only. /// /// Arguments: diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 8f260e1cdb5..29f354d5728 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -320,7 +320,8 @@ pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize { /// /// Then, the tree will have the following shape: /// -/// ```rust +/// ```ignore (private-internal) +/// # use NamedMatch::*; /// MatchedSeq([ /// MatchedSeq([ /// MatchedNonterminal(a), diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 94b6c3153ca..fdd8dc93fc1 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -217,10 +217,10 @@ pub(super) fn transcribe<'a>( } // Replace the meta-var with the matched token tree from the invocation. - mbe::TokenTree::MetaVar(mut sp, mut orignal_ident) => { + mbe::TokenTree::MetaVar(mut sp, mut original_ident) => { // Find the matched nonterminal from the macro invocation, and use it to replace // the meta-var. - let ident = MacroRulesNormalizedIdent::new(orignal_ident); + let ident = MacroRulesNormalizedIdent::new(original_ident); if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) { match cur_matched { MatchedTokenTree(ref tt) => { @@ -249,9 +249,9 @@ pub(super) fn transcribe<'a>( // If we aren't able to match the meta-var, we push it back into the result but // with modified syntax context. (I believe this supports nested macros). marker.visit_span(&mut sp); - marker.visit_ident(&mut orignal_ident); + marker.visit_ident(&mut original_ident); result.push(TokenTree::token(token::Dollar, sp).into()); - result.push(TokenTree::Token(Token::from_ast_ident(orignal_ident)).into()); + result.push(TokenTree::Token(Token::from_ast_ident(original_ident)).into()); } } diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 9159d60463c..520769d308e 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -244,7 +244,6 @@ declare_features! ( // Unstable `#[target_feature]` directives. (active, aarch64_ver_target_feature, "1.27.0", Some(44839), None), - (active, adx_target_feature, "1.32.0", Some(44839), None), (active, arm_target_feature, "1.27.0", Some(44839), None), (active, avx512_target_feature, "1.27.0", Some(44839), None), (active, bpf_target_feature, "1.54.0", Some(44839), None), @@ -358,6 +357,8 @@ declare_features! ( (active, custom_inner_attributes, "1.30.0", Some(54726), None), /// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`. (active, custom_test_frameworks, "1.30.0", Some(50297), None), + /// Allows using `#[debugger_visualizer]`. + (active, debugger_visualizer, "1.62.0", Some(95939), None), /// Allows declarative macros 2.0 (`macro`). (active, decl_macro, "1.17.0", Some(39412), None), /// Allows rustc to inject a default alloc_error_handler diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index e588385cfca..33d2e82b24a 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -379,6 +379,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Unstable attributes: // ========================================================================== + // RFC #3191: #[debugger_visualizer] support + gated!( + debugger_visualizer, Normal, template!(List: r#"natvis_file = "...""#), + DuplicatesOk, experimental!(debugger_visualizer) + ), + // Linking: gated!(naked, Normal, template!(Word), WarnFollowing, naked_functions, experimental!(naked)), gated!( @@ -644,6 +650,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl." ), + rustc_attr!( + rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing, + "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \ + the given type by annotating all impl items with #[rustc_allow_incoherent_impl]." + ), BuiltinAttribute { name: sym::rustc_diagnostic_item, type_: Normal, diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 324e1100057..db8807bad40 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -312,6 +312,7 @@ pub enum Res<Id = hir::HirId> { /// HACK(min_const_generics): self types also have an optional requirement to **not** mention /// any generic parameters to allow the following with `min_const_generics`: /// ``` + /// # struct Foo; /// impl Foo { fn test() -> [u8; std::mem::size_of::<Self>()] { todo!() } } /// /// struct Bar([u8; baz::<Self>()]); @@ -663,4 +664,9 @@ impl<Id> Res<Id> { pub fn expected_in_tuple_struct_pat(&self) -> bool { matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..)) } + + /// Returns whether such a resolved path can occur in a unit struct/variant pattern + pub fn expected_in_unit_struct_pat(&self) -> bool { + matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Const), _) | Res::SelfCtor(..)) + } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index dfeee3f356f..2f379b85b92 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -49,15 +49,15 @@ pub enum ParamName { /// Synthetic name generated when user elided a lifetime in an impl header. /// /// E.g., the lifetimes in cases like these: - /// - /// impl Foo for &u32 - /// impl Foo<'_> for u32 - /// + /// ```ignore (fragment) + /// impl Foo for &u32 + /// impl Foo<'_> for u32 + /// ``` /// in that case, we rewrite to - /// - /// impl<'f> Foo for &'f u32 - /// impl<'f> Foo<'f> for u32 - /// + /// ```ignore (fragment) + /// impl<'f> Foo for &'f u32 + /// impl<'f> Foo<'f> for u32 + /// ``` /// where `'f` is something like `Fresh(0)`. The indices are /// unique per impl, but not necessarily continuous. Fresh(LocalDefId), @@ -1082,7 +1082,7 @@ pub enum PatKind<'hir> { /// If `slice` exists, then `after` can be non-empty. /// /// The representation for e.g., `[a, b, .., c, d]` is: - /// ``` + /// ```ignore (illustrative) /// PatKind::Slice([Binding(a), Binding(b)], Some(Wild), [Binding(c), Binding(d)]) /// ``` Slice(&'hir [Pat<'hir>], Option<&'hir Pat<'hir>>, &'hir [Pat<'hir>]), @@ -1441,7 +1441,7 @@ pub enum AsyncGeneratorKind { /// An explicit `async` block written by the user. Block, - /// An explicit `async` block written by the user. + /// An explicit `async` closure written by the user. Closure, /// The `async` block generated as the body of an async function. @@ -2078,10 +2078,7 @@ pub enum YieldSource { impl YieldSource { pub fn is_await(&self) -> bool { - match self { - YieldSource::Await { .. } => true, - YieldSource::Yield => false, - } + matches!(self, YieldSource::Await { .. }) } } @@ -2247,7 +2244,7 @@ pub const FN_OUTPUT_NAME: Symbol = sym::Output; /// wouldn't it be better to make the `ty` field an enum like the /// following? /// -/// ``` +/// ```ignore (pseudo-rust) /// enum TypeBindingKind { /// Equals(...), /// Binding(...), diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index b3c22d4ec21..9e5d7818924 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -327,8 +327,6 @@ language_item_table! { Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None; RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None; RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None; - - CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; } pub enum GenericRequirement { diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 2d06c9d8ec9..f69ae8ebe41 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -22,7 +22,7 @@ //! //! Example: //! -//! ``` +//! ```ignore (needs flags) //! #[rustc_if_this_changed(Hir)] //! fn foo() { } //! diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index 7569abfbb10..61b1dd8cb01 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -5,6 +5,7 @@ //! The user adds annotations to the crate of the following form: //! //! ``` +//! # #![feature(rustc_attrs)] //! #![rustc_partition_reused(module="spike", cfg="rpass2")] //! #![rustc_partition_codegened(module="spike-x", cfg="rpass2")] //! ``` diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 58c309a5c52..39f7d30e81a 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -6,7 +6,7 @@ //! is always the "expected" output from the POV of diagnostics. //! //! Examples: -//! +//! ```ignore (fragment) //! infcx.at(cause, param_env).sub(a, b) //! // requires that `a <: b`, with `a` considered the "expected" type //! @@ -15,11 +15,11 @@ //! //! infcx.at(cause, param_env).eq(a, b) //! // requires that `a == b`, with `a` considered the "expected" type -//! +//! ``` //! For finer-grained control, you can also do use `trace`: -//! +//! ```ignore (fragment) //! infcx.at(...).trace(a, b).sub(&c, &d) -//! +//! ``` //! This will set `a` and `b` as the "root" values for //! error-reporting, but actually operate on `c` and `d`. This is //! sometimes useful when the types of `c` and `d` are not traceable diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 5e67c8cfa27..07e51afd904 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -87,9 +87,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// /// with a mapping M that maps `'?0` to `'static`. But if we found that there /// exists only one possible impl of `Trait`, and it looks like - /// - /// impl<T> Trait<'static> for T { .. } - /// + /// ```ignore (illustrative) + /// impl<T> Trait<'static> for T { .. } + /// ``` /// then we would prepare a query result R that (among other /// things) includes a mapping to `'?0 := 'static`. When /// canonicalizing this query result R, we would leave this diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 0ca0fe33614..aa3f0600ccc 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -202,7 +202,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { /// /// A good example of this is the following: /// - /// ```rust + /// ```compile_fail,E0308 /// #![feature(generic_const_exprs)] /// /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] { diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 2e50dbff510..af81f5cde2c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -889,7 +889,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// /// For the following code: /// - /// ```no_run + /// ```ignore (illustrative) /// let x: Foo<Bar<Qux>> = foo::<Bar<Qux>>(); /// ``` /// @@ -1584,9 +1584,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Variable(ty::error::ExpectedFound<Ty<'a>>), Fixed(&'static str), } - let (expected_found, exp_found, is_simple_error) = match values { - None => (None, Mismatch::Fixed("type"), false), + let (expected_found, exp_found, is_simple_error, values) = match values { + None => (None, Mismatch::Fixed("type"), false, None), Some(values) => { + let values = self.resolve_vars_if_possible(values); let (is_simple_error, exp_found) = match values { ValuePairs::Terms(infer::ExpectedFound { expected: ty::Term::Ty(expected), @@ -1614,7 +1615,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return; } }; - (vals, exp_found, is_simple_error) + (vals, exp_found, is_simple_error, Some(values)) } }; @@ -1872,7 +1873,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// A possible error is to forget to add `.await` when using futures: /// - /// ``` + /// ```compile_fail,E0308 /// async fn make_u32() -> u32 { /// 22 /// } 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 b1eb9f0da87..78e0864d918 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 @@ -731,6 +731,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // | help: specify type like: `<Impl as Into<u32>>::into(foo_impl)` // | // = note: cannot satisfy `Impl: Into<_>` + debug!(?segment); if !impl_candidates.is_empty() && e.span.contains(span) && let Some(expr) = exprs.first() && let ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind @@ -739,9 +740,29 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let mut eraser = TypeParamEraser(self.tcx); let candidate_len = impl_candidates.len(); let mut suggestions: Vec<_> = impl_candidates.iter().map(|candidate| { + let trait_item = self.tcx + .associated_items(candidate.def_id) + .find_by_name_and_kind( + self.tcx, + segment.ident, + ty::AssocKind::Fn, + candidate.def_id + ); + let prefix = if let Some(trait_item) = trait_item + && let Some(trait_m) = trait_item.def_id.as_local() + && let hir::TraitItemKind::Fn(fn_, _) = &self.tcx.hir().trait_item(hir::TraitItemId { def_id: trait_m }).kind + { + match fn_.decl.implicit_self { + hir::ImplicitSelfKind::ImmRef => "&", + hir::ImplicitSelfKind::MutRef => "&mut ", + _ => "", + } + } else { + "" + }; let candidate = candidate.super_fold_with(&mut eraser); vec![ - (expr.span.shrink_to_lo(), format!("{}::{}(", candidate, segment.ident)), + (expr.span.shrink_to_lo(), format!("{}::{}({}", candidate, segment.ident, prefix)), if exprs.len() == 1 { (expr.span.shrink_to_hi().with_hi(e.span.hi()), ")".to_string()) } else { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index be9db6aa25b..cb72cb41a7c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -18,7 +18,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// /// Consider a case where we have /// - /// ```no_run + /// ```compile_fail,E0623 /// fn foo(x: &mut Vec<&u8>, y: &u8) { /// x.push(y); /// } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs index 9c5b944c1e9..e5ae835e813 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::{self, Region, TyCtxt}; /// br - the bound region corresponding to the above region which is of type `BrAnon(_)` /// /// # Example -/// ``` +/// ```compile_fail,E0623 /// fn foo(x: &mut Vec<&u8>, y: &u8) /// { x.push(y); } /// ``` diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 6ec929f9895..c9121f7d348 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -159,9 +159,9 @@ pub struct InferCtxtInner<'tcx> { /// outlive the lifetime 'a". These constraints derive from /// instantiated type parameters. So if you had a struct defined /// like - /// + /// ```ignore (illustrative) /// struct Foo<T:'static> { ... } - /// + /// ``` /// then in some expression `let x = Foo { ... }` it will /// instantiate the type parameter `T` with a fresh type `$0`. At /// the same time, it will record a region obligation of diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 78e6f3a05be..d25484efabc 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -309,14 +309,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// the same as generating an outlives constraint on `Tc` itself. /// For example, if we had a function like this: /// - /// ```rust + /// ``` + /// # #![feature(type_alias_impl_trait)] + /// # fn main() {} + /// # trait Foo<'a> {} + /// # impl<'a, T> Foo<'a> for (&'a u32, T) {} /// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> { /// (x, y) /// } /// /// // Equivalent to: + /// # mod dummy { use super::*; /// type FooReturn<'a, T> = impl Foo<'a>; - /// fn foo<'a, T>(..) -> FooReturn<'a, T> { .. } + /// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> { + /// (x, y) + /// } + /// # } /// ``` /// /// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0` @@ -602,17 +610,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Returns `true` if `opaque_hir_id` is a sibling or a child of a sibling of `def_id`. /// /// Example: -/// ```rust +/// ```ignore UNSOLVED (is this a bug?) +/// # #![feature(type_alias_impl_trait)] /// pub mod foo { /// pub mod bar { -/// pub trait Bar { .. } -/// +/// pub trait Bar { /* ... */ } /// pub type Baz = impl Bar; /// -/// fn f1() -> Baz { .. } +/// # impl Bar for () {} +/// fn f1() -> Baz { /* ... */ } /// } -/// -/// fn f2() -> bar::Baz { .. } +/// fn f2() -> bar::Baz { /* ... */ } /// } /// ``` /// diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index c8236ce79c0..9ddda7b92eb 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -103,7 +103,7 @@ impl<'a, 'tcx> OutlivesEnvironment<'tcx> { /// /// Example: /// - /// ``` + /// ```ignore (pseudo-rust) /// fn foo<T>() { /// callback(for<'a> |x: &'a T| { /// // ^^^^^^^ not legal syntax, but probably should be diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index e3312e6c6e1..19d03ffa696 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -33,9 +33,9 @@ //! Consider: //! //! ``` -//! fn bar<T>(a: T, b: impl for<'a> Fn(&'a T)); +//! fn bar<T>(a: T, b: impl for<'a> Fn(&'a T)) {} //! fn foo<T>(x: T) { -//! bar(x, |y| { ... }) +//! bar(x, |y| { /* ... */}) //! // ^ closure arg //! } //! ``` diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index f69212c599b..ab565d43961 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -313,7 +313,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// /// It will not, however, work for higher-ranked bounds like: /// - /// ```rust + /// ```compile_fail,E0311 /// trait Foo<'a, 'b> /// where for<'x> <Self as Foo<'x, 'b>>::Bar: 'x /// { diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 4ea1c3f76c8..efe254387dc 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -174,19 +174,19 @@ pub enum GenericKind<'tcx> { /// Describes the things that some `GenericKind` value `G` is known to /// outlive. Each variant of `VerifyBound` can be thought of as a /// function: -/// -/// fn(min: Region) -> bool { .. } -/// +/// ```ignore (pseudo-rust) +/// fn(min: Region) -> bool { .. } +/// ``` /// where `true` means that the region `min` meets that `G: min`. /// (False means nothing.) /// /// So, for example, if we have the type `T` and we have in scope that /// `T: 'a` and `T: 'b`, then the verify bound might be: -/// -/// fn(min: Region) -> bool { -/// ('a: min) || ('b: min) -/// } -/// +/// ```ignore (pseudo-rust) +/// fn(min: Region) -> bool { +/// ('a: min) || ('b: min) +/// } +/// ``` /// This is described with an `AnyRegion('a, 'b)` node. #[derive(Debug, Clone)] pub enum VerifyBound<'tcx> { @@ -194,7 +194,7 @@ pub enum VerifyBound<'tcx> { /// following, where `G` is the generic for which this verify /// bound was created: /// - /// ```rust + /// ```ignore (pseudo-rust) /// fn(min) -> bool { /// if G == K { /// B(min) @@ -218,7 +218,7 @@ pub enum VerifyBound<'tcx> { /// /// So we would compile to a verify-bound like /// - /// ``` + /// ```ignore (illustrative) /// IfEq(<T as Trait<'a>>::Item, AnyRegion('a)) /// ``` /// @@ -228,7 +228,7 @@ pub enum VerifyBound<'tcx> { /// Given a region `R`, expands to the function: /// - /// ``` + /// ```ignore (pseudo-rust) /// fn(min) -> bool { /// R: min /// } @@ -243,7 +243,7 @@ pub enum VerifyBound<'tcx> { /// Given a set of bounds `B`, expands to the function: /// - /// ```rust + /// ```ignore (pseudo-rust) /// fn(min) -> bool { /// exists (b in B) { b(min) } /// } @@ -255,7 +255,7 @@ pub enum VerifyBound<'tcx> { /// Given a set of bounds `B`, expands to the function: /// - /// ```rust + /// ```ignore (pseudo-rust) /// fn(min) -> bool { /// forall (b in B) { b(min) } /// } diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 74c44089045..a0e2965b605 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -73,10 +73,10 @@ pub struct TypeVariableStorage<'tcx> { /// table exists only to help with the occurs check. In particular, /// we want to report constraints like these as an occurs check /// violation: - /// - /// ?1 <: ?3 - /// Box<?3> <: ?1 - /// + /// ``` text + /// ?1 <: ?3 + /// Box<?3> <: ?1 + /// ``` /// Without this second table, what would happen in a case like /// this is that we would instantiate `?1` with a generalized /// type like `Box<?6>`. We would then relate `Box<?3> <: Box<?6>` @@ -287,8 +287,9 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { /// related via equality or subtyping will yield the same root /// variable (per the union-find algorithm), so `sub_root_var(a) /// == sub_root_var(b)` implies that: - /// - /// exists X. (a <: X || X <: a) && (b <: X || X <: b) + /// ```text + /// exists X. (a <: X || X <: a) && (b <: X || X <: b) + /// ``` pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { self.sub_relations().find(vid) } diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index e859bcaec12..4c691c25189 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -14,7 +14,6 @@ #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![cfg_attr(bootstrap, feature(derive_default_enum))] diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index f847c08a9dc..40e02f47bd1 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(let_else)] #![feature(internal_output_capture)] diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 18f229564c2..028c14366c6 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -28,7 +28,6 @@ #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(array_windows)] -#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(crate_visibility_modifier)] @@ -491,6 +490,11 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { "converted into hard error, see RFC 2972 \ <https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md> for more information", ); + store.register_removed( + "mutable_borrow_reservation_conflict", + "now allowed, see issue #59159 \ + <https://github.com/rust-lang/rust/issues/59159> for more information", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 71769fceec1..4e7aeca9ce1 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -254,7 +254,10 @@ fn check_panic_str<'tcx>( if n_arguments > 0 && fmt_parser.errors.is_empty() { let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] { [] => vec![fmt_span], - v => v.iter().map(|span| fmt_span.from_inner(*span)).collect(), + v => v + .iter() + .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end))) + .collect(), }; cx.struct_span_lint(NON_FMT_PANICS, arg_spans, |lint| { let mut l = lint.build(match n_arguments { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a42e3d5d957..b8ba65adaa8 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1109,7 +1109,8 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```compile_fail + /// #![deny(unaligned_references)] /// #[repr(packed)] /// pub struct Foo { /// field1: u64, @@ -2346,40 +2347,6 @@ declare_lint! { } declare_lint! { - /// The `mutable_borrow_reservation_conflict` lint detects the reservation - /// of a two-phased borrow that conflicts with other shared borrows. - /// - /// ### Example - /// - /// ```rust - /// let mut v = vec![0, 1, 2]; - /// let shared = &v; - /// v.push(shared.len()); - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This is a [future-incompatible] lint to transition this to a hard error - /// in the future. See [issue #59159] for a complete description of the - /// problem, and some possible solutions. - /// - /// [issue #59159]: https://github.com/rust-lang/rust/issues/59159 - /// [future-incompatible]: ../index.md#future-incompatible-lints - pub MUTABLE_BORROW_RESERVATION_CONFLICT, - Warn, - "reservation of a two-phased borrow conflicts with other shared borrows", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::Custom( - "this borrowing pattern was not meant to be accepted, \ - and may become a hard error in the future" - ), - reference: "issue #59159 <https://github.com/rust-lang/rust/issues/59159>", - }; -} - -declare_lint! { /// The `soft_unstable` lint detects unstable features that were /// unintentionally allowed on stable. /// @@ -3179,7 +3146,6 @@ declare_lint_pass! { META_VARIABLE_MISUSE, DEPRECATED_IN_FUTURE, AMBIGUOUS_ASSOCIATED_ITEMS, - MUTABLE_BORROW_RESERVATION_CONFLICT, INDIRECT_STRUCTURAL_MATCH, POINTER_STRUCTURAL_MATCH, NONTRIVIAL_STRUCTURAL_MATCH, diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index f49166433fa..83fc7bcde8a 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -5,8 +5,8 @@ use crate::diagnostics::error::{ SessionDiagnosticDeriveError, }; use crate::diagnostics::utils::{ - option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, Applicability, - FieldInfo, HasFieldMap, SetOnce, + report_error_if_not_applied_to_span, type_matches_path, Applicability, FieldInfo, FieldInnerTy, + HasFieldMap, SetOnce, }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; @@ -353,35 +353,40 @@ impl SessionDiagnosticDeriveBuilder { info: FieldInfo<'_>, ) -> Result<TokenStream, SessionDiagnosticDeriveError> { let field_binding = &info.binding.binding; - let option_ty = option_inner_ty(&info.ty); - let generated_code = self.generate_non_option_field_code( + + let inner_ty = FieldInnerTy::from_type(&info.ty); + let name = attr.path.segments.last().unwrap().ident.to_string(); + let (binding, needs_destructure) = match (name.as_str(), &inner_ty) { + // `primary_span` can accept a `Vec<Span>` so don't destructure that. + ("primary_span", FieldInnerTy::Vec(_)) => (quote! { #field_binding.clone() }, false), + _ => (quote! { *#field_binding }, true), + }; + + let generated_code = self.generate_inner_field_code( attr, FieldInfo { vis: info.vis, binding: info.binding, - ty: option_ty.unwrap_or(&info.ty), + ty: inner_ty.inner_type().unwrap_or(&info.ty), span: info.span, }, + binding, )?; - if option_ty.is_none() { - Ok(quote! { #generated_code }) + if needs_destructure { + Ok(inner_ty.with(field_binding, generated_code)) } else { - Ok(quote! { - if let Some(#field_binding) = #field_binding { - #generated_code - } - }) + Ok(generated_code) } } - fn generate_non_option_field_code( + fn generate_inner_field_code( &mut self, attr: &Attribute, info: FieldInfo<'_>, + binding: TokenStream, ) -> Result<TokenStream, SessionDiagnosticDeriveError> { let diag = &self.diag; - let field_binding = &info.binding.binding; let name = attr.path.segments.last().unwrap().ident.to_string(); let name = name.as_str(); @@ -397,14 +402,14 @@ impl SessionDiagnosticDeriveBuilder { "primary_span" => { report_error_if_not_applied_to_span(attr, &info)?; Ok(quote! { - #diag.set_span(*#field_binding); + #diag.set_span(#binding); }) } "label" | "note" | "help" => { report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_subdiagnostic(field_binding, name, name)) + Ok(self.add_subdiagnostic(binding, name, name)) } - "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(*#field_binding); }), + "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }), _ => throw_invalid_attr!(attr, &meta, |diag| { diag .help("only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` are valid field attributes") @@ -413,7 +418,7 @@ impl SessionDiagnosticDeriveBuilder { Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name { "label" | "note" | "help" => { report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_subdiagnostic(field_binding, name, &s.value())) + Ok(self.add_subdiagnostic(binding, name, &s.value())) } _ => throw_invalid_attr!(attr, &meta, |diag| { diag.help("only `label`, `note` and `help` are valid field attributes") @@ -509,7 +514,7 @@ impl SessionDiagnosticDeriveBuilder { /// `fluent_attr_identifier`. fn add_subdiagnostic( &self, - field_binding: &proc_macro2::Ident, + field_binding: TokenStream, kind: &str, fluent_attr_identifier: &str, ) -> TokenStream { @@ -520,7 +525,7 @@ impl SessionDiagnosticDeriveBuilder { let fn_name = format_ident!("span_{}", kind); quote! { #diag.#fn_name( - *#field_binding, + #field_binding, rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier) ); } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 961b42f424f..65b1328682f 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -5,8 +5,8 @@ use crate::diagnostics::error::{ SessionDiagnosticDeriveError, }; use crate::diagnostics::utils::{ - option_inner_ty, report_error_if_not_applied_to_applicability, - report_error_if_not_applied_to_span, Applicability, FieldInfo, HasFieldMap, SetOnce, + report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, + Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; @@ -301,11 +301,11 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { ) -> Result<TokenStream, SessionDiagnosticDeriveError> { let ast = binding.ast(); - let option_ty = option_inner_ty(&ast.ty); + let inner_ty = FieldInnerTy::from_type(&ast.ty); let info = FieldInfo { vis: &ast.vis, binding: binding, - ty: option_ty.unwrap_or(&ast.ty), + ty: inner_ty.inner_type().unwrap_or(&ast.ty), span: &ast.span(), }; @@ -353,15 +353,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { ); }; - if option_ty.is_none() { - Ok(quote! { #generated }) - } else { - Ok(quote! { - if let Some(#binding) = #binding { - #generated - } - }) - } + Ok(inner_ty.with(binding, generated)) } fn into_tokens(&mut self) -> Result<TokenStream, SessionDiagnosticDeriveError> { diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 1f36af0a20b..aba861fc6aa 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -1,7 +1,7 @@ use crate::diagnostics::error::{span_err, throw_span_err, SessionDiagnosticDeriveError}; use proc_macro::Span; use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use quote::{format_ident, quote, ToTokens}; use std::collections::BTreeSet; use std::str::FromStr; use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility}; @@ -76,22 +76,71 @@ pub(crate) fn report_error_if_not_applied_to_span( report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "Span") } -/// If `ty` is an Option, returns `Some(inner type)`, otherwise returns `None`. -pub(crate) fn option_inner_ty(ty: &Type) -> Option<&Type> { - if type_matches_path(ty, &["std", "option", "Option"]) { +/// Inner type of a field and type of wrapper. +pub(crate) enum FieldInnerTy<'ty> { + /// Field is wrapped in a `Option<$inner>`. + Option(&'ty Type), + /// Field is wrapped in a `Vec<$inner>`. + Vec(&'ty Type), + /// Field isn't wrapped in an outer type. + None, +} + +impl<'ty> FieldInnerTy<'ty> { + /// Returns inner type for a field, if there is one. + /// + /// - If `ty` is an `Option`, returns `FieldInnerTy::Option { inner: (inner type) }`. + /// - If `ty` is a `Vec`, returns `FieldInnerTy::Vec { inner: (inner type) }`. + /// - Otherwise returns `None`. + pub(crate) fn from_type(ty: &'ty Type) -> Self { + let variant: &dyn Fn(&'ty Type) -> FieldInnerTy<'ty> = + if type_matches_path(ty, &["std", "option", "Option"]) { + &FieldInnerTy::Option + } else if type_matches_path(ty, &["std", "vec", "Vec"]) { + &FieldInnerTy::Vec + } else { + return FieldInnerTy::None; + }; + if let Type::Path(ty_path) = ty { let path = &ty_path.path; let ty = path.segments.iter().last().unwrap(); if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments { if bracketed.args.len() == 1 { if let syn::GenericArgument::Type(ty) = &bracketed.args[0] { - return Some(ty); + return variant(ty); } } } } + + unreachable!(); + } + + /// Returns `Option` containing inner type if there is one. + pub(crate) fn inner_type(&self) -> Option<&'ty Type> { + match self { + FieldInnerTy::Option(inner) | FieldInnerTy::Vec(inner) => Some(inner), + FieldInnerTy::None => None, + } + } + + /// Surrounds `inner` with destructured wrapper type, exposing inner type as `binding`. + pub(crate) fn with(&self, binding: impl ToTokens, inner: impl ToTokens) -> TokenStream { + match self { + FieldInnerTy::Option(..) => quote! { + if let Some(#binding) = #binding { + #inner + } + }, + FieldInnerTy::Vec(..) => quote! { + for #binding in #binding { + #inner + } + }, + FieldInnerTy::None => quote! { #inner }, + } } - None } /// Field information passed to the builder. Deliberately omits attrs to discourage the diff --git a/compiler/rustc_metadata/src/foreign_modules.rs b/compiler/rustc_metadata/src/foreign_modules.rs index 97fcbeb4ccc..70da96154b0 100644 --- a/compiler/rustc_metadata/src/foreign_modules.rs +++ b/compiler/rustc_metadata/src/foreign_modules.rs @@ -6,7 +6,7 @@ use rustc_session::cstore::ForeignModule; crate fn collect(tcx: TyCtxt<'_>) -> Vec<ForeignModule> { let mut modules = Vec::new(); for id in tcx.hir().items() { - if !matches!(tcx.hir().def_kind(id.def_id), DefKind::ForeignMod) { + if !matches!(tcx.def_kind(id.def_id), DefKind::ForeignMod) { continue; } let item = tcx.hir().item(id); diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 43b6ecee794..f468399930d 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -36,7 +36,7 @@ struct Collector<'tcx> { impl<'tcx> Collector<'tcx> { fn process_item(&mut self, id: rustc_hir::ItemId) { - if !matches!(self.tcx.hir().def_kind(id.def_id), DefKind::ForeignMod) { + if !matches!(self.tcx.def_kind(id.def_id), DefKind::ForeignMod) { return; } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 1edb62e189f..b25522cfd96 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1023,6 +1023,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.root.tables.expn_that_defined.get(self, id).unwrap().decode((self, sess)) } + fn get_debugger_visualizers(self) -> Vec<rustc_span::DebuggerVisualizerFile> { + self.root.debugger_visualizers.decode(self).collect::<Vec<_>>() + } + /// Iterates over all the stability attributes in the given crate. fn get_lib_features(self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Option<Symbol>)] { tcx.arena.alloc_from_iter(self.root.lib_features.decode(self)) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index da1dd6af65a..c00c6ce2f71 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -233,6 +233,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, } used_crate_source => { Lrc::clone(&cdata.source) } + debugger_visualizers => { cdata.get_debugger_visualizers() } exported_symbols => { let syms = cdata.exported_symbols(tcx); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b46ea955a3a..77d6ce1e766 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -35,7 +35,9 @@ use rustc_serialize::{opaque, Encodable, Encoder}; use rustc_session::config::CrateType; use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib}; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SyntaxContext}; +use rustc_span::{ + self, DebuggerVisualizerFile, ExternalSource, FileName, SourceFile, Span, SyntaxContext, +}; use rustc_span::{ hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind}, RealFileName, @@ -672,6 +674,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let tables = self.tables.encode(&mut self.opaque); let tables_bytes = self.position() - i; + i = self.position(); + let debugger_visualizers = self.encode_debugger_visualizers(); + let debugger_visualizers_bytes = self.position() - i; + // Encode exported symbols info. This is prefetched in `encode_metadata` so we encode // this as late as possible to give the prefetching as much time as possible to complete. i = self.position(); @@ -717,6 +723,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE), has_default_lib_allocator, proc_macro_data, + debugger_visualizers, compiler_builtins: tcx.sess.contains_name(&attrs, sym::compiler_builtins), needs_allocator: tcx.sess.contains_name(&attrs, sym::needs_allocator), needs_panic_runtime: tcx.sess.contains_name(&attrs, sym::needs_panic_runtime), @@ -757,25 +764,26 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } eprintln!("metadata stats:"); - eprintln!(" dep bytes: {}", dep_bytes); - eprintln!(" lib feature bytes: {}", lib_feature_bytes); - eprintln!(" lang item bytes: {}", lang_item_bytes); - eprintln!(" diagnostic item bytes: {}", diagnostic_item_bytes); - eprintln!(" native bytes: {}", native_lib_bytes); - eprintln!(" source_map bytes: {}", source_map_bytes); - eprintln!(" traits bytes: {}", traits_bytes); - eprintln!(" impls bytes: {}", impls_bytes); - eprintln!("incoherent_impls bytes: {}", incoherent_impls_bytes); - eprintln!(" exp. symbols bytes: {}", exported_symbols_bytes); - eprintln!(" def-path table bytes: {}", def_path_table_bytes); - eprintln!(" def-path hashes bytes: {}", def_path_hash_map_bytes); - eprintln!(" proc-macro-data-bytes: {}", proc_macro_data_bytes); - eprintln!(" mir bytes: {}", mir_bytes); - eprintln!(" item bytes: {}", item_bytes); - eprintln!(" table bytes: {}", tables_bytes); - eprintln!(" hygiene bytes: {}", hygiene_bytes); - eprintln!(" zero bytes: {}", zero_bytes); - eprintln!(" total bytes: {}", total_bytes); + eprintln!(" dep bytes: {}", dep_bytes); + eprintln!(" lib feature bytes: {}", lib_feature_bytes); + eprintln!(" lang item bytes: {}", lang_item_bytes); + eprintln!(" diagnostic item bytes: {}", diagnostic_item_bytes); + eprintln!(" native bytes: {}", native_lib_bytes); + eprintln!(" debugger visualizers bytes: {}", debugger_visualizers_bytes); + eprintln!(" source_map bytes: {}", source_map_bytes); + eprintln!(" traits bytes: {}", traits_bytes); + eprintln!(" impls bytes: {}", impls_bytes); + eprintln!(" incoherent_impls bytes: {}", incoherent_impls_bytes); + eprintln!(" exp. symbols bytes: {}", exported_symbols_bytes); + eprintln!(" def-path table bytes: {}", def_path_table_bytes); + eprintln!(" def-path hashes bytes: {}", def_path_hash_map_bytes); + eprintln!(" proc-macro-data-bytes: {}", proc_macro_data_bytes); + eprintln!(" mir bytes: {}", mir_bytes); + eprintln!(" item bytes: {}", item_bytes); + eprintln!(" table bytes: {}", tables_bytes); + eprintln!(" hygiene bytes: {}", hygiene_bytes); + eprintln!(" zero bytes: {}", zero_bytes); + eprintln!(" total bytes: {}", total_bytes); } root @@ -1716,6 +1724,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } + fn encode_debugger_visualizers(&mut self) -> Lazy<[DebuggerVisualizerFile]> { + empty_proc_macro!(self); + self.lazy(self.tcx.debugger_visualizers(LOCAL_CRATE).iter()) + } + fn encode_crate_deps(&mut self) -> Lazy<[CrateDep]> { empty_proc_macro!(self); @@ -1800,7 +1813,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { FxHashMap::default(); for id in tcx.hir().items() { - if matches!(tcx.hir().def_kind(id.def_id), DefKind::Impl) { + if matches!(tcx.def_kind(id.def_id), DefKind::Impl) { if let Some(trait_ref) = tcx.impl_trait_ref(id.def_id.to_def_id()) { let simplified_self_ty = fast_reject::simplify_type( self.tcx, diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index cdbed90e6b9..a0fd9ef4f87 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -219,6 +219,7 @@ crate struct CrateRoot<'tcx> { proc_macro_data: Option<ProcMacroData>, tables: LazyTables<'tcx>, + debugger_visualizers: Lazy<[rustc_span::DebuggerVisualizerFile]>, exported_symbols: Lazy!([(ExportedSymbol<'tcx>, SymbolExportInfo)]), diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 85e53559c29..20c8b0bb70f 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -308,11 +308,6 @@ impl<'hir> Map<'hir> { Some(def_kind) } - pub fn def_kind(self, local_def_id: LocalDefId) -> DefKind { - self.opt_def_kind(local_def_id) - .unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", local_def_id)) - } - pub fn find_parent_node(self, id: HirId) -> Option<HirId> { if id.local_id == ItemLocalId::from_u32(0) { Some(self.tcx.hir_owner_parent(id.owner)) @@ -742,7 +737,7 @@ impl<'hir> Map<'hir> { /// } /// ``` /// - /// ``` + /// ```compile_fail,E0308 /// fn foo(x: usize) -> bool { /// loop { /// true // If `get_return_block` gets passed the `id` corresponding diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs index 497d3811f28..2350a6ab155 100644 --- a/compiler/rustc_middle/src/infer/mod.rs +++ b/compiler/rustc_middle/src/infer/mod.rs @@ -10,7 +10,7 @@ use rustc_span::Span; /// Requires that `region` must be equal to one of the regions in `choice_regions`. /// We often denote this using the syntax: /// -/// ``` +/// ```text /// R0 member of [O1..On] /// ``` #[derive(Debug, Clone, HashStable, TypeFoldable, Lift)] diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 199b5fa0314..04529ed1161 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -27,7 +27,6 @@ #![feature(array_windows)] #![feature(assert_matches)] #![feature(backtrace)] -#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(core_intrinsics)] #![cfg_attr(bootstrap, feature(derive_default_enum))] diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 8cfc5ed0a95..5564852f305 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -770,11 +770,11 @@ impl InitMask { /// /// Note that all examples below are written with 8 (instead of 64) bit blocks for simplicity, /// and with the least significant bit (and lowest block) first: - /// - /// 00000000|00000000 - /// ^ ^ ^ ^ - /// index: 0 7 8 15 - /// + /// ```text + /// 00000000|00000000 + /// ^ ^ ^ ^ + /// index: 0 7 8 15 + /// ``` /// Also, if not stated, assume that `is_init = true`, that is, we are searching for the first 1 bit. fn find_bit_fast( init_mask: &InitMask, diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 0dff71be4b8..682f3734d30 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -737,14 +737,14 @@ pub enum BorrowKind { /// This is used when lowering matches: when matching on a place we want to /// ensure that place have the same value from the start of the match until /// an arm is selected. This prevents this code from compiling: - /// - /// let mut x = &Some(0); - /// match *x { - /// None => (), - /// Some(_) if { x = &None; false } => (), - /// Some(_) => (), - /// } - /// + /// ```compile_fail,E0510 + /// let mut x = &Some(0); + /// match *x { + /// None => (), + /// Some(_) if { x = &None; false } => (), + /// Some(_) => (), + /// } + /// ``` /// This can't be a shared borrow because mutably borrowing (*x as Some).0 /// should not prevent `if let None = x { ... }`, for example, because the /// mutating `(*x as Some).0` can't affect the discriminant of `x`. @@ -755,27 +755,30 @@ pub enum BorrowKind { /// cannot currently be expressed by the user and is used only in /// implicit closure bindings. It is needed when the closure is /// borrowing or mutating a mutable referent, e.g.: - /// - /// let x: &mut isize = ...; - /// let y = || *x += 5; - /// + /// ``` + /// let mut z = 3; + /// let x: &mut isize = &mut z; + /// let y = || *x += 5; + /// ``` /// If we were to try to translate this closure into a more explicit /// form, we'd encounter an error with the code as written: - /// - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// + /// ```compile_fail,E0594 + /// struct Env<'a> { x: &'a &'a mut isize } + /// let mut z = 3; + /// let x: &mut isize = &mut z; + /// let y = (&mut Env { x: &x }, fn_ptr); // Closure is pair of env and fn + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// ``` /// This is then illegal because you cannot mutate an `&mut` found /// in an aliasable location. To solve, you'd have to translate with /// an `&mut` borrow: - /// - /// struct Env { x: &mut &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x - /// fn fn_ptr(env: &mut Env) { **env.x += 5; } - /// + /// ```compile_fail,E0596 + /// struct Env<'a> { x: &'a mut &'a mut isize } + /// let mut z = 3; + /// let x: &mut isize = &mut z; + /// let y = (&mut Env { x: &mut x }, fn_ptr); // changed from &x to &mut x + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// ``` /// Now the assignment to `**env.x` is legal, but creating a /// mutable pointer to `x` is not because `x` is not mutable. We /// could fix this by declaring `x` as `let mut x`. This is ok in @@ -1016,7 +1019,7 @@ pub struct LocalDecl<'tcx> { /// ``` /// fn foo(x: &str) { /// match { - /// match x.parse().unwrap() { + /// match x.parse::<u32>().unwrap() { /// y => y + 2 /// } /// } { @@ -1692,9 +1695,9 @@ pub enum StatementKind<'tcx> { /// Encodes a user's type ascription. These need to be preserved /// intact so that NLL can respect them. For example: - /// - /// let a: T = y; - /// + /// ```ignore (illustrative) + /// let a: T = y; + /// ``` /// The effect of this annotation is to relate the type `T_y` of the place `y` /// to the user-given type `T`. The effect depends on the specified variance: /// @@ -1987,7 +1990,7 @@ pub enum ProjectionElem<V, T> { /// These indices are generated by slice patterns. Easiest to explain /// by example: /// - /// ``` + /// ```ignore (illustrative) /// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false }, /// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false }, /// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true }, @@ -2017,9 +2020,7 @@ pub enum ProjectionElem<V, T> { from_end: bool, }, - /// "Downcast" to a variant of an ADT. Currently, we only introduce - /// this for ADTs with more than one variant. It may be better to - /// just introduce it always, or always for enums. + /// "Downcast" to a variant of an enum or a generator. /// /// The included Symbol is the name of the variant, used for printing MIR. Downcast(Option<Symbol>, VariantIdx), @@ -2561,16 +2562,12 @@ pub enum Rvalue<'tcx> { UnaryOp(UnOp, Operand<'tcx>), /// Computes the discriminant of the place, returning it as an integer of type - /// [`discriminant_ty`]. + /// [`discriminant_ty`]. Returns zero for types without discriminant. /// /// The validity requirements for the underlying value are undecided for this rvalue, see /// [#91095]. Note too that the value of the discriminant is not the same thing as the /// variant index; use [`discriminant_for_variant`] to convert. /// - /// For types defined in the source code as enums, this is well behaved. This is also well - /// formed for other types, but yields no particular value - there is no reason it couldn't be - /// defined to yield eg zero though. - /// /// [`discriminant_ty`]: crate::ty::Ty::discriminant_ty /// [#91095]: https://github.com/rust-lang/rust/issues/91095 /// [`discriminant_for_variant`]: crate::ty::Ty::discriminant_for_variant @@ -3181,7 +3178,7 @@ impl<'tcx> ConstantKind<'tcx> { /// /// An example: /// -/// ```rust +/// ```ignore (illustrative) /// struct S<'a>((i32, &'a str), String); /// let S((_, w): (i32, &'static str), _): S = ...; /// // ------ ^^^^^^^^^^^^^^^^^^^ (1) diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index f76217d921d..d389fa8c0eb 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -438,7 +438,7 @@ impl<'tcx> CodegenUnitNameBuilder<'tcx> { /// /// This function will build CGU names of the form: /// - /// ``` + /// ```text /// <crate-name>.<crate-disambiguator>[-in-<local-crate-id>](-<component>)*[.<special-suffix>] /// <local-crate-id> = <local-crate-name>.<local-crate-disambiguator> /// ``` diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index b7f695da544..8111409b8bc 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -448,6 +448,12 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { self.push(&format!("+ user_ty: {:?}", user_ty)); } + let fmt_val = |val: &ConstValue<'tcx>| match val { + ConstValue::Scalar(s) => format!("Scalar({:?})", s), + ConstValue::Slice { .. } => format!("Slice(..)"), + ConstValue::ByRef { .. } => format!("ByRef(..)"), + }; + let val = match literal { ConstantKind::Ty(ct) => match ct.val() { ty::ConstKind::Param(p) => format!("Param({})", p), @@ -457,7 +463,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { uv.substs, uv.promoted, ), - ty::ConstKind::Value(val) => format!("Value({:?})", val), + ty::ConstKind::Value(val) => format!("Value({})", fmt_val(&val)), ty::ConstKind::Error(_) => "Error".to_string(), // These variants shouldn't exist in the MIR. ty::ConstKind::Placeholder(_) @@ -467,7 +473,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { // To keep the diffs small, we render this like we render `ty::Const::Value`. // // This changes once `ty::Const::Value` is represented using valtrees. - ConstantKind::Val(val, _) => format!("Value({:?})", val), + ConstantKind::Val(val, _) => format!("Value({})", fmt_val(&val)), }; self.push(&format!("+ literal: Const {{ ty: {}, val: {} }}", literal.ty(), val)); diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 597ade42236..f1d5201454d 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -12,7 +12,7 @@ use rustc_target::abi::VariantIdx; #[derive(Copy, Clone, Debug, TypeFoldable)] pub struct PlaceTy<'tcx> { pub ty: Ty<'tcx>, - /// Downcast to a particular variant of an enum, if included. + /// Downcast to a particular variant of an enum or a generator, if included. pub variant_index: Option<VariantIdx>, } diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index cc08857463d..e6eb63fd3b2 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -202,7 +202,7 @@ pub enum TerminatorKind<'tcx> { /// This assignment occurs both in the unwind and the regular code paths. The semantics are best /// explained by the elaboration: /// - /// ``` + /// ```ignore (MIR) /// BB0 { /// DropAndReplace(P <- V, goto BB1, unwind BB2) /// } @@ -210,7 +210,7 @@ pub enum TerminatorKind<'tcx> { /// /// becomes /// - /// ``` + /// ```ignore (MIR) /// BB0 { /// Drop(P, goto BB1, unwind BB2) /// } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 45b1ad6df82..350bdfdadb2 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -29,7 +29,7 @@ //! //! For example, the `super_basic_block_data` method begins like this: //! -//! ```rust +//! ```ignore (pseudo-rust) //! fn super_basic_block_data(&mut self, //! block: BasicBlock, //! data: & $($mutability)? BasicBlockData<'tcx>) { @@ -1170,10 +1170,10 @@ pub enum NonMutatingUseContext { AddressOf, /// Used as base for another place, e.g., `x` in `x.y`. Will not mutate the place. /// For example, the projection `x.y` is not marked as a mutation in these cases: - /// - /// z = x.y; - /// f(&x.y); - /// + /// ```ignore (illustrative) + /// z = x.y; + /// f(&x.y); + /// ``` Projection, } @@ -1199,10 +1199,10 @@ pub enum MutatingUseContext { AddressOf, /// Used as base for another place, e.g., `x` in `x.y`. Could potentially mutate the place. /// For example, the projection `x.y` is marked as a mutation in these cases: - /// - /// x.y = ...; - /// f(&mut x.y); - /// + /// ```ignore (illustrative) + /// x.y = ...; + /// f(&mut x.y); + /// ``` Projection, /// Retagging, a "Stacked Borrows" shadow state operation Retag, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index cc80ab8f16e..e439d128dbc 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1628,6 +1628,12 @@ rustc_queries! { desc { "looking at the source for a crate" } separate_provide_extern } + /// Returns the debugger visualizers defined for this crate. + query debugger_visualizers(_: CrateNum) -> Vec<rustc_span::DebuggerVisualizerFile> { + storage(ArenaCacheSelector<'tcx>) + desc { "looking up the debugger visualizers for this crate" } + separate_provide_extern + } query postorder_cnums(_: ()) -> &'tcx [CrateNum] { eval_always desc { "generating a postorder list of CrateNums" } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 7c3d08b26bf..ffde1294ec6 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -47,7 +47,8 @@ pub enum Reveal { /// impl. Concretely, that means that the following example will /// fail to compile: /// - /// ``` + /// ```compile_fail,E0308 + /// #![feature(specialization)] /// trait Assoc { /// type Output; /// } @@ -57,7 +58,7 @@ pub enum Reveal { /// } /// /// fn main() { - /// let <() as Assoc>::Output = true; + /// let x: <() as Assoc>::Output = true; /// } /// ``` UserFacing, @@ -515,7 +516,7 @@ pub type SelectionResult<'tcx, T> = Result<Option<T>, SelectionError<'tcx>>; /// For example, the obligation may be satisfied by a specific impl (case A), /// or it may be relative to some bound that is in scope (case B). /// -/// ``` +/// ```ignore (illustrative) /// impl<T:Clone> Clone<T> for Option<T> { ... } // Impl_1 /// impl<T:Clone> Clone<T> for Box<T> { ... } // Impl_2 /// impl Clone for i32 { ... } // Impl_3 diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index c43ec048c3f..ce13825b016 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -180,6 +180,7 @@ pub struct LeafDef { /// Example: /// /// ``` + /// #![feature(specialization)] /// trait Tr { /// fn assoc(&self); /// } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 2676b7ab521..d9332f6896a 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -59,7 +59,7 @@ pub enum PointerCast { /// sized struct to a dynamically sized one. E.g., `&[i32; 4]` -> `&[i32]` is /// represented by: /// -/// ``` +/// ```ignore (illustrative) /// Deref(None) -> [i32; 4], /// Borrow(AutoBorrow::Ref) -> &[i32; 4], /// Unsize -> &[i32], diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index fc6710f07e3..2e4c16e39eb 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -82,7 +82,7 @@ bitflags! { /// /// is essentially represented with [`Ty`] as the following pseudocode: /// -/// ``` +/// ```ignore (illustrative) /// struct S { x } /// ``` /// diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 3bddf7fb6ff..c88cac30a19 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -293,7 +293,7 @@ pub struct CaptureInfo { /// let mut t = (0,1); /// /// let c = || { - /// println!("{t}"); // L1 + /// println!("{t:?}"); // L1 /// t.1 = 4; // L2 /// }; /// ``` @@ -309,7 +309,7 @@ pub struct CaptureInfo { /// let x = 5; /// /// let c = || { - /// let _ = x + /// let _ = x; /// }; /// ``` /// @@ -373,17 +373,19 @@ pub enum BorrowKind { /// is borrowing or mutating a mutable referent, e.g.: /// /// ``` - /// let x: &mut isize = ...; + /// let mut z = 3; + /// let x: &mut isize = &mut z; /// let y = || *x += 5; /// ``` /// /// If we were to try to translate this closure into a more explicit /// form, we'd encounter an error with the code as written: /// - /// ``` - /// struct Env { x: & &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn + /// ```compile_fail,E0594 + /// struct Env<'a> { x: &'a &'a mut isize } + /// let mut z = 3; + /// let x: &mut isize = &mut z; + /// let y = (&mut Env { x: &x }, fn_ptr); // Closure is pair of env and fn /// fn fn_ptr(env: &mut Env) { **env.x += 5; } /// ``` /// @@ -391,10 +393,11 @@ pub enum BorrowKind { /// in an aliasable location. To solve, you'd have to translate with /// an `&mut` borrow: /// - /// ``` - /// struct Env { x: &mut &mut isize } - /// let x: &mut isize = ...; - /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x + /// ```compile_fail,E0596 + /// struct Env<'a> { x: &'a mut &'a mut isize } + /// let mut z = 3; + /// let x: &mut isize = &mut z; + /// let y = (&mut Env { x: &mut x }, fn_ptr); // changed from &x to &mut x /// fn fn_ptr(env: &mut Env) { **env.x += 5; } /// ``` /// diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f9ef264f68e..4d06c95915b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -455,12 +455,13 @@ pub struct TypeckResults<'tcx> { /// # Example /// /// ```rust + /// # use std::fmt::Debug; /// fn foo(x: &u32) -> impl Debug { *x } /// ``` /// /// The function signature here would be: /// - /// ``` + /// ```ignore (illustrative) /// for<'a> fn(&'a u32) -> Foo /// ``` /// @@ -469,7 +470,7 @@ pub struct TypeckResults<'tcx> { /// /// The *liberated* form of this would be /// - /// ``` + /// ```ignore (illustrative) /// fn(&'a u32) -> u32 /// ``` /// diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 075928c889d..896a8280551 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -41,7 +41,7 @@ //! //! For example, if you have `struct S(Ty, U)` where `S: TypeFoldable` and `U: //! TypeFoldable`, and an instance `S(ty, u)`, it would be visited like so: -//! ``` +//! ```text //! s.visit_with(visitor) calls //! - s.super_visit_with(visitor) calls //! - ty.visit_with(visitor) calls @@ -486,13 +486,13 @@ impl<'tcx> TyCtxt<'tcx> { /// traversed. If we encounter a bound region bound by this /// binder or one outer to it, it appears free. Example: /// - /// ``` - /// for<'a> fn(for<'b> fn(), T) - /// ^ ^ ^ ^ - /// | | | | here, would be shifted in 1 - /// | | | here, would be shifted in 2 - /// | | here, would be `INNERMOST` shifted in by 1 - /// | here, initially, binder would be `INNERMOST` + /// ```ignore (illustrative) + /// for<'a> fn(for<'b> fn(), T) + /// // ^ ^ ^ ^ + /// // | | | | here, would be shifted in 1 + /// // | | | here, would be shifted in 2 + /// // | | here, would be `INNERMOST` shifted in by 1 + /// // | here, initially, binder would be `INNERMOST` /// ``` /// /// You see that, initially, *any* bound value is free, diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 226456588e7..b8088766cca 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -56,7 +56,9 @@ impl<'tcx> TyCtxt<'tcx> { /// Checks whether a type is visibly uninhabited from a particular module. /// /// # Example - /// ```rust + /// ``` + /// #![feature(never_type)] + /// # fn main() {} /// enum Void {} /// mod a { /// pub mod b { @@ -67,6 +69,7 @@ impl<'tcx> TyCtxt<'tcx> { /// } /// /// mod c { + /// use super::Void; /// pub struct AlsoSecretlyUninhabited { /// _priv: Void, /// } @@ -84,7 +87,7 @@ impl<'tcx> TyCtxt<'tcx> { /// contain `Foo`. /// /// # Example - /// ```rust + /// ```ignore (illustrative) /// let foo_result: Result<T, Foo> = ... ; /// let Ok(t) = foo_result; /// ``` diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 00b3639a997..03d3a4a8c5f 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -337,7 +337,7 @@ impl<'tcx> Instance<'tcx> { /// Returns `Ok(None)` if we cannot resolve `Instance` to a specific instance. /// For example, in a context like this, /// - /// ``` + /// ```ignore (illustrative) /// fn foo<T: Debug>(t: T) { ... } /// ``` /// diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index af9216a990a..d847068b5bf 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1030,9 +1030,9 @@ impl<'tcx> Predicate<'tcx> { /// their values. /// /// Example: -/// -/// struct Foo<T, U: Bar<T>> { ... } -/// +/// ```ignore (illustrative) +/// struct Foo<T, U: Bar<T>> { ... } +/// ``` /// Here, the `GenericPredicates` for `Foo` would contain a list of bounds like /// `[[], [U:Bar<T>]]`. Now if there were some particular reference /// like `Foo<isize,usize>`, then the `InstantiatedPredicates` would be `[[], @@ -1090,9 +1090,9 @@ pub struct OpaqueHiddenType<'tcx> { /// The type variable that represents the value of the opaque type /// that we require. In other words, after we compile this function, /// we will be created a constraint like: - /// - /// Foo<'a, T> = ?C - /// + /// ```ignore (pseudo-rust) + /// Foo<'a, T> = ?C + /// ``` /// where `?C` is the value of this type variable. =) It may /// naturally refer to the type and lifetime parameters in scope /// in this function, though ultimately it should only reference @@ -1133,7 +1133,7 @@ rustc_index::newtype_index! { /// /// To make this more concrete, consider this program: /// - /// ``` + /// ```ignore (illustrative) /// struct Foo { } /// fn bar<T>(x: T) { /// let y: for<'a> fn(&'a u8, Foo) = ...; @@ -1172,7 +1172,7 @@ impl UniverseIndex { /// corresponds to entering a `forall` quantifier. So, for /// example, suppose we have this type in universe `U`: /// - /// ``` + /// ```ignore (illustrative) /// for<'a> fn(&'a u32) /// ``` /// @@ -1959,7 +1959,7 @@ pub enum ImplOverlapKind { /// The widely-used version 0.1.0 of the crate `traitobject` had accidentally relied /// that difference, making what reduces to the following set of impls: /// - /// ``` + /// ```compile_fail,(E0119) /// trait Trait {} /// impl Trait for dyn Send + Sync {} /// impl Trait for dyn Sync + Send {} diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 3fe68f723ec..a1d1b6b3a78 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -136,6 +136,10 @@ pub trait Printer<'tcx>: Sized { match key.disambiguated_data.data { // Closures' own generics are only captures, don't print them. DefPathData::ClosureExpr => {} + // This covers both `DefKind::AnonConst` and `DefKind::InlineConst`. + // Anon consts doesn't have their own generics, and inline consts' own + // generics are their inferred types, so don't print them. + DefPathData::AnonConst => {} // If we have any generic arguments to print, we do that // on top of the same path, but without its own generics. diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 991666908f9..eed90337c0a 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2676,7 +2676,7 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N // Iterate all local crate items no matter where they are defined. let hir = tcx.hir(); for id in hir.items() { - if matches!(hir.def_kind(id.def_id), DefKind::Use) { + if matches!(tcx.def_kind(id.def_id), DefKind::Use) { continue; } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 0d55fe3a392..7969e7aa151 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -187,12 +187,14 @@ pub enum TyKind<'tcx> { /// Looking at the following example, the witness for this generator /// may end up as something like `for<'a> [Vec<i32>, &'a Vec<i32>]`: /// - /// ```rust + /// ```ignore UNSOLVED (ask @compiler-errors, should this error? can we just swap the yields?) + /// #![feature(generators)] /// |a| { /// let x = &vec![3]; /// yield a; /// yield x[0]; /// } + /// # ; /// ``` GeneratorWitness(Binder<'tcx, &'tcx List<Ty<'tcx>>>), @@ -276,9 +278,9 @@ impl<'tcx> TyKind<'tcx> { static_assert_size!(TyKind<'_>, 32); /// A closure can be modeled as a struct that looks like: -/// -/// struct Closure<'l0...'li, T0...Tj, CK, CS, U>(...U); -/// +/// ```ignore (illustrative) +/// struct Closure<'l0...'li, T0...Tj, CK, CS, U>(...U); +/// ``` /// where: /// /// - 'l0...'li and T0...Tj are the generic parameters @@ -295,25 +297,25 @@ static_assert_size!(TyKind<'_>, 32); /// and the up-var has the type `Foo`, then that field of U will be `&Foo`). /// /// So, for example, given this function: -/// -/// fn foo<'a, T>(data: &'a mut T) { -/// do(|| data.count += 1) -/// } -/// +/// ```ignore (illustrative) +/// fn foo<'a, T>(data: &'a mut T) { +/// do(|| data.count += 1) +/// } +/// ``` /// the type of the closure would be something like: -/// -/// struct Closure<'a, T, U>(...U); -/// +/// ```ignore (illustrative) +/// struct Closure<'a, T, U>(...U); +/// ``` /// Note that the type of the upvar is not specified in the struct. /// You may wonder how the impl would then be able to use the upvar, /// if it doesn't know it's type? The answer is that the impl is /// (conceptually) not fully generic over Closure but rather tied to /// instances with the expected upvar types: -/// -/// impl<'b, 'a, T> FnMut() for Closure<'a, T, (&'b mut &'a mut T,)> { -/// ... -/// } -/// +/// ```ignore (illustrative) +/// impl<'b, 'a, T> FnMut() for Closure<'a, T, (&'b mut &'a mut T,)> { +/// ... +/// } +/// ``` /// You can see that the *impl* fully specified the type of the upvar /// and thus knows full well that `data` has type `&'b mut &'a mut T`. /// (Here, I am assuming that `data` is mut-borrowed.) @@ -760,9 +762,9 @@ impl<'tcx> UpvarSubsts<'tcx> { } /// An inline const is modeled like -/// -/// const InlineConst<'l0...'li, T0...Tj, R>: R; -/// +/// ```ignore (illustrative) +/// const InlineConst<'l0...'li, T0...Tj, R>: R; +/// ``` /// where: /// /// - 'l0...'li and T0...Tj are the generic parameters @@ -936,9 +938,9 @@ impl<'tcx> List<ty::Binder<'tcx, ExistentialPredicate<'tcx>>> { /// A complete reference to a trait. These take numerous guises in syntax, /// but perhaps the most recognizable form is in a where-clause: -/// -/// T: Foo<U> -/// +/// ```ignore (illustrative) +/// T: Foo<U> +/// ``` /// This would be represented by a trait-reference where the `DefId` is the /// `DefId` for the trait `Foo` and the substs define `T` as parameter 0, /// and `U` as parameter 1. @@ -1012,9 +1014,9 @@ impl<'tcx> PolyTraitRef<'tcx> { /// An existential reference to a trait, where `Self` is erased. /// For example, the trait object `Trait<'a, 'b, X, Y>` is: -/// -/// exists T. T: Trait<'a, 'b, X, Y> -/// +/// ```ignore (illustrative) +/// exists T. T: Trait<'a, 'b, X, Y> +/// ``` /// The substitutions don't include the erased `Self`, only trait /// type and lifetime parameters (`[X, Y]` and `['a, 'b]` above). #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] @@ -1434,7 +1436,7 @@ impl<'tcx> fmt::Debug for Region<'tcx> { /// /// In general, the region lattice looks like /// -/// ``` +/// ```text /// static ----------+-----...------+ (greatest) /// | | | /// early-bound and | | @@ -1780,14 +1782,14 @@ impl<'tcx> Region<'tcx> { /// Given an early-bound or free region, returns the `DefId` where it was bound. /// For example, consider the regions in this snippet of code: /// - /// ``` + /// ```ignore (illustrative) /// impl<'a> Foo { - /// ^^ -- early bound, declared on an impl + /// // ^^ -- early bound, declared on an impl /// /// fn bar<'b, 'c>(x: &self, y: &'b u32, z: &'c u64) where 'static: 'c - /// ^^ ^^ ^ anonymous, late-bound - /// | early-bound, appears in where-clauses - /// late-bound, appears only in fn args + /// // ^^ ^^ ^ anonymous, late-bound + /// // | early-bound, appears in where-clauses + /// // late-bound, appears only in fn args /// {..} /// } /// ``` diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 46b938ea93b..5063420e975 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -687,17 +687,17 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { /// /// ``` /// type Func<A> = fn(A); - /// type MetaFunc = for<'a> fn(Func<&'a i32>) + /// type MetaFunc = for<'a> fn(Func<&'a i32>); /// ``` /// /// The type `MetaFunc`, when fully expanded, will be - /// - /// for<'a> fn(fn(&'a i32)) - /// ^~ ^~ ^~~ - /// | | | - /// | | DebruijnIndex of 2 - /// Binders - /// + /// ```ignore (illustrative) + /// for<'a> fn(fn(&'a i32)) + /// // ^~ ^~ ^~~ + /// // | | | + /// // | | DebruijnIndex of 2 + /// // Binders + /// ``` /// Here the `'a` lifetime is bound in the outer function, but appears as an argument of the /// inner one. Therefore, that appearance will have a DebruijnIndex of 2, because we must skip /// over the inner binder (remember that we count De Bruijn indices from 1). However, in the @@ -709,17 +709,17 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { /// /// ``` /// type FuncTuple<A> = (A,fn(A)); - /// type MetaFuncTuple = for<'a> fn(FuncTuple<&'a i32>) + /// type MetaFuncTuple = for<'a> fn(FuncTuple<&'a i32>); /// ``` /// /// Here the final type will be: - /// - /// for<'a> fn((&'a i32, fn(&'a i32))) - /// ^~~ ^~~ - /// | | - /// DebruijnIndex of 1 | - /// DebruijnIndex of 2 - /// + /// ```ignore (illustrative) + /// for<'a> fn((&'a i32, fn(&'a i32))) + /// // ^~~ ^~~ + /// // | | + /// // DebruijnIndex of 1 | + /// // DebruijnIndex of 2 + /// ``` /// As indicated in the diagram, here the same type `&'a i32` is substituted once, but in the /// first case we do not increase the De Bruijn index and in the second case we do. The reason /// is that only in the second case have we passed through a fn binder. @@ -767,7 +767,7 @@ pub struct UserSubsts<'tcx> { /// sometimes needed to constrain the type parameters on the impl. For /// example, in this code: /// -/// ``` +/// ```ignore (illustrative) /// struct Foo<T> { } /// impl<A> Foo<A> { fn method() { } } /// ``` diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index c190eec7e5a..1c8af13ce9c 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -973,7 +973,7 @@ impl<'tcx> ExplicitSelf<'tcx> { /// /// Examples: /// - /// ``` + /// ```ignore (illustrative) /// impl<'a> Foo for &'a T { /// // Legal declarations: /// fn method1(self: &&'a T); // ExplicitSelf::ByReference diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 9f57e1a977a..09946f02448 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -34,7 +34,7 @@ impl<'tcx> TypeWalker<'tcx> { /// /// Example: Imagine you are walking `Foo<Bar<i32>, usize>`. /// - /// ``` + /// ```ignore (illustrative) /// let mut iter: TypeWalker = ...; /// iter.next(); // yields Foo /// iter.next(); // yields Bar<i32> diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs index b627b0763a2..7eca49454ba 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -1,7 +1,7 @@ //! See docs in build/expr/mod.rs use crate::build::expr::category::Category; -use crate::build::{BlockAnd, BlockAndExtension, Builder}; +use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary}; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::*; @@ -20,7 +20,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr: &Expr<'tcx>, ) -> BlockAnd<Operand<'tcx>> { let local_scope = self.local_scope(); - self.as_operand(block, Some(local_scope), expr, None) + self.as_operand(block, Some(local_scope), expr, None, NeedsTemporary::Maybe) } /// Returns an operand suitable for use until the end of the current scope expression and @@ -42,15 +42,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// We tweak the handling of parameters of unsized type slightly to avoid the need to create a /// local variable of unsized type. For example, consider this program: /// - /// ```rust - /// fn foo(p: dyn Debug) { ... } + /// ``` + /// #![feature(unsized_locals, unsized_fn_params)] + /// # use core::fmt::Debug; + /// fn foo(p: dyn Debug) { dbg!(p); } /// - /// fn bar(box_p: Box<dyn Debug>) { foo(*p); } + /// fn bar(box_p: Box<dyn Debug>) { foo(*box_p); } /// ``` /// /// Ordinarily, for sized types, we would compile the call `foo(*p)` like so: /// - /// ```rust + /// ```ignore (illustrative) /// let tmp0 = *box_p; // tmp0 would be the operand returned by this function call /// foo(tmp0) /// ``` @@ -60,7 +62,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// that we create *stores the entire box*, and the parameter to the call itself will be /// `*tmp0`: /// - /// ```rust + /// ```ignore (illustrative) /// let tmp0 = box_p; call foo(*tmp0) /// ``` /// @@ -94,32 +96,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// Like `as_local_call_operand`, except that the argument will /// not be valid once `scope` ends. + #[instrument(level = "debug", skip(self, scope))] crate fn as_operand( &mut self, mut block: BasicBlock, scope: Option<region::Scope>, expr: &Expr<'tcx>, local_info: Option<Box<LocalInfo<'tcx>>>, + needs_temporary: NeedsTemporary, ) -> BlockAnd<Operand<'tcx>> { - debug!("as_operand(block={:?}, expr={:?} local_info={:?})", block, expr, local_info); let this = self; if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind { let source_info = this.source_info(expr.span); let region_scope = (region_scope, source_info); return this.in_scope(region_scope, lint_level, |this| { - this.as_operand(block, scope, &this.thir[value], local_info) + this.as_operand(block, scope, &this.thir[value], local_info, needs_temporary) }); } let category = Category::of(&expr.kind).unwrap(); - debug!("as_operand: category={:?} for={:?}", category, expr.kind); + debug!(?category, ?expr.kind); match category { - Category::Constant => { + Category::Constant if let NeedsTemporary::No = needs_temporary || !expr.ty.needs_drop(this.tcx, this.param_env) => { let constant = this.as_constant(expr); block.and(Operand::Constant(Box::new(constant))) } - Category::Place | Category::Rvalue(..) => { + Category::Constant | Category::Place | Category::Rvalue(..) => { let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut)); if this.local_decls[operand].local_info.is_none() { this.local_decls[operand].local_info = local_info; @@ -176,6 +179,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - this.as_operand(block, scope, expr, None) + this.as_operand(block, scope, expr, None, NeedsTemporary::Maybe) } } diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 3b3120cf04b..fa1bb0622bd 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -37,7 +37,7 @@ crate enum PlaceBase { /// /// Consider the following example /// ```rust - /// let t = (10, (10, (10, 10))); + /// let t = (((10, 10), 10), 10); /// /// let c = || { /// println!("{}", t.0.0.0); @@ -45,7 +45,7 @@ crate enum PlaceBase { /// ``` /// Here the THIR expression for `t.0.0.0` will be something like /// - /// ``` + /// ```ignore (illustrative) /// * Field(0) /// * Field(0) /// * Field(0) diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index be777418433..d807500f1fb 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -4,7 +4,7 @@ use rustc_index::vec::Idx; use crate::build::expr::as_place::PlaceBase; use crate::build::expr::category::{Category, RvalueFunc}; -use crate::build::{BlockAnd, BlockAndExtension, Builder}; +use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary}; use rustc_hir::lang_items::LangItem; use rustc_middle::middle::region; use rustc_middle::mir::AssertKind; @@ -52,17 +52,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) } ExprKind::Repeat { value, count } => { - let value_operand = - unpack!(block = this.as_operand(block, scope, &this.thir[value], None)); + let value_operand = unpack!( + block = + this.as_operand(block, scope, &this.thir[value], None, NeedsTemporary::No) + ); block.and(Rvalue::Repeat(value_operand, count)) } ExprKind::Binary { op, lhs, rhs } => { - let lhs = unpack!(block = this.as_operand(block, scope, &this.thir[lhs], None)); - let rhs = unpack!(block = this.as_operand(block, scope, &this.thir[rhs], None)); + let lhs = unpack!( + block = + this.as_operand(block, scope, &this.thir[lhs], None, NeedsTemporary::Maybe) + ); + let rhs = unpack!( + block = + this.as_operand(block, scope, &this.thir[rhs], None, NeedsTemporary::No) + ); this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs) } ExprKind::Unary { op, arg } => { - let arg = unpack!(block = this.as_operand(block, scope, &this.thir[arg], None)); + let arg = unpack!( + block = + this.as_operand(block, scope, &this.thir[arg], None, NeedsTemporary::No) + ); // Check for -MIN on signed integers if this.check_overflow && op == UnOp::Neg && expr.ty.is_signed() { let bool_ty = this.tcx.types.bool; @@ -167,13 +178,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(Rvalue::Use(Operand::Move(Place::from(result)))) } ExprKind::Cast { source } => { - let source = - unpack!(block = this.as_operand(block, scope, &this.thir[source], None)); + let source = unpack!( + block = + this.as_operand(block, scope, &this.thir[source], None, NeedsTemporary::No) + ); block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty)) } ExprKind::Pointer { cast, source } => { - let source = - unpack!(block = this.as_operand(block, scope, &this.thir[source], None)); + let source = unpack!( + block = + this.as_operand(block, scope, &this.thir[source], None, NeedsTemporary::No) + ); block.and(Rvalue::Cast(CastKind::Pointer(cast), source, expr.ty)) } ExprKind::Array { ref fields } => { @@ -208,7 +223,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fields: Vec<_> = fields .into_iter() .copied() - .map(|f| unpack!(block = this.as_operand(block, scope, &this.thir[f], None))) + .map(|f| { + unpack!( + block = this.as_operand( + block, + scope, + &this.thir[f], + None, + NeedsTemporary::Maybe + ) + ) + }) .collect(); block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(el_ty)), fields)) @@ -219,7 +244,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fields: Vec<_> = fields .into_iter() .copied() - .map(|f| unpack!(block = this.as_operand(block, scope, &this.thir[f], None))) + .map(|f| { + unpack!( + block = this.as_operand( + block, + scope, + &this.thir[f], + None, + NeedsTemporary::Maybe + ) + ) + }) .collect(); block.and(Rvalue::Aggregate(Box::new(AggregateKind::Tuple), fields)) @@ -296,7 +331,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) ), _ => { - unpack!(block = this.as_operand(block, scope, upvar, None)) + unpack!( + block = this.as_operand( + block, + scope, + upvar, + None, + NeedsTemporary::Maybe + ) + ) } } } @@ -325,13 +368,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { literal: ConstantKind::zero_sized(this.tcx.types.unit), })))) } - ExprKind::Yield { .. } - | ExprKind::Literal { .. } + + ExprKind::Literal { .. } | ExprKind::NamedConst { .. } | ExprKind::NonHirLiteral { .. } | ExprKind::ConstParam { .. } | ExprKind::ConstBlock { .. } - | ExprKind::StaticRef { .. } + | ExprKind::StaticRef { .. } => { + let constant = this.as_constant(expr); + block.and(Rvalue::Use(Operand::Constant(Box::new(constant)))) + } + + ExprKind::Yield { .. } | ExprKind::Block { .. } | ExprKind::Match { .. } | ExprKind::If { .. } @@ -359,9 +407,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // so make an operand and then return that debug_assert!(!matches!( Category::of(&expr.kind), - Some(Category::Rvalue(RvalueFunc::AsRvalue)) + Some(Category::Rvalue(RvalueFunc::AsRvalue) | Category::Constant) )); - let operand = unpack!(block = this.as_operand(block, scope, expr, None)); + let operand = + unpack!(block = this.as_operand(block, scope, expr, None, NeedsTemporary::No)); block.and(Rvalue::Use(operand)) } } diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index cee657e9da2..4399fdf8520 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -1,7 +1,7 @@ //! See docs in build/expr/mod.rs use crate::build::expr::category::{Category, RvalueFunc}; -use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; +use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary}; use rustc_ast::InlineAsmOptions; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -329,7 +329,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block, Some(scope), &this.thir[f.expr], - Some(local_info) + Some(local_info), + NeedsTemporary::Maybe, ) ), ) @@ -516,8 +517,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Yield { value } => { let scope = this.local_scope(); - let value = - unpack!(block = this.as_operand(block, Some(scope), &this.thir[value], None)); + let value = unpack!( + block = this.as_operand( + block, + Some(scope), + &this.thir[value], + None, + NeedsTemporary::No + ) + ); let resume = this.cfg.start_new_block(); this.cfg.terminate( block, diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index d45ae19752e..8594d0806bb 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1032,11 +1032,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// exhaustive match, consider: /// /// ``` + /// # fn foo(x: (bool, bool)) { /// match x { /// (true, true) => (), /// (_, false) => (), /// (false, true) => (), /// } + /// # } /// ``` /// /// For this match, we check if `x.0` matches `true` (for the first @@ -1157,7 +1159,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// For example, if we have something like this: /// - /// ```rust + /// ```ignore (illustrative) /// ... /// Some(x) if cond1 => ... /// Some(x) => ... @@ -1481,11 +1483,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// ``` /// # let (x, y, z) = (true, true, true); /// match (x, y, z) { - /// (true, _, true) => true, // (0) - /// (_, true, _) => true, // (1) - /// (false, false, _) => false, // (2) - /// (true, _, false) => false, // (3) + /// (true , _ , true ) => true, // (0) + /// (_ , true , _ ) => true, // (1) + /// (false, false, _ ) => false, // (2) + /// (true , _ , false) => false, // (3) /// } + /// # ; /// ``` /// /// In that case, after we test on `x`, there are 2 overlapping candidate @@ -1502,14 +1505,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// with precisely the reachable arms being reachable - but that problem /// is trivially NP-complete: /// - /// ```rust - /// match (var0, var1, var2, var3, ...) { - /// (true, _, _, false, true, ...) => false, - /// (_, true, true, false, _, ...) => false, - /// (false, _, false, false, _, ...) => false, - /// ... - /// _ => true - /// } + /// ```ignore (illustrative) + /// match (var0, var1, var2, var3, ...) { + /// (true , _ , _ , false, true, ...) => false, + /// (_ , true, true , false, _ , ...) => false, + /// (false, _ , false, false, _ , ...) => false, + /// ... + /// _ => true + /// } /// ``` /// /// Here the last arm is reachable only if there is an assignment to @@ -1520,7 +1523,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// our simplistic treatment of constants and guards would make it occur /// in very common situations - for example [#29740]: /// - /// ```rust + /// ```ignore (illustrative) /// match x { /// "foo" if foo_guard => ..., /// "bar" if bar_guard => ..., diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 3c51f791862..ce57e5fe846 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -549,6 +549,18 @@ rustc_index::newtype_index! { struct ScopeId { .. } } +#[derive(Debug)] +enum NeedsTemporary { + /// Use this variant when whatever you are converting with `as_operand` + /// is the last thing you are converting. This means that if we introduced + /// an intermediate temporary, we'd only read it immediately after, so we can + /// also avoid it. + No, + /// For all cases where you aren't sure or that are too expensive to compute + /// for now. It is always safe to fall back to this. + Maybe, +} + /////////////////////////////////////////////////////////////////////////// /// The `BlockAnd` "monad" packages up the new basic block along with a /// produced value (sometimes just unit, of course). The `unpack!` diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index d96534fe3e0..465bd62406e 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -803,16 +803,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// scope (which can be larger or smaller). /// /// Consider: - /// - /// let x = foo(bar(X, Y)); - /// + /// ```ignore (illustrative) + /// let x = foo(bar(X, Y)); + /// ``` /// We wish to pop the storage for X and Y after `bar()` is /// called, not after the whole `let` is completed. /// /// As another example, if the second argument diverges: - /// - /// foo(Box::new(2), panic!()) - /// + /// ```ignore (illustrative) + /// foo(Box::new(2), panic!()) + /// ``` /// We would allocate the box but then free it on the unwinding /// path; we would also emit a free on the 'success' path from /// panic, but that will turn out to be removed as dead-code. @@ -944,7 +944,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// Example: when compiling the call to `foo` here: /// - /// ```rust + /// ```ignore (illustrative) /// foo(bar(), ...) /// ``` /// @@ -955,7 +955,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// dropped). However, if no unwind occurs, then `_X` will be /// unconditionally consumed by the `call`: /// - /// ``` + /// ```ignore (illustrative) /// bb { /// ... /// _R = CALL(foo, _X, ...) diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 6687e1160ed..4ffee59a962 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -2,10 +2,10 @@ //! //! This crate also contains the match exhaustiveness and usefulness checking. #![allow(rustc::potential_query_instability)] -#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(crate_visibility_modifier)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(let_else)] #![feature(min_specialization)] diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index f382f79af29..147c136e651 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -158,6 +158,7 @@ impl<'tcx> Cx<'tcx> { } fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> { + let tcx = self.tcx; let expr_ty = self.typeck_results().expr_ty(expr); let expr_span = expr.span; let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); @@ -196,7 +197,7 @@ impl<'tcx> Cx<'tcx> { let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e)); let tupled_args = Expr { - ty: self.tcx.mk_tup(arg_tys), + ty: tcx.mk_tup(arg_tys), temp_lifetime, span: expr.span, kind: ExprKind::Tuple { fields: self.mirror_exprs(args) }, @@ -488,24 +489,24 @@ impl<'tcx> Cx<'tcx> { out_expr: out_expr.as_ref().map(|expr| self.mirror_expr(expr)), }, hir::InlineAsmOperand::Const { ref anon_const } => { - let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); + let anon_const_def_id = tcx.hir().local_def_id(anon_const.hir_id); let value = mir::ConstantKind::from_anon_const( - self.tcx, + tcx, anon_const_def_id, self.param_env, ); - let span = self.tcx.hir().span(anon_const.hir_id); + let span = tcx.hir().span(anon_const.hir_id); InlineAsmOperand::Const { value, span } } hir::InlineAsmOperand::SymFn { ref anon_const } => { - let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); + let anon_const_def_id = tcx.hir().local_def_id(anon_const.hir_id); let value = mir::ConstantKind::from_anon_const( - self.tcx, + tcx, anon_const_def_id, self.param_env, ); - let span = self.tcx.hir().span(anon_const.hir_id); + let span = tcx.hir().span(anon_const.hir_id); InlineAsmOperand::SymFn { value, span } } @@ -519,21 +520,16 @@ impl<'tcx> Cx<'tcx> { }, hir::ExprKind::ConstBlock(ref anon_const) => { - let tcx = self.tcx; - let local_def_id = tcx.hir().local_def_id(anon_const.hir_id); - let anon_const_def_id = local_def_id.to_def_id(); - - // Need to include the parent substs - let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id); - let ty = tcx.typeck(local_def_id).node_type(hir_id); - let typeck_root_def_id = tcx.typeck_root_def_id(anon_const_def_id); + let ty = self.typeck_results().node_type(anon_const.hir_id); + let did = tcx.hir().local_def_id(anon_const.hir_id).to_def_id(); + let typeck_root_def_id = tcx.typeck_root_def_id(did); let parent_substs = tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id)); let substs = InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty }) .substs; - ExprKind::ConstBlock { did: anon_const_def_id, substs } + ExprKind::ConstBlock { did, substs } } // Now comes the rote stuff: hir::ExprKind::Repeat(ref v, _) => { @@ -591,7 +587,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Field(ref source, ..) => ExprKind::Field { lhs: self.mirror_expr(source), - name: Field::new(self.tcx.field_index(expr.hir_id, self.typeck_results)), + name: Field::new(tcx.field_index(expr.hir_id, self.typeck_results)), }, hir::ExprKind::Cast(ref source, ref cast_ty) => { // Check for a user-given type annotation on this `cast` @@ -640,7 +636,7 @@ impl<'tcx> Cx<'tcx> { let (d, o) = adt_def.discriminant_def_for_variant(idx); use rustc_middle::ty::util::IntTypeExt; let ty = adt_def.repr().discr_type(); - let ty = ty.to_ty(self.tcx()); + let ty = ty.to_ty(tcx); Some((d, o, ty)) } _ => None, @@ -652,8 +648,7 @@ impl<'tcx> Cx<'tcx> { let source = if let Some((did, offset, var_ty)) = var { let param_env_ty = self.param_env.and(var_ty); - let size = self - .tcx + let size = tcx .layout_of(param_env_ty) .unwrap_or_else(|e| { panic!("could not compute layout for {:?}: {:?}", param_env_ty, e) @@ -671,7 +666,7 @@ impl<'tcx> Cx<'tcx> { Some(did) => { // in case we are offsetting from a computed discriminant // and not the beginning of discriminants (which is always `0`) - let substs = InternalSubsts::identity_for_item(self.tcx(), did); + let substs = InternalSubsts::identity_for_item(tcx, did); let kind = ExprKind::NamedConst { def_id: did, substs, user_ty: None }; let lhs = self.thir.exprs.push(Expr { diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 72f0d07c260..60eead69a1b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -13,7 +13,7 @@ //! Instead of listing all those constructors (which is intractable), we group those value //! constructors together as much as possible. Example: //! -//! ``` +//! ```compile_fail,E0004 //! match (0, false) { //! (0 ..=100, true) => {} // `p_1` //! (50..=150, false) => {} // `p_2` @@ -344,13 +344,13 @@ enum IntBorder { /// straddles the boundary of one of the inputs. /// /// The following input: -/// ``` +/// ```text /// |-------------------------| // `self` /// |------| |----------| |----| /// |-------| |-------| /// ``` /// would be iterated over as follows: -/// ``` +/// ```text /// ||---|--||-|---|---|---|--| /// ``` #[derive(Debug, Clone)] @@ -492,14 +492,17 @@ impl Slice { /// /// Let's look at an example, where we are trying to split the last pattern: /// ``` +/// # fn foo(x: &[bool]) { /// match x { /// [true, true, ..] => {} /// [.., false, false] => {} /// [..] => {} /// } +/// # } /// ``` /// Here are the results of specialization for the first few lengths: /// ``` +/// # fn foo(x: &[bool]) { match x { /// // length 0 /// [] => {} /// // length 1 @@ -520,6 +523,8 @@ impl Slice { /// [true, true, _, _, _ ] => {} /// [_, _, _, false, false] => {} /// [_, _, _, _, _ ] => {} +/// # _ => {} +/// # }} /// ``` /// /// If we went above length 5, we would simply be inserting more columns full of wildcards in the @@ -1128,7 +1133,8 @@ impl<'tcx> SplitWildcard<'tcx> { /// In the following example `Fields::wildcards` returns `[_, _, _, _]`. Then in /// `extract_pattern_arguments` we fill some of the entries, and the result is /// `[Some(0), _, _, _]`. -/// ```rust +/// ```compile_fail,E0004 +/// # fn foo() -> [Option<u8>; 4] { [None; 4] } /// let x: [Option<u8>; 4] = foo(); /// match x { /// [Some(0), ..] => {} diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 687b2e23c9f..4e96cfd9bbd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -35,23 +35,27 @@ //! This is enough to compute reachability: a pattern in a `match` expression is reachable iff it //! is useful w.r.t. the patterns above it: //! ```rust +//! # fn foo(x: Option<i32>) { //! match x { -//! Some(_) => ..., -//! None => ..., // reachable: `None` is matched by this but not the branch above -//! Some(0) => ..., // unreachable: all the values this matches are already matched by -//! // `Some(_)` above +//! Some(_) => {}, +//! None => {}, // reachable: `None` is matched by this but not the branch above +//! Some(0) => {}, // unreachable: all the values this matches are already matched by +//! // `Some(_)` above //! } +//! # } //! ``` //! //! This is also enough to compute exhaustiveness: a match is exhaustive iff the wildcard `_` //! pattern is _not_ useful w.r.t. the patterns in the match. The values returned by `usefulness` //! are used to tell the user which values are missing. -//! ```rust +//! ```compile_fail,E0004 +//! # fn foo(x: Option<i32>) { //! match x { -//! Some(0) => ..., -//! None => ..., +//! Some(0) => {}, +//! None => {}, //! // not exhaustive: `_` is useful because it matches `Some(1)` //! } +//! # } //! ``` //! //! The entrypoint of this file is the [`compute_match_usefulness`] function, which computes @@ -120,12 +124,15 @@ //! say from knowing only the first constructor of our candidate value. //! //! Let's take the following example: -//! ``` +//! ```compile_fail,E0004 +//! # enum Enum { Variant1(()), Variant2(Option<bool>, u32)} +//! # fn foo(x: Enum) { //! match x { //! Enum::Variant1(_) => {} // `p1` //! Enum::Variant2(None, 0) => {} // `p2` //! Enum::Variant2(Some(_), 0) => {} // `q` //! } +//! # } //! ``` //! //! We can easily see that if our candidate value `v` starts with `Variant1` it will not match `q`. @@ -133,11 +140,13 @@ //! and `v1`. In fact, such a `v` will be a witness of usefulness of `q` exactly when the tuple //! `(v0, v1)` is a witness of usefulness of `q'` in the following reduced match: //! -//! ``` +//! ```compile_fail,E0004 +//! # fn foo(x: (Option<bool>, u32)) { //! match x { //! (None, 0) => {} // `p2'` //! (Some(_), 0) => {} // `q'` //! } +//! # } //! ``` //! //! This motivates a new step in computing usefulness, that we call _specialization_. @@ -150,7 +159,7 @@ //! like a stack. We note a pattern-stack simply with `[p_1 ... p_n]`. //! Here's a sequence of specializations of a list of pattern-stacks, to illustrate what's //! happening: -//! ``` +//! ```ignore (illustrative) //! [Enum::Variant1(_)] //! [Enum::Variant2(None, 0)] //! [Enum::Variant2(Some(_), 0)] @@ -234,7 +243,7 @@ //! - We return the concatenation of all the witnesses found, if any. //! //! Example: -//! ``` +//! ```ignore (illustrative) //! [Some(true)] // p_1 //! [None] // p_2 //! [Some(_)] // q @@ -659,13 +668,15 @@ enum ArmType { /// /// For example, if we are constructing a witness for the match against /// -/// ``` +/// ```compile_fail,E0004 +/// # #![feature(type_ascription)] /// struct Pair(Option<(u32, u32)>, bool); -/// +/// # fn foo(p: Pair) { /// match (p: Pair) { /// Pair(None, _) => {} /// Pair(_, false) => {} /// } +/// # } /// ``` /// /// We'll perform the following steps: diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index d90ead3228c..c9722a6df77 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -37,21 +37,21 @@ pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageLive}; /// /// ```rust /// struct S; -/// fn foo(pred: bool) { // maybe-init: -/// // {} -/// let a = S; let b = S; let c; let d; // {a, b} +/// fn foo(pred: bool) { // maybe-init: +/// // {} +/// let a = S; let mut b = S; let c; let d; // {a, b} /// /// if pred { -/// drop(a); // { b} -/// b = S; // { b} +/// drop(a); // { b} +/// b = S; // { b} /// /// } else { -/// drop(b); // {a} -/// d = S; // {a, d} +/// drop(b); // {a} +/// d = S; // {a, d} /// -/// } // {a, b, d} +/// } // {a, b, d} /// -/// c = S; // {a, b, c, d} +/// c = S; // {a, b, c, d} /// } /// ``` /// @@ -90,21 +90,21 @@ impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> { /// /// ```rust /// struct S; -/// fn foo(pred: bool) { // maybe-uninit: -/// // {a, b, c, d} -/// let a = S; let b = S; let c; let d; // { c, d} +/// fn foo(pred: bool) { // maybe-uninit: +/// // {a, b, c, d} +/// let a = S; let mut b = S; let c; let d; // { c, d} /// /// if pred { -/// drop(a); // {a, c, d} -/// b = S; // {a, c, d} +/// drop(a); // {a, c, d} +/// b = S; // {a, c, d} /// /// } else { -/// drop(b); // { b, c, d} -/// d = S; // { b, c } +/// drop(b); // { b, c, d} +/// d = S; // { b, c } /// -/// } // {a, b, c, d} +/// } // {a, b, c, d} /// -/// c = S; // {a, b, d} +/// c = S; // {a, b, d} /// } /// ``` /// @@ -155,21 +155,21 @@ impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> { /// /// ```rust /// struct S; -/// fn foo(pred: bool) { // definite-init: -/// // { } -/// let a = S; let b = S; let c; let d; // {a, b } +/// fn foo(pred: bool) { // definite-init: +/// // { } +/// let a = S; let mut b = S; let c; let d; // {a, b } /// /// if pred { -/// drop(a); // { b, } -/// b = S; // { b, } +/// drop(a); // { b, } +/// b = S; // { b, } /// /// } else { -/// drop(b); // {a, } -/// d = S; // {a, d} +/// drop(b); // {a, } +/// d = S; // {a, d} /// -/// } // { } +/// } // { } /// -/// c = S; // { c } +/// c = S; // { c } /// } /// ``` /// @@ -210,21 +210,21 @@ impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { /// /// ```rust /// struct S; -/// fn foo(pred: bool) { // ever-init: -/// // { } -/// let a = S; let b = S; let c; let d; // {a, b } +/// fn foo(pred: bool) { // ever-init: +/// // { } +/// let a = S; let mut b = S; let c; let d; // {a, b } /// /// if pred { -/// drop(a); // {a, b, } -/// b = S; // {a, b, } +/// drop(a); // {a, b, } +/// b = S; // {a, b, } /// /// } else { -/// drop(b); // {a, b, } -/// d = S; // {a, b, d } +/// drop(b); // {a, b, } +/// d = S; // {a, b, d } /// -/// } // {a, b, d } +/// } // {a, b, d } /// -/// c = S; // {a, b, c, d } +/// c = S; // {a, b, c, d } /// } /// ``` pub struct EverInitializedPlaces<'a, 'tcx> { diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index c221b358670..d0837bcf754 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -1,5 +1,4 @@ #![feature(associated_type_defaults)] -#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(exact_size_is_empty)] diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 691f4fb0e54..f7535d338da 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -415,7 +415,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // Try to read the local as an immediate so that if it is representable as a scalar, we can // handle it as such, but otherwise, just return the value as is. - Some(match self.ecx.try_read_immediate(&op) { + Some(match self.ecx.read_immediate_raw(&op, /*force*/ false) { Ok(Ok(imm)) => imm.into(), _ => op, }) @@ -709,8 +709,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return; } - // FIXME> figure out what to do when try_read_immediate fails - let imm = self.use_ecx(|this| this.ecx.try_read_immediate(value)); + // FIXME> figure out what to do when read_immediate_raw fails + let imm = self.use_ecx(|this| this.ecx.read_immediate_raw(value, /*force*/ false)); if let Some(Ok(imm)) = imm { match *imm { diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index 4945c10c9aa..aa898cfd3ba 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -412,7 +412,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // Try to read the local as an immediate so that if it is representable as a scalar, we can // handle it as such, but otherwise, just return the value as is. - Some(match self.ecx.try_read_immediate(&op) { + Some(match self.ecx.read_immediate_raw(&op, /*force*/ false) { Ok(Ok(imm)) => imm.into(), _ => op, }) diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 144ea0ec619..d5d4bfa255b 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -18,13 +18,13 @@ //! First upvars are stored //! It is followed by the generator state field. //! Then finally the MIR locals which are live across a suspension point are stored. -//! +//! ```ignore (illustrative) //! struct Generator { //! upvars..., //! state: u32, //! mir_locals..., //! } -//! +//! ``` //! This pass computes the meaning of the state field and the MIR locals which are live //! across a suspension point. There are however three hardcoded generator states: //! 0 - Generator have not been resumed yet diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 3c14a324c36..7cd7d26328a 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -14,7 +14,7 @@ pub struct MatchBranchSimplification; /// /// For example: /// -/// ```rust +/// ```ignore (MIR) /// bb0: { /// switchInt(move _3) -> [42_isize: bb1, otherwise: bb2]; /// } @@ -32,7 +32,7 @@ pub struct MatchBranchSimplification; /// /// into: /// -/// ```rust +/// ```ignore (MIR) /// bb0: { /// _2 = Eq(move _3, const 42_isize); /// goto -> bb3; diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs index da683a33651..bbfaace7041 100644 --- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs +++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs @@ -12,7 +12,7 @@ use rustc_middle::{ /// Pass to convert `if` conditions on integrals into switches on the integral. /// For an example, it turns something like /// -/// ``` +/// ```ignore (MIR) /// _3 = Eq(move _4, const 43i32); /// StorageDead(_4); /// switchInt(_3) -> [false: bb2, otherwise: bb3]; @@ -20,7 +20,7 @@ use rustc_middle::{ /// /// into: /// -/// ``` +/// ```ignore (MIR) /// switchInt(_4) -> [43i32: bb3, otherwise: bb2]; /// ``` pub struct SimplifyComparisonIntegral; diff --git a/compiler/rustc_mir_transform/src/simplify_try.rs b/compiler/rustc_mir_transform/src/simplify_try.rs index ce4b45062e8..b3d45c7a221 100644 --- a/compiler/rustc_mir_transform/src/simplify_try.rs +++ b/compiler/rustc_mir_transform/src/simplify_try.rs @@ -1,10 +1,12 @@ //! The general point of the optimizations provided here is to simplify something like: //! //! ```rust +//! # fn foo<T, E>(x: Result<T, E>) -> Result<T, E> { //! match x { //! Ok(x) => Ok(x), //! Err(x) => Err(x) //! } +//! # } //! ``` //! //! into just `x`. @@ -23,7 +25,7 @@ use std::slice::Iter; /// /// This is done by transforming basic blocks where the statements match: /// -/// ```rust +/// ```ignore (MIR) /// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY ); /// _TMP_2 = _LOCAL_TMP; /// ((_LOCAL_0 as Variant).FIELD: TY) = move _TMP_2; @@ -32,7 +34,7 @@ use std::slice::Iter; /// /// into: /// -/// ```rust +/// ```ignore (MIR) /// _LOCAL_0 = move _LOCAL_1 /// ``` pub struct SimplifyArmIdentity; @@ -472,7 +474,7 @@ impl Visitor<'_> for LocalUseCounter { } /// Match on: -/// ```rust +/// ```ignore (MIR) /// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY); /// ``` fn match_get_variant_field<'tcx>( @@ -492,7 +494,7 @@ fn match_get_variant_field<'tcx>( } /// Match on: -/// ```rust +/// ```ignore (MIR) /// ((_LOCAL_FROM as Variant).FIELD: TY) = move _LOCAL_INTO; /// ``` fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> { @@ -507,7 +509,7 @@ fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local } /// Match on: -/// ```rust +/// ```ignore (MIR) /// discriminant(_LOCAL_TO_SET) = VAR_IDX; /// ``` fn match_set_discr(stmt: &Statement<'_>) -> Option<(Local, VariantIdx)> { @@ -690,7 +692,7 @@ impl<'tcx> SimplifyBranchSameOptimizationFinder<'_, 'tcx> { /// /// Statements can be trivially equal if the kinds match. /// But they can also be considered equal in the following case A: - /// ``` + /// ```ignore (MIR) /// discriminant(_0) = 0; // bb1 /// _0 = move _1; // bb2 /// ``` diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 1828aecb375..ee02151152c 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -91,12 +91,13 @@ //! another function. It suffices to just take a reference in order to introduce //! an edge. Consider the following example: //! -//! ```rust +//! ``` +//! # use core::fmt::Display; //! fn print_val<T: Display>(x: T) { //! println!("{}", x); //! } //! -//! fn call_fn(f: &Fn(i32), x: i32) { +//! fn call_fn(f: &dyn Fn(i32), x: i32) { //! f(x); //! } //! @@ -445,12 +446,9 @@ fn collect_items_rec<'tcx>( // depend on any other items. } hir::InlineAsmOperand::SymFn { anon_const } => { - let def_id = tcx.hir().body_owner_def_id(anon_const.body).to_def_id(); - if let Ok(val) = tcx.const_eval_poly(def_id) { - rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_const_value(tcx, val, &mut neighbors); - }); - } + let fn_ty = + tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id); + visit_fn_use(tcx, fn_ty, false, *op_sp, &mut neighbors); } hir::InlineAsmOperand::SymStatic { path: _, def_id } => { let instance = Instance::mono(tcx, *def_id); @@ -1167,7 +1165,7 @@ struct RootCollector<'a, 'tcx> { impl<'v> RootCollector<'_, 'v> { fn process_item(&mut self, id: hir::ItemId) { - match self.tcx.hir().def_kind(id.def_id) { + match self.tcx.def_kind(id.def_id) { DefKind::Enum | DefKind::Struct | DefKind::Union => { let item = self.tcx.hir().item(id); match item.kind { @@ -1228,7 +1226,7 @@ impl<'v> RootCollector<'_, 'v> { } fn process_impl_item(&mut self, id: hir::ImplItemId) { - if matches!(self.tcx.hir().def_kind(id.def_id), DefKind::AssocFn) { + if matches!(self.tcx.def_kind(id.def_id), DefKind::AssocFn) { self.push_if_root(id.def_id); } } diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index c9accbcd86e..f0333d6c6da 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -1,5 +1,4 @@ #![feature(array_windows)] -#![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(control_flow_enum)] #![feature(let_else)] diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 358b01df3b9..2f90a7c54ab 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -371,9 +371,10 @@ impl<'a> Parser<'a> { } /// Matches the following grammar (per RFC 1559). - /// - /// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ; - /// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ; + /// ```ebnf + /// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ; + /// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ; + /// ``` pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> { let nt_meta = match self.token.kind { token::Interpolated(ref nt) => match **nt { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 10f1daf1129..0f940cffcc4 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -485,7 +485,7 @@ impl<'a> Parser<'a> { /// Parses an implementation item. /// - /// ``` + /// ```ignore (illustrative) /// impl<'a, T> TYPE { /* impl items */ } /// impl<'a, T> TRAIT for TYPE { /* impl items */ } /// impl<'a, T> !TRAIT for TYPE { /* impl items */ } @@ -493,7 +493,7 @@ impl<'a> Parser<'a> { /// ``` /// /// We actually parse slightly more relaxed grammar for better error reporting and recovery. - /// ``` + /// ```ebnf /// "impl" GENERICS "const"? "!"? TYPE "for"? (TYPE | "..") ("where" PREDICATES)? "{" BODY "}" /// "impl" GENERICS "const"? "!"? TYPE ("where" PREDICATES)? "{" BODY "}" /// ``` @@ -806,7 +806,7 @@ impl<'a> Parser<'a> { } /// Parses a `type` alias with the following grammar: - /// ``` + /// ```ebnf /// TypeAlias = "type" Ident Generics {":" GenericBounds}? {"=" Ty}? ";" ; /// ``` /// The `"type"` has already been eaten. @@ -930,7 +930,7 @@ impl<'a> Parser<'a> { /// /// # Examples /// - /// ``` + /// ```ignore (illustrative) /// extern crate foo; /// extern crate bar as foo; /// ``` @@ -1020,7 +1020,7 @@ impl<'a> Parser<'a> { &format!("`{}` must come before `{}`", invalid_qual, current_qual), format!("{} {}", invalid_qual, current_qual), Applicability::MachineApplicable, - ).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`"); + ).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`"); } } Err(err) @@ -1630,7 +1630,7 @@ impl<'a> Parser<'a> { /// Parses a declarative macro 2.0 definition. /// The `macro` keyword has already been parsed. - /// ``` + /// ```ebnf /// MacBody = "{" TOKEN_STREAM "}" ; /// MacParams = "(" TOKEN_STREAM ")" ; /// DeclMac = "macro" Ident MacParams? MacBody ; @@ -2086,7 +2086,7 @@ impl<'a> Parser<'a> { &format!("`{misplaced_qual}` must come before `{current_qual}`"), format!("{misplaced_qual} {current_qual}"), Applicability::MachineApplicable, - ).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`"); + ).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`"); } } // Recover incorrect visibility order such as `async pub` diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index cd61584a876..63112f23605 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -26,11 +26,10 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, AstLike, AttrStyle, AttrVec, Const, CrateSugar, Extern}; -use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacDelimiter, Mutability, StrLit, Unsafe}; -use rustc_ast::{Visibility, VisibilityKind}; +use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacArgsEq, MacDelimiter, Mutability, StrLit}; +use rustc_ast::{Unsafe, Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; use rustc_errors::PResult; use rustc_errors::{ struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, MultiSpan, @@ -1157,13 +1156,7 @@ impl<'a> Parser<'a> { } else if !delimited_only { if self.eat(&token::Eq) { let eq_span = self.prev_token.span; - - // Collect tokens because they are used during lowering to HIR. - let expr = self.parse_expr_force_collect()?; - let span = expr.span; - - let token_kind = token::Interpolated(Lrc::new(token::NtExpr(expr))); - MacArgs::Eq(eq_span, Token::new(token_kind, span)) + MacArgs::Eq(eq_span, MacArgsEq::Ast(self.parse_expr_force_collect()?)) } else { MacArgs::Empty } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 9e771a8af1a..b0439a5987a 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -52,7 +52,7 @@ pub(super) enum RecoverQuestionMark { /// Signals whether parsing a type should recover `->`. /// /// More specifically, when parsing a function like: -/// ```rust +/// ```compile_fail /// fn foo() => u8 { 0 } /// fn bar(): u8 { 0 } /// ``` @@ -499,12 +499,12 @@ impl<'a> Parser<'a> { } /// Parses a function pointer type (`TyKind::BareFn`). - /// ``` - /// [unsafe] [extern "ABI"] fn (S) -> T - /// ^~~~~^ ^~~~^ ^~^ ^ - /// | | | | - /// | | | Return type - /// Function Style ABI Parameter types + /// ```ignore (illustrative) + /// [unsafe] [extern "ABI"] fn (S) -> T + /// // ^~~~~^ ^~~~^ ^~^ ^ + /// // | | | | + /// // | | | Return type + /// // Function Style ABI Parameter types /// ``` /// We actually parse `FnHeader FnDecl`, but we error on `const` and `async` qualifiers. fn parse_ty_bare_fn( @@ -707,7 +707,7 @@ impl<'a> Parser<'a> { } /// Parses a bound according to the grammar: - /// ``` + /// ```ebnf /// BOUND = TY_BOUND | LT_BOUND /// ``` fn parse_generic_bound(&mut self) -> PResult<'a, Result<GenericBound, Span>> { @@ -729,7 +729,7 @@ impl<'a> Parser<'a> { } /// Parses a lifetime ("outlives") bound, e.g. `'a`, according to: - /// ``` + /// ```ebnf /// LT_BOUND = LIFETIME /// ``` fn parse_generic_lt_bound( @@ -787,7 +787,7 @@ impl<'a> Parser<'a> { /// /// If no modifiers are present, this does not consume any tokens. /// - /// ``` + /// ```ebnf /// TY_BOUND_MODIFIERS = ["~const"] ["?"] /// ``` fn parse_ty_bound_modifiers(&mut self) -> PResult<'a, BoundModifiers> { @@ -807,7 +807,7 @@ impl<'a> Parser<'a> { } /// Parses a type bound according to: - /// ``` + /// ```ebnf /// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN) /// TY_BOUND_NOPAREN = [TY_BOUND_MODIFIERS] [for<LT_PARAM_DEFS>] SIMPLE_PATH /// ``` diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 4781813ee8e..47477898b24 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -2,8 +2,9 @@ use crate::parse_in; -use rustc_ast::tokenstream::{DelimSpan, TokenTree}; -use rustc_ast::{self as ast, Attribute, MacArgs, MacDelimiter, MetaItem, MetaItemKind}; +use rustc_ast::tokenstream::DelimSpan; +use rustc_ast::{self as ast, Attribute, MacArgs, MacArgsEq, MacDelimiter, MetaItem, MetaItemKind}; +use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, FatalError, PResult}; use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; @@ -42,16 +43,40 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta path: item.path.clone(), kind: match &item.args { MacArgs::Empty => MetaItemKind::Word, - MacArgs::Eq(_, t) => { - 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) => { check_meta_bad_delim(sess, *dspan, *delim, "wrong meta list delimiters"); let nmis = parse_in(sess, t.clone(), "meta list", |p| p.parse_meta_seq_top())?; MetaItemKind::List(nmis) } + MacArgs::Eq(_, MacArgsEq::Ast(expr)) => { + if let ast::ExprKind::Lit(lit) = &expr.kind { + if !lit.kind.is_unsuffixed() { + let mut err = sess.span_diagnostic.struct_span_err( + lit.span, + "suffixed literals are not allowed in attributes", + ); + err.help( + "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \ + use an unsuffixed version (`1`, `1.0`, etc.)", + ); + return Err(err); + } else { + MetaItemKind::NameValue(lit.clone()) + } + } else { + // The non-error case can happen with e.g. `#[foo = 1+1]`. The error case can + // happen with e.g. `#[foo = include_str!("non-existent-file.rs")]`; in that + // case we delay the error because an earlier error will have already been + // reported. + let msg = format!("unexpected expression: `{}`", pprust::expr_to_string(expr)); + let mut err = sess.span_diagnostic.struct_span_err(expr.span, msg); + if let ast::ExprKind::Err = expr.kind { + err.downgrade_to_delayed_bug(); + } + return Err(err); + } + } + MacArgs::Eq(_, MacArgsEq::Hir(lit)) => MetaItemKind::NameValue(lit.clone()), }, }) } diff --git a/compiler/rustc_parse_format/Cargo.toml b/compiler/rustc_parse_format/Cargo.toml index aa1714e820f..fcc68b3a219 100644 --- a/compiler/rustc_parse_format/Cargo.toml +++ b/compiler/rustc_parse_format/Cargo.toml @@ -4,5 +4,4 @@ version = "0.0.0" edition = "2021" [dependencies] -rustc_span = { path = "../rustc_span" } rustc_lexer = { path = "../rustc_lexer" } diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 00bac26a16a..f6fa19030ac 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -9,8 +9,8 @@ html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))) )] -#![feature(nll)] -#![feature(bool_to_option)] +// We want to be able to build this crate with a stable compiler, so no +// `#![feature]` attributes should be added. pub use Alignment::*; pub use Count::*; @@ -22,7 +22,19 @@ use std::iter; use std::str; use std::string; -use rustc_span::{InnerSpan, Symbol}; +// Note: copied from rustc_span +/// Range inside of a `Span` used for diagnostics when we only have access to relative positions. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct InnerSpan { + pub start: usize, + pub end: usize, +} + +impl InnerSpan { + pub fn new(start: usize, end: usize) -> InnerSpan { + InnerSpan { start, end } + } +} /// The type of format string that we are parsing. #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -57,7 +69,7 @@ pub enum Piece<'a> { #[derive(Copy, Clone, Debug, PartialEq)] pub struct Argument<'a> { /// Where to find this argument - pub position: Position, + pub position: Position<'a>, /// How to format the argument pub format: FormatSpec<'a>, } @@ -72,11 +84,11 @@ pub struct FormatSpec<'a> { /// Packed version of various flags provided. pub flags: u32, /// The integer precision to use. - pub precision: Count, + pub precision: Count<'a>, /// The span of the precision formatting flag (for diagnostics). pub precision_span: Option<InnerSpan>, /// The string width requested for the resulting format. - pub width: Count, + pub width: Count<'a>, /// The span of the width formatting flag (for diagnostics). pub width_span: Option<InnerSpan>, /// The descriptor string representing the name of the format desired for @@ -89,16 +101,16 @@ pub struct FormatSpec<'a> { /// Enum describing where an argument for a format can be located. #[derive(Copy, Clone, Debug, PartialEq)] -pub enum Position { +pub enum Position<'a> { /// The argument is implied to be located at an index ArgumentImplicitlyIs(usize), /// The argument is located at a specific index given in the format ArgumentIs(usize), /// The argument has a name. - ArgumentNamed(Symbol, InnerSpan), + ArgumentNamed(&'a str, InnerSpan), } -impl Position { +impl Position<'_> { pub fn index(&self) -> Option<usize> { match self { ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i), @@ -143,11 +155,11 @@ pub enum Flag { /// A count is used for the precision and width parameters of an integer, and /// can reference either an argument or a literal integer. #[derive(Copy, Clone, Debug, PartialEq)] -pub enum Count { +pub enum Count<'a> { /// The count is specified explicitly. CountIs(usize), /// The count is specified by the argument with the given name. - CountIsName(Symbol, InnerSpan), + CountIsName(&'a str, InnerSpan), /// The count is specified by the argument at the given index. CountIsParam(usize), /// The count is implied and cannot be explicitly specified. @@ -489,7 +501,7 @@ impl<'a> Parser<'a> { /// integer index of an argument, a named argument, or a blank string. /// Returns `Some(parsed_position)` if the position is not implicitly /// consuming a macro argument, `None` if it's the case. - fn position(&mut self) -> Option<Position> { + fn position(&mut self) -> Option<Position<'a>> { if let Some(i) = self.integer() { Some(ArgumentIs(i)) } else { @@ -498,7 +510,7 @@ impl<'a> Parser<'a> { let word = self.word(); let end = start + word.len(); let span = self.to_span_index(start).to(self.to_span_index(end)); - Some(ArgumentNamed(Symbol::intern(word), span)) + Some(ArgumentNamed(word, span)) } // This is an `ArgumentNext`. @@ -651,7 +663,7 @@ impl<'a> Parser<'a> { /// Parses a `Count` parameter at the current position. This does not check /// for 'CountIsNextParam' because that is only used in precision, not /// width. - fn count(&mut self, start: usize) -> (Count, Option<InnerSpan>) { + fn count(&mut self, start: usize) -> (Count<'a>, Option<InnerSpan>) { if let Some(i) = self.integer() { if let Some(end) = self.consume_pos('$') { let span = self.to_span_index(start).to(self.to_span_index(end + 1)); @@ -667,7 +679,7 @@ impl<'a> Parser<'a> { (CountImplied, None) } else if let Some(end) = self.consume_pos('$') { let span = self.to_span_index(start + 1).to(self.to_span_index(end)); - (CountIsName(Symbol::intern(word), span), None) + (CountIsName(word, span), None) } else { self.cur = tmp; (CountImplied, None) @@ -723,7 +735,7 @@ impl<'a> Parser<'a> { break; } } - found.then_some(cur) + if found { Some(cur) } else { None } } } diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index 6c960fdc72b..c9667922ee7 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -144,93 +144,91 @@ fn format_align_fill() { } #[test] fn format_counts() { - rustc_span::create_default_session_globals_then(|| { - same( - "{:10x}", - &[NextArgument(Argument { - position: ArgumentImplicitlyIs(0), - format: FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountImplied, - width: CountIs(10), - precision_span: None, - width_span: None, - ty: "x", - ty_span: None, - }, - })], - ); - same( - "{:10$.10x}", - &[NextArgument(Argument { - position: ArgumentImplicitlyIs(0), - format: FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountIs(10), - width: CountIsParam(10), - precision_span: None, - width_span: Some(InnerSpan::new(3, 6)), - ty: "x", - ty_span: None, - }, - })], - ); - same( - "{:.*x}", - &[NextArgument(Argument { - position: ArgumentImplicitlyIs(1), - format: FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountIsParam(0), - width: CountImplied, - precision_span: Some(InnerSpan::new(3, 5)), - width_span: None, - ty: "x", - ty_span: None, - }, - })], - ); - same( - "{:.10$x}", - &[NextArgument(Argument { - position: ArgumentImplicitlyIs(0), - format: FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountIsParam(10), - width: CountImplied, - precision_span: Some(InnerSpan::new(3, 7)), - width_span: None, - ty: "x", - ty_span: None, - }, - })], - ); - same( - "{:a$.b$?}", - &[NextArgument(Argument { - position: ArgumentImplicitlyIs(0), - format: FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountIsName(Symbol::intern("b"), InnerSpan::new(6, 7)), - width: CountIsName(Symbol::intern("a"), InnerSpan::new(4, 4)), - precision_span: None, - width_span: None, - ty: "?", - ty_span: None, - }, - })], - ); - }); + same( + "{:10x}", + &[NextArgument(Argument { + position: ArgumentImplicitlyIs(0), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountImplied, + width: CountIs(10), + precision_span: None, + width_span: None, + ty: "x", + ty_span: None, + }, + })], + ); + same( + "{:10$.10x}", + &[NextArgument(Argument { + position: ArgumentImplicitlyIs(0), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountIs(10), + width: CountIsParam(10), + precision_span: None, + width_span: Some(InnerSpan::new(3, 6)), + ty: "x", + ty_span: None, + }, + })], + ); + same( + "{:.*x}", + &[NextArgument(Argument { + position: ArgumentImplicitlyIs(1), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountIsParam(0), + width: CountImplied, + precision_span: Some(InnerSpan::new(3, 5)), + width_span: None, + ty: "x", + ty_span: None, + }, + })], + ); + same( + "{:.10$x}", + &[NextArgument(Argument { + position: ArgumentImplicitlyIs(0), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountIsParam(10), + width: CountImplied, + precision_span: Some(InnerSpan::new(3, 7)), + width_span: None, + ty: "x", + ty_span: None, + }, + })], + ); + same( + "{:a$.b$?}", + &[NextArgument(Argument { + position: ArgumentImplicitlyIs(0), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountIsName("b", InnerSpan::new(6, 7)), + width: CountIsName("a", InnerSpan::new(4, 4)), + precision_span: None, + width_span: None, + ty: "?", + ty_span: None, + }, + })], + ); } #[test] fn format_flags() { diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index 39e578bce7e..a3ef1981e84 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -9,6 +9,7 @@ rustc_middle = { path = "../rustc_middle" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } +rustc_expand = { path = "../rustc_expand" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_parse = { path = "../rustc_parse" } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index a9444972130..b297012f19f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -100,6 +100,7 @@ impl CheckAttrVisitor<'_> { sym::allow_internal_unstable => { self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs) } + sym::debugger_visualizer => self.check_debugger_visualizer(&attr, target), sym::rustc_allow_const_fn_unstable => { self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target) } @@ -124,6 +125,9 @@ impl CheckAttrVisitor<'_> { sym::rustc_allow_incoherent_impl => { self.check_allow_incoherent_impl(&attr, span, target) } + sym::rustc_has_incoherent_inherent_impls => { + self.check_has_incoherent_inherent_impls(&attr, span, target) + } sym::rustc_const_unstable | sym::rustc_const_stable | sym::unstable @@ -1095,7 +1099,6 @@ impl CheckAttrVisitor<'_> { } } - /// Warns against some misuses of `#[pass_by_value]` fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) -> bool { match target { Target::Method(MethodKind::Inherent) => true, @@ -1113,6 +1116,30 @@ impl CheckAttrVisitor<'_> { } } + fn check_has_incoherent_inherent_impls( + &self, + attr: &Attribute, + span: Span, + target: Target, + ) -> bool { + match target { + Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => { + true + } + _ => { + self.tcx + .sess + .struct_span_err( + attr.span, + "`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.", + ) + .span_label(span, "only adts, extern types and traits are supported") + .emit(); + false + } + } + } + /// Warns against some misuses of `#[must_use]` fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { let node = self.tcx.hir().get(hir_id); @@ -1860,6 +1887,65 @@ impl CheckAttrVisitor<'_> { } } + /// Checks if the items on the `#[debugger_visualizer]` attribute are valid. + fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool { + match target { + Target::Mod => {} + _ => { + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to a module") + .emit(); + return false; + } + } + + let hints = match attr.meta_item_list() { + Some(meta_item_list) => meta_item_list, + None => { + self.emit_debugger_visualizer_err(attr); + return false; + } + }; + + let hint = match hints.len() { + 1 => &hints[0], + _ => { + self.emit_debugger_visualizer_err(attr); + return false; + } + }; + + if !hint.has_name(sym::natvis_file) { + self.emit_debugger_visualizer_err(attr); + return false; + } + + let meta_item = match hint.meta_item() { + Some(meta_item) => meta_item, + None => { + self.emit_debugger_visualizer_err(attr); + return false; + } + }; + + match (meta_item.name_or_empty(), meta_item.value_str()) { + (sym::natvis_file, Some(_)) => true, + (_, _) => { + self.emit_debugger_visualizer_err(attr); + false + } + } + } + + fn emit_debugger_visualizer_err(&self, attr: &Attribute) { + self.tcx + .sess + .struct_span_err(attr.span, "invalid argument") + .note(r#"expected: `natvis_file = "..."`"#) + .emit(); + } + /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. /// (Allows proc_macro functions) fn check_rustc_allow_const_fn_unstable( diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs new file mode 100644 index 00000000000..f89092c57a3 --- /dev/null +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -0,0 +1,137 @@ +//! Detecting usage of the `#[debugger_visualizer]` attribute. + +use hir::CRATE_HIR_ID; +use rustc_data_structures::fx::FxHashSet; +use rustc_expand::base::resolve_path; +use rustc_hir as hir; +use rustc_hir::def_id::CrateNum; +use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::{HirId, Target}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::LOCAL_CRATE; +use rustc_span::{sym, DebuggerVisualizerFile, DebuggerVisualizerType}; + +use std::sync::Arc; + +struct DebuggerVisualizerCollector<'tcx> { + debugger_visualizers: FxHashSet<DebuggerVisualizerFile>, + tcx: TyCtxt<'tcx>, +} + +impl<'v, 'tcx> ItemLikeVisitor<'v> for DebuggerVisualizerCollector<'tcx> { + fn visit_item(&mut self, item: &hir::Item<'_>) { + let target = Target::from_item(item); + match target { + Target::Mod => { + self.check_for_debugger_visualizer(item.hir_id()); + } + _ => {} + } + } + + fn visit_trait_item(&mut self, _: &hir::TraitItem<'_>) {} + + fn visit_impl_item(&mut self, _: &hir::ImplItem<'_>) {} + + fn visit_foreign_item(&mut self, _: &hir::ForeignItem<'_>) {} +} + +impl<'tcx> DebuggerVisualizerCollector<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> DebuggerVisualizerCollector<'tcx> { + DebuggerVisualizerCollector { tcx, debugger_visualizers: FxHashSet::default() } + } + + fn check_for_debugger_visualizer(&mut self, hir_id: HirId) { + let attrs = self.tcx.hir().attrs(hir_id); + for attr in attrs { + if attr.has_name(sym::debugger_visualizer) { + let list = match attr.meta_item_list() { + Some(list) => list, + _ => continue, + }; + + let meta_item = match list.len() { + 1 => match list[0].meta_item() { + Some(meta_item) => meta_item, + _ => continue, + }, + _ => continue, + }; + + let file = match (meta_item.name_or_empty(), meta_item.value_str()) { + (sym::natvis_file, Some(value)) => { + match resolve_path(&self.tcx.sess.parse_sess, value.as_str(), attr.span) { + Ok(file) => file, + Err(mut err) => { + err.emit(); + continue; + } + } + } + (_, _) => continue, + }; + + if file.is_file() { + let contents = match std::fs::read(&file) { + Ok(contents) => contents, + Err(err) => { + self.tcx + .sess + .struct_span_err( + attr.span, + &format!( + "Unable to read contents of file `{}`. {}", + file.display(), + err + ), + ) + .emit(); + continue; + } + }; + + self.debugger_visualizers.insert(DebuggerVisualizerFile::new( + Arc::from(contents), + DebuggerVisualizerType::Natvis, + )); + } else { + self.tcx + .sess + .struct_span_err( + attr.span, + &format!("{} is not a valid file", file.display()), + ) + .emit(); + } + } + } + } +} + +/// Traverses and collects the debugger visualizers for a specific crate. +fn debugger_visualizers<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> Vec<DebuggerVisualizerFile> { + assert_eq!(cnum, LOCAL_CRATE); + + // Initialize the collector. + let mut collector = DebuggerVisualizerCollector::new(tcx); + + // Collect debugger visualizers in this crate. + tcx.hir().visit_all_item_likes(&mut collector); + + // Collect debugger visualizers on the crate attributes. + collector.check_for_debugger_visualizer(CRATE_HIR_ID); + + // Extract out the found debugger_visualizer items. + let DebuggerVisualizerCollector { debugger_visualizers, .. } = collector; + + let mut visualizers = debugger_visualizers.into_iter().collect::<Vec<_>>(); + + // Sort the visualizers so we always get a deterministic query result. + visualizers.sort(); + visualizers +} + +pub fn provide(providers: &mut Providers) { + providers.debugger_visualizers = debugger_visualizers; +} diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 18d9bdf8e17..79900a90aed 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -240,9 +240,9 @@ fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems { let crate_items = tcx.hir_crate_items(()); for id in crate_items.items() { - collector.check_for_lang(Target::from_def_kind(tcx.hir().def_kind(id.def_id)), id.hir_id()); + collector.check_for_lang(Target::from_def_kind(tcx.def_kind(id.def_id)), id.hir_id()); - if matches!(tcx.hir().def_kind(id.def_id), DefKind::Enum) { + if matches!(tcx.def_kind(id.def_id), DefKind::Enum) { let item = tcx.hir().item(id); if let hir::ItemKind::Enum(def, ..) = &item.kind { for variant in def.variants { diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 71d49d8b7ea..d9d08488d28 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -26,6 +26,7 @@ use rustc_middle::ty::query::Providers; mod check_attr; mod check_const; pub mod dead; +mod debugger_visualizer; mod diagnostic_items; pub mod entry; pub mod hir_id_validator; @@ -47,6 +48,7 @@ pub fn provide(providers: &mut Providers) { check_attr::provide(providers); check_const::provide(providers); dead::provide(providers); + debugger_visualizer::provide(providers); diagnostic_items::provide(providers); entry::provide(providers); lang_items::provide(providers); diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index b7502c4b1e2..fa05434e48b 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -20,6 +20,7 @@ rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +tracing = "0.1" [features] rustc_use_parallel_compiler = ["rustc-rayon-core", "rustc_query_system/rustc_use_parallel_compiler"] diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index bc82b0053b9..ae4ad428159 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -435,6 +435,8 @@ macro_rules! define_queries { fn force_from_dep_node(tcx: TyCtxt<'_>, dep_node: DepNode) -> bool { if let Some(key) = recover(tcx, dep_node) { + #[cfg(debug_assertions)] + let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); let tcx = QueryCtxt::from_tcx(tcx); force_query::<queries::$name<'_>, _>(tcx, key, dep_node); true @@ -532,6 +534,7 @@ macro_rules! define_queries_struct { $($(#[$attr])* #[inline(always)] + #[tracing::instrument(level = "trace", skip(self, tcx))] fn $name( &'tcx self, tcx: TyCtxt<$tcx>, diff --git a/compiler/rustc_query_system/src/dep_graph/debug.rs b/compiler/rustc_query_system/src/dep_graph/debug.rs index a544ac2c343..f9f3169af69 100644 --- a/compiler/rustc_query_system/src/dep_graph/debug.rs +++ b/compiler/rustc_query_system/src/dep_graph/debug.rs @@ -7,9 +7,9 @@ use std::error::Error; /// A dep-node filter goes from a user-defined string to a query over /// nodes. Right now the format is like this: -/// -/// x & y & z -/// +/// ```ignore (illustrative) +/// x & y & z +/// ``` /// where the format-string of the dep-node must contain `x`, `y`, and /// `z`. #[derive(Debug)] diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 6b70e9342fa..68284dcaa0b 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -1,5 +1,4 @@ #![feature(assert_matches)] -#![feature(bool_to_option)] #![feature(core_intrinsics)] #![feature(hash_raw_entry)] #![feature(let_else)] diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 700290d67a4..3498df95196 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -675,6 +675,7 @@ where } } +#[derive(Debug)] pub enum QueryMode { Get, Ensure, @@ -697,7 +698,6 @@ where None }; - debug!("ty::query::get_query<{}>(key={:?}, span={:?})", Q::NAME, key, span); let (result, dep_node_index) = try_execute_query( tcx, Q::query_state(tcx), diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index aef9fb57a6a..95e608da012 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -117,7 +117,7 @@ impl<'a> Resolver<'a> { } fn report_with_use_injections(&mut self, krate: &Crate) { - for UseError { mut err, candidates, def_id, instead, suggestion } in + for UseError { mut err, candidates, def_id, instead, suggestion, path } in self.use_injections.drain(..) { let (span, found_use) = if let Some(def_id) = def_id.as_local() { @@ -135,6 +135,7 @@ impl<'a> Resolver<'a> { if instead { Instead::Yes } else { Instead::No }, found_use, IsPattern::No, + path, ); } else if let Some((span, msg, sugg, appl)) = suggestion { err.span_suggestion(span, msg, sugg, appl); @@ -702,6 +703,7 @@ impl<'a> Resolver<'a> { Instead::No, FoundUse::Yes, IsPattern::Yes, + vec![], ); } err @@ -1482,6 +1484,7 @@ impl<'a> Resolver<'a> { Instead::No, FoundUse::Yes, IsPattern::No, + vec![], ); if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { @@ -2267,16 +2270,16 @@ impl<'a, 'b> ImportResolver<'a, 'b> { /// Given a `binding_span` of a binding within a use statement: /// -/// ``` +/// ```ignore (illustrative) /// use foo::{a, b, c}; -/// ^ +/// // ^ /// ``` /// /// then return the span until the next binding or the end of the statement: /// -/// ``` +/// ```ignore (illustrative) /// use foo::{a, b, c}; -/// ^^^ +/// // ^^^ /// ``` fn find_span_of_binding_until_next_binding( sess: &Session, @@ -2320,14 +2323,14 @@ fn find_span_of_binding_until_next_binding( /// Given a `binding_span`, return the span through to the comma or opening brace of the previous /// binding. /// -/// ``` +/// ```ignore (illustrative) /// use foo::a::{a, b, c}; -/// ^^--- binding span -/// | -/// returned span +/// // ^^--- binding span +/// // | +/// // returned span /// /// use foo::{a, b, c}; -/// --- binding span +/// // --- binding span /// ``` fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option<Span> { let source_map = sess.source_map(); @@ -2363,15 +2366,15 @@ fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option /// Given a `use_span` of a binding within a use statement, returns the highlighted span and if /// it is a nested use tree. /// -/// ``` +/// ```ignore (illustrative) /// use foo::a::{b, c}; -/// ^^^^^^^^^^ // false +/// // ^^^^^^^^^^ -- false /// /// use foo::{a, b, c}; -/// ^^^^^^^^^^ // true +/// // ^^^^^^^^^^ -- true /// /// use foo::{a, b::{c, d}}; -/// ^^^^^^^^^^^^^^^ // true +/// // ^^^^^^^^^^^^^^^ -- true /// ``` fn find_span_immediately_after_crate_name( sess: &Session, @@ -2448,6 +2451,7 @@ fn show_candidates( instead: Instead, found_use: FoundUse, is_pattern: IsPattern, + path: Vec<Segment>, ) { if candidates.is_empty() { return; @@ -2515,6 +2519,14 @@ fn show_candidates( accessible_path_strings.into_iter().map(|a| a.0), Applicability::MaybeIncorrect, ); + if let [first, .., last] = &path[..] { + err.span_suggestion_verbose( + first.ident.span.until(last.ident.span), + &format!("if you import `{}`, refer to it directly", last.ident), + String::new(), + Applicability::Unspecified, + ); + } } else { msg.push(':'); diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 18ce359524d..baaab33d71f 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1179,7 +1179,7 @@ impl<'a> Resolver<'a> { ConstantItemRibKind(trivial, _) => { let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. - if !(trivial || features.generic_const_exprs) { + if !(trivial == HasGenericParams::Yes || features.generic_const_exprs) { // HACK(min_const_generics): If we encounter `Self` in an anonymous constant // we can't easily tell if it's generic at this stage, so we instead remember // this and then enforce the self type to be concrete later on. @@ -1267,7 +1267,7 @@ impl<'a> Resolver<'a> { ConstantItemRibKind(trivial, _) => { let features = self.session.features_untracked(); // HACK(min_const_generics): We currently only allow `N` or `{ N }`. - if !(trivial || features.generic_const_exprs) { + if !(trivial == HasGenericParams::Yes || features.generic_const_exprs) { if let Some(span) = finalize { self.report_error( span, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b89b9c376af..723e66e9ef6 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -94,6 +94,12 @@ crate enum HasGenericParams { No, } +impl HasGenericParams { + fn force_yes_if(self, b: bool) -> Self { + if b { Self::Yes } else { self } + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] crate enum ConstantItemKind { Const, @@ -125,9 +131,9 @@ crate enum RibKind<'a> { /// We're in a constant item. Can't refer to dynamic stuff. /// - /// 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, Option<(Ident, ConstantItemKind)>), + /// The item may reference generic parameters in trivial constant expressions. + /// All other constants aren't allowed to use generic params at all. + ConstantItemRibKind(HasGenericParams, Option<(Ident, ConstantItemKind)>), /// We passed through a module. ModuleRibKind(Module<'a>), @@ -390,13 +396,10 @@ impl<'a> PathSource<'a> { ) | Res::Local(..) | Res::SelfCtor(..) ), - PathSource::Pat => matches!( - res, - Res::Def( - DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst, - _, - ) | Res::SelfCtor(..) - ), + PathSource::Pat => { + res.expected_in_unit_struct_pat() + || matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _)) + } PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(), PathSource::Struct => matches!( res, @@ -483,6 +486,9 @@ struct DiagnosticMetadata<'ast> { current_where_predicate: Option<&'ast WherePredicate>, current_type_path: Option<&'ast Ty>, + + /// The current impl items (used to suggest). + current_impl_items: Option<&'ast [P<AssocItem>]>, } struct LateResolutionVisitor<'a, 'b, 'ast> { @@ -826,19 +832,24 @@ 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, None, |this| { - this.smart_resolve_path( - ty.id, - qself.as_ref(), - path, - PathSource::Expr(None), - ); - - if let Some(ref qself) = *qself { - this.visit_ty(&qself.ty); - } - this.visit_path(path, ty.id); - }); + self.with_constant_rib( + IsRepeatExpr::No, + HasGenericParams::Yes, + None, + |this| { + this.smart_resolve_path( + ty.id, + qself.as_ref(), + path, + PathSource::Expr(None), + ); + + if let Some(ref qself) = *qself { + this.visit_ty(&qself.ty); + } + this.visit_path(path, ty.id); + }, + ); self.diagnostic_metadata.currently_processing_generics = prev; return; @@ -1629,7 +1640,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { items: ref impl_items, .. }) => { + self.diagnostic_metadata.current_impl_items = Some(impl_items); self.resolve_implementation(generics, of_trait, &self_ty, item.id, impl_items); + self.diagnostic_metadata.current_impl_items = None; } ItemKind::Trait(box Trait { ref generics, ref bounds, ref items, .. }) => { @@ -1684,7 +1697,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // not used as part of the type system, this is far less surprising. this.with_constant_rib( IsRepeatExpr::No, - true, + HasGenericParams::Yes, None, |this| this.visit_expr(expr), ); @@ -1763,7 +1776,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // so it doesn't matter whether this is a trivial constant. this.with_constant_rib( IsRepeatExpr::No, - true, + HasGenericParams::Yes, Some((item.ident, constant_item_kind)), |this| this.visit_expr(expr), ); @@ -1909,20 +1922,23 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // Note that we intentionally still forbid `[0; N + 1]` during // name resolution so that we don't extend the future // compat lint to new cases. + #[instrument(level = "debug", skip(self, f))] fn with_constant_rib( &mut self, is_repeat: IsRepeatExpr, - is_trivial: bool, + may_use_generics: HasGenericParams, 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, item), |this| { + self.with_rib(ValueNS, ConstantItemRibKind(may_use_generics, item), |this| { this.with_rib( TypeNS, - ConstantItemRibKind(is_repeat == IsRepeatExpr::Yes || is_trivial, item), + ConstantItemRibKind( + may_use_generics.force_yes_if(is_repeat == IsRepeatExpr::Yes), + item, + ), |this| { - this.with_label_rib(ConstantItemRibKind(is_trivial, item), f); + this.with_label_rib(ConstantItemRibKind(may_use_generics, item), f); }, ) }); @@ -2064,7 +2080,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // not used as part of the type system, this is far less surprising. this.with_constant_rib( IsRepeatExpr::No, - true, + HasGenericParams::Yes, None, |this| { visit::walk_assoc_item( @@ -2682,6 +2698,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { def_id, instead, suggestion, + path: path.into(), }); } @@ -2745,6 +2762,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { def_id, instead: false, suggestion: None, + path: path.into(), }); } else { err.cancel(); @@ -3077,12 +3095,23 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { debug!("resolve_anon_const {:?} is_repeat: {:?}", constant, is_repeat); self.with_constant_rib( is_repeat, - constant.value.is_potential_trivial_const_param(), + if constant.value.is_potential_trivial_const_param() { + HasGenericParams::Yes + } else { + HasGenericParams::No + }, None, |this| visit::walk_anon_const(this, constant), ); } + fn resolve_inline_const(&mut self, constant: &'ast AnonConst) { + debug!("resolve_anon_const {constant:?}"); + self.with_constant_rib(IsRepeatExpr::No, HasGenericParams::Yes, None, |this| { + visit::walk_anon_const(this, constant); + }); + } + fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) { // First, record candidate traits for this expression if it could // result in the invocation of a method call. @@ -3180,7 +3209,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { if const_args.contains(&idx) { self.with_constant_rib( IsRepeatExpr::No, - argument.is_potential_trivial_const_param(), + if argument.is_potential_trivial_const_param() { + HasGenericParams::Yes + } else { + HasGenericParams::No + }, None, |this| { this.resolve_expr(argument, None); @@ -3235,7 +3268,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }); } ExprKind::ConstBlock(ref ct) => { - self.resolve_anon_const(ct, IsRepeatExpr::No); + self.resolve_inline_const(ct); } ExprKind::Index(ref elem, ref idx) => { self.resolve_expr(elem, Some(expr)); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 3076cc11317..4f07d0076f1 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -6,7 +6,7 @@ use crate::path_names_to_string; use crate::{Module, ModuleKind, ModuleOrUniformRoot}; use crate::{PathResult, PathSource, Segment}; -use rustc_ast::visit::FnKind; +use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{ self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, NodeId, Path, Ty, TyKind, @@ -144,15 +144,22 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); // Make the base error. + struct BaseError<'a> { + msg: String, + fallback_label: String, + span: Span, + could_be_expr: bool, + suggestion: Option<(Span, &'a str, String)>, + } let mut expected = source.descr_expected(); let path_str = Segment::names_to_string(path); let item_str = path.last().unwrap().ident; - let (base_msg, fallback_label, base_span, could_be_expr) = if let Some(res) = res { - ( - format!("expected {}, found {} `{}`", expected, res.descr(), path_str), - format!("not a {}", expected), + let base_error = if let Some(res) = res { + BaseError { + msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str), + fallback_label: format!("not a {expected}"), span, - match res { + could_be_expr: match res { Res::Def(DefKind::Fn, _) => { // Verify whether this is a fn call or an Fn used as a type. self.r @@ -171,22 +178,49 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { | Res::Local(_) => true, _ => false, }, - ) + suggestion: None, + } } else { let item_span = path.last().unwrap().ident.span; - let (mod_prefix, mod_str) = if path.len() == 1 { - (String::new(), "this scope".to_string()) + let (mod_prefix, mod_str, suggestion) = if path.len() == 1 { + debug!(?self.diagnostic_metadata.current_impl_items); + debug!(?self.diagnostic_metadata.current_function); + let suggestion = if let Some(items) = self.diagnostic_metadata.current_impl_items + && let Some((fn_kind, _)) = self.diagnostic_metadata.current_function + && self.current_trait_ref.is_none() + && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt() + && let Some(item) = items.iter().find(|i| { + if let AssocItemKind::Fn(fn_) = &i.kind + && !fn_.sig.decl.has_self() + && i.ident.name == item_str.name + { + debug!(?item_str.name); + debug!(?fn_.sig.decl.inputs); + return true + } + false + }) + { + Some(( + item_span, + "consider using the associated function", + format!("Self::{}", item.ident) + )) + } else { + None + }; + (String::new(), "this scope".to_string(), suggestion) } else if path.len() == 2 && path[0].ident.name == kw::PathRoot { if self.r.session.edition() > Edition::Edition2015 { // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude // which overrides all other expectations of item type expected = "crate"; - (String::new(), "the list of imported crates".to_string()) + (String::new(), "the list of imported crates".to_string(), None) } else { - (String::new(), "the crate root".to_string()) + (String::new(), "the crate root".to_string(), None) } } else if path.len() == 2 && path[0].ident.name == kw::Crate { - (String::new(), "the crate root".to_string()) + (String::new(), "the crate root".to_string(), None) } else { let mod_path = &path[..path.len() - 1]; let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) { @@ -194,22 +228,28 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { _ => None, } .map_or_else(String::new, |res| format!("{} ", res.descr())); - (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path))) + (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), None) }; - ( - format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str), - if path_str == "async" && expected.starts_with("struct") { + BaseError { + msg: format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"), + fallback_label: if path_str == "async" && expected.starts_with("struct") { "`async` blocks are only allowed in Rust 2018 or later".to_string() } else { - format!("not found in {}", mod_str) + format!("not found in {mod_str}") }, - item_span, - false, - ) + span: item_span, + could_be_expr: false, + suggestion, + } }; let code = source.error_code(res.is_some()); - let mut err = self.r.session.struct_span_err_with_code(base_span, &base_msg, code); + let mut err = + self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code); + + if let Some(sugg) = base_error.suggestion { + err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect); + } if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal { err.multipart_suggestion( @@ -269,7 +309,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } - self.detect_assoct_type_constraint_meant_as_path(base_span, &mut err); + self.detect_assoct_type_constraint_meant_as_path(base_error.span, &mut err); // Emit special messages for unresolved `Self` and `self`. if is_self_type(path, ns) { @@ -471,7 +511,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { source, res, &path_str, - &fallback_label, + &base_error.fallback_label, ) { // We do this to avoid losing a secondary span when we override the main error span. self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span); @@ -479,8 +519,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } - let is_macro = base_span.from_expansion() && base_span.desugaring_kind().is_none(); - if !self.type_ascription_suggestion(&mut err, base_span) { + let is_macro = + base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none(); + if !self.type_ascription_suggestion(&mut err, base_error.span) { let mut fallback = false; if let ( PathSource::Trait(AliasPossibility::Maybe), @@ -493,7 +534,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { let spans: Vec<Span> = bounds .iter() .map(|bound| bound.span()) - .filter(|&sp| sp != base_span) + .filter(|&sp| sp != base_error.span) .collect(); let start_span = bounds.iter().map(|bound| bound.span()).next().unwrap(); @@ -515,7 +556,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { multi_span.push_span_label(sp, msg); } multi_span.push_span_label( - base_span, + base_error.span, "expected this type to be a trait...".to_string(), ); err.span_help( @@ -525,14 +566,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ); if bounds.iter().all(|bound| match bound { ast::GenericBound::Outlives(_) => true, - ast::GenericBound::Trait(tr, _) => tr.span == base_span, + ast::GenericBound::Trait(tr, _) => tr.span == base_error.span, }) { let mut sugg = vec![]; - if base_span != start_span { - sugg.push((start_span.until(base_span), String::new())); + if base_error.span != start_span { + sugg.push((start_span.until(base_error.span), String::new())); } - if base_span != end_span { - sugg.push((base_span.shrink_to_hi().to(end_span), String::new())); + if base_error.span != end_span { + sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new())); } err.multipart_suggestion( @@ -550,7 +591,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fallback = true; match self.diagnostic_metadata.current_let_binding { Some((pat_sp, Some(ty_sp), None)) - if ty_sp.contains(base_span) && could_be_expr => + if ty_sp.contains(base_error.span) && base_error.could_be_expr => { err.span_suggestion_short( pat_sp.between(ty_sp), @@ -568,7 +609,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } if fallback { // Fallback label. - err.span_label(base_span, fallback_label); + err.span_label(base_error.span, base_error.fallback_label); } } if let Some(err_code) = &err.code { diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 50428811fff..030c27af444 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -401,7 +401,7 @@ pub fn provide(providers: &mut ty::query::Providers) { /// The reason for this separate call is to resolve what would otherwise /// be a cycle. Consider this example: /// -/// ```rust +/// ```ignore UNSOLVED (maybe @jackh726 knows what lifetime parameter to give Sub) /// trait Base<'a> { /// type BaseItem; /// } @@ -2546,7 +2546,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// Returns all the late-bound vars that come into scope from supertrait HRTBs, based on the /// associated type name and starting trait. /// For example, imagine we have - /// ```rust + /// ```ignore (illustrative) /// trait Foo<'a, 'b> { /// type As; /// } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index f6109b1dc1a..62485beac47 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -9,7 +9,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(box_patterns)] #![feature(drain_filter)] -#![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(if_let_guard)] #![feature(let_chains)] @@ -696,6 +695,9 @@ struct UseError<'a> { instead: bool, /// Extra free-form suggestion. suggestion: Option<(Span, &'static str, String, Applicability)>, + /// Path `Segment`s at the place of use that failed. Used for accurate suggestion after telling + /// the user to import the item directly. + path: Vec<Segment>, } #[derive(Clone, Copy, PartialEq, Debug)] diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index e933fe1cb24..6fb87e15a33 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -289,12 +289,26 @@ impl ParseSess { self.proc_macro_quoted_spans.lock().clone() } + pub fn create_err<'a>( + &'a self, + err: impl SessionDiagnostic<'a>, + ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + err.into_diagnostic(self) + } + pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed { - err.into_diagnostic(self).emit() + self.create_err(err).emit() + } + + pub fn create_warning<'a>( + &'a self, + warning: impl SessionDiagnostic<'a, ()>, + ) -> DiagnosticBuilder<'a, ()> { + warning.into_diagnostic(self) } pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) { - warning.into_diagnostic(self).emit() + self.create_warning(warning).emit() } pub fn struct_err( diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index e8279f6fed2..b2c23cda6aa 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -413,9 +413,21 @@ impl Session { pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed { self.diagnostic().err(msg) } + pub fn create_err<'a>( + &'a self, + err: impl SessionDiagnostic<'a>, + ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + self.parse_sess.create_err(err) + } pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed { self.parse_sess.emit_err(err) } + pub fn create_warning<'a>( + &'a self, + err: impl SessionDiagnostic<'a, ()>, + ) -> DiagnosticBuilder<'a, ()> { + self.parse_sess.create_warning(err) + } pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) { self.parse_sess.emit_warning(warning) } diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index d5f806308cf..3976c062221 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -279,8 +279,14 @@ impl DefId { } #[inline] + #[track_caller] pub fn expect_local(self) -> LocalDefId { - self.as_local().unwrap_or_else(|| panic!("DefId::expect_local: `{:?}` isn't local", self)) + // NOTE: `match` below is required to apply `#[track_caller]`, + // i.e. don't use closures. + match self.as_local() { + Some(local_def_id) => local_def_id, + None => panic!("DefId::expect_local: `{:?}` isn't local", self), + } } #[inline] diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 447b73fa3c3..f194cf50211 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -686,7 +686,7 @@ impl SyntaxContext { /// context up one macro definition level. That is, if we have a nested macro /// definition as follows: /// - /// ```rust + /// ```ignore (illustrative) /// macro_rules! f { /// macro_rules! g { /// ... @@ -710,6 +710,7 @@ impl SyntaxContext { /// For example, consider the following three resolutions of `f`: /// /// ```rust + /// #![feature(decl_macro)] /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty. /// m!(f); /// macro m($f:ident) { @@ -746,7 +747,8 @@ impl SyntaxContext { /// via a glob import with the given `SyntaxContext`. /// For example: /// - /// ```rust + /// ```compile_fail,E0425 + /// #![feature(decl_macro)] /// m!(f); /// macro m($i:ident) { /// mod foo { @@ -786,7 +788,7 @@ impl SyntaxContext { /// Undo `glob_adjust` if possible: /// - /// ```rust + /// ```ignore (illustrative) /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) { /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope)); /// } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index f22faef2580..7357cebf62e 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -15,7 +15,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(array_windows)] -#![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(let_else)] #![feature(if_let_guard)] @@ -71,6 +70,7 @@ use std::hash::Hash; use std::ops::{Add, Range, Sub}; use std::path::{Path, PathBuf}; use std::str::FromStr; +use std::sync::Arc; use md5::Digest; use md5::Md5; @@ -1200,6 +1200,28 @@ impl SourceFileHash { } } +#[derive(HashStable_Generic)] +#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] +pub enum DebuggerVisualizerType { + Natvis, +} + +/// A single debugger visualizer file. +#[derive(HashStable_Generic)] +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)] +pub struct DebuggerVisualizerFile { + /// The complete debugger visualizer source. + pub src: Arc<[u8]>, + /// Indicates which visualizer type this targets. + pub visualizer_type: DebuggerVisualizerType, +} + +impl DebuggerVisualizerFile { + pub fn new(src: Arc<[u8]>, visualizer_type: DebuggerVisualizerType) -> Self { + DebuggerVisualizerFile { src, visualizer_type } + } +} + /// A single source in the [`SourceMap`]. #[derive(Clone)] pub struct SourceFile { diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 460b5c18fc1..505c0af953a 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -1058,10 +1058,11 @@ impl SourceMap { /// Tries to find the span of the semicolon of a macro call statement. /// The input must be the *call site* span of a statement from macro expansion. - /// - /// v output - /// mac!(); - /// ^^^^^^ input + /// ```ignore (illustrative) + /// // v output + /// mac!(); + /// // ^^^^^^ input + /// ``` pub fn mac_call_stmt_semi_span(&self, mac_call: Span) -> Option<Span> { let span = self.span_extend_while(mac_call, char::is_whitespace).ok()?; let span = span.shrink_to_hi().with_hi(BytePos(span.hi().0.checked_add(1)?)); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c1299c94c4b..d6885bebc32 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -556,6 +556,7 @@ symbols! { debug_struct, debug_trait_builder, debug_tuple, + debugger_visualizer, decl_macro, declare_lint_pass, decode, @@ -927,6 +928,7 @@ symbols! { native_link_modifiers_bundle, native_link_modifiers_verbatim, native_link_modifiers_whole_archive, + natvis_file, ne, nearbyintf32, nearbyintf64, @@ -1187,6 +1189,7 @@ symbols! { rustc_error, rustc_evaluate_where_clauses, rustc_expected_cgu_reuse, + rustc_has_incoherent_inherent_impls, rustc_if_this_changed, rustc_inherit_overflow_checks, rustc_insignificant_dtor, diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 0e8fd9cc93f..a2cd3c4c468 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -276,12 +276,19 @@ impl ToJson for Endian { } /// Size of a type in bytes. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] #[derive(HashStable_Generic)] pub struct Size { raw: u64, } +// This is debug-printed a lot in larger structs, don't waste too much space there +impl fmt::Debug for Size { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Size({} bytes)", self.bytes()) + } +} + impl Size { pub const ZERO: Size = Size { raw: 0 }; @@ -485,12 +492,19 @@ impl Step for Size { } /// Alignment of a type in bytes (always a power of two). -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)] #[derive(HashStable_Generic)] pub struct Align { pow2: u8, } +// This is debug-printed a lot in larger structs, don't waste too much space there +impl fmt::Debug for Align { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Align({} bytes)", self.bytes()) + } +} + impl Align { pub const ONE: Align = Align { pow2: 0 }; diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index e9ef71ede51..4dc6af15001 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -9,7 +9,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(associated_type_bounds)] -#![feature(bool_to_option)] #![feature(exhaustive_patterns)] #![feature(let_else)] #![feature(min_specialization)] diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 2ae7f34a91e..0dd497448ca 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -12,7 +12,6 @@ #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(crate_visibility_modifier)] diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 4b949ff8b95..5bd49dd21e7 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -22,11 +22,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { /// (*), computes the "definition type" for an opaque type /// definition -- that is, the inferred value of `Foo1<'x>` or /// `Foo2<'x>` that we would conceptually use in its definition: - /// - /// type Foo1<'x> = impl Bar<'x> = AAA; <-- this type AAA - /// type Foo2<'x> = impl Bar<'x> = BBB; <-- or this type BBB - /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } - /// + /// ```ignore (illustrative) + /// type Foo1<'x> = impl Bar<'x> = AAA; // <-- this type AAA + /// type Foo2<'x> = impl Bar<'x> = BBB; // <-- or this type BBB + /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } + /// ``` /// Note that these values are defined in terms of a distinct set of /// generic parameters (`'x` instead of `'a`) from C1 or C2. The main /// purpose of this function is to do that translation. diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index d95512bb88f..79e9635f90e 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -255,9 +255,9 @@ impl<'tcx> AutoTraitFinder<'tcx> { /// `FulfillmentContext` will drive `SelectionContext` to consider that impl before giving up. /// If we were to rely on `FulfillmentContext`s decision, we might end up synthesizing an impl /// like this: - /// - /// impl<T> Send for Foo<T> where T: IntoIterator - /// + /// ```ignore (illustrative) + /// impl<T> Send for Foo<T> where T: IntoIterator + /// ``` /// While it might be technically true that Foo implements Send where `T: IntoIterator`, /// the bound is overly restrictive - it's really only necessary that `T: Iterator`. /// @@ -420,10 +420,10 @@ impl<'tcx> AutoTraitFinder<'tcx> { /// two trait predicates that differ only in their region parameters: /// one containing a HRTB lifetime parameter, and one containing a 'normal' /// lifetime parameter. For example: - /// - /// T as MyTrait<'a> - /// T as MyTrait<'static> - /// + /// ```ignore (illustrative) + /// T as MyTrait<'a> + /// T as MyTrait<'static> + /// ``` /// If we put both of these predicates in our computed `ParamEnv`, we'll /// confuse `SelectionContext`, since it will (correctly) view both as being applicable. /// diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 40c3f4047ae..24110d30a84 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -547,7 +547,7 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe /// 2. They ground negative reasoning for coherence. If a user wants to /// write both a conditional blanket impl and a specific impl, we need to /// make sure they do not overlap. For example, if we write -/// ``` +/// ```ignore (illustrative) /// impl<T> IntoIterator for Vec<T> /// impl<T: Iterator> IntoIterator for T /// ``` diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index a3d3c7c0cf3..bfb8ce6f105 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -866,7 +866,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return false; } - let orig_ty = old_pred.self_ty().skip_binder(); + // This is a quick fix to resolve an ICE (#96223). + // This change should probably be deeper. + // As suggested by @jackh726, `mk_trait_obligation_with_new_self_ty` could take a `Binder<(TraitRef, Ty)> + // instead of `Binder<Ty>` leading to some changes to its call places. + let Some(orig_ty) = old_pred.self_ty().no_bound_vars() else { + return false; + }; let mk_result = |new_ty| { let obligation = self.mk_trait_obligation_with_new_self_ty(param_env, old_pred, new_ty); diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index b39310d1294..e1b6ed92269 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -595,7 +595,7 @@ fn object_ty_for_trait<'tcx>( /// - let `Receiver` be the type of the `self` argument, i.e `Self`, `&Self`, `Rc<Self>`, /// - require the following bound: /// -/// ``` +/// ```ignore (not-rust) /// Receiver[Self => T]: DispatchFromDyn<Receiver[Self => dyn Trait]> /// ``` /// @@ -621,13 +621,13 @@ fn object_ty_for_trait<'tcx>( /// Instead, we fudge a little by introducing a new type parameter `U` such that /// `Self: Unsize<U>` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`. /// Written as a chalk-style query: -/// -/// forall (U: Trait + ?Sized) { -/// if (Self: Unsize<U>) { -/// Receiver: DispatchFromDyn<Receiver[Self => U]> -/// } +/// ```ignore (not-rust) +/// forall (U: Trait + ?Sized) { +/// if (Self: Unsize<U>) { +/// Receiver: DispatchFromDyn<Receiver[Self => U]> /// } -/// +/// } +/// ``` /// for `self: &'a mut Self`, this means `&'a mut Self: DispatchFromDyn<&'a mut U>` /// for `self: Rc<Self>`, this means `Rc<Self>: DispatchFromDyn<Rc<U>>` /// for `self: Pin<Box<Self>>`, this means `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<U>>>` diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs index ce0e0a21ff5..c266eec25aa 100644 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs @@ -304,42 +304,40 @@ impl<'tcx> OnUnimplementedFormatString { match token { Piece::String(_) => (), // Normal string, no need to check it Piece::NextArgument(a) => match a.position { - // `{Self}` is allowed - Position::ArgumentNamed(s, _) if s == kw::SelfUpper => (), - // `{ThisTraitsName}` is allowed - Position::ArgumentNamed(s, _) if s == trait_name => (), - // `{from_method}` is allowed - Position::ArgumentNamed(s, _) if s == sym::from_method => (), - // `{from_desugaring}` is allowed - Position::ArgumentNamed(s, _) if s == sym::from_desugaring => (), - // `{ItemContext}` is allowed - Position::ArgumentNamed(s, _) if s == sym::ItemContext => (), - // `{integral}` and `{integer}` and `{float}` are allowed - Position::ArgumentNamed(s, _) - if s == sym::integral || s == sym::integer_ || s == sym::float => - { - () - } - // So is `{A}` if A is a type parameter Position::ArgumentNamed(s, _) => { - match generics.params.iter().find(|param| param.name == s) { - Some(_) => (), - None => { - let reported = struct_span_err!( - tcx.sess, - span, - E0230, - "there is no parameter `{}` on {}", - s, - if trait_def_id == item_def_id { - format!("trait `{}`", trait_name) - } else { - "impl".to_string() - } - ) - .emit(); - result = Err(reported); - } + match Symbol::intern(s) { + // `{Self}` is allowed + kw::SelfUpper => (), + // `{ThisTraitsName}` is allowed + s if s == trait_name => (), + // `{from_method}` is allowed + sym::from_method => (), + // `{from_desugaring}` is allowed + sym::from_desugaring => (), + // `{ItemContext}` is allowed + sym::ItemContext => (), + // `{integral}` and `{integer}` and `{float}` are allowed + sym::integral | sym::integer_ | sym::float => (), + // So is `{A}` if A is a type parameter + s => match generics.params.iter().find(|param| param.name == s) { + Some(_) => (), + None => { + let reported = struct_span_err!( + tcx.sess, + span, + E0230, + "there is no parameter `{}` on {}", + s, + if trait_def_id == item_def_id { + format!("trait `{}`", trait_name) + } else { + "impl".to_string() + } + ) + .emit(); + result = Err(reported); + } + }, } } // `{:1}` and `{}` are not to be used @@ -392,34 +390,37 @@ impl<'tcx> OnUnimplementedFormatString { .map(|p| match p { Piece::String(s) => s, Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s, _) => match generic_map.get(&s) { - Some(val) => val, - None if s == name => &trait_str, - None => { - if let Some(val) = options.get(&s) { - val - } else if s == sym::from_desugaring || s == sym::from_method { - // don't break messages using these two arguments incorrectly - &empty_string - } else if s == sym::ItemContext { - &item_context - } else if s == sym::integral { - "{integral}" - } else if s == sym::integer_ { - "{integer}" - } else if s == sym::float { - "{float}" - } else { - bug!( - "broken on_unimplemented {:?} for {:?}: \ + Position::ArgumentNamed(s, _) => { + let s = Symbol::intern(s); + match generic_map.get(&s) { + Some(val) => val, + None if s == name => &trait_str, + None => { + if let Some(val) = options.get(&s) { + val + } else if s == sym::from_desugaring || s == sym::from_method { + // don't break messages using these two arguments incorrectly + &empty_string + } else if s == sym::ItemContext { + &item_context + } else if s == sym::integral { + "{integral}" + } else if s == sym::integer_ { + "{integer}" + } else if s == sym::float { + "{float}" + } else { + bug!( + "broken on_unimplemented {:?} for {:?}: \ no argument matching {:?}", - self.0, - trait_ref, - s - ) + self.0, + trait_ref, + s + ) + } } } - }, + } _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0), }, }) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index c7a61cbe25a..be4d734b964 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -158,9 +158,9 @@ pub(super) enum ProjectAndUnifyResult<'tcx> { } /// Evaluates constraints of the form: -/// -/// for<...> <T as Trait>::U == V -/// +/// ```ignore (not-rust) +/// for<...> <T as Trait>::U == V +/// ``` /// If successful, this may result in additional obligations. Also returns /// the projection cache key used to track these additional obligations. /// @@ -224,9 +224,9 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>( } /// Evaluates constraints of the form: -/// -/// <T as Trait>::U == V -/// +/// ```ignore (not-rust) +/// <T as Trait>::U == V +/// ``` /// If successful, this may result in additional obligations. /// /// See [poly_project_and_unify_type] for an explanation of the return value. @@ -1258,7 +1258,7 @@ fn assemble_candidates_from_param_env<'cx, 'tcx>( /// In the case of a nested projection like <<A as Foo>::FooT as Bar>::BarT, we may find /// that the definition of `Foo` has some clues: /// -/// ``` +/// ```ignore (illustrative) /// trait Foo { /// type FooT : Bar<BarT=i32> /// } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index dbb6c54fcd9..07720ba71ca 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -175,9 +175,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let needs_infer = stack.obligation.predicate.has_infer_types_or_consts(); - let sized_predicate = self.tcx().lang_items().sized_trait() - == Some(stack.obligation.predicate.skip_binder().def_id()); - // If there are STILL multiple candidates, we can further // reduce the list by dropping duplicates -- including // resolving specializations. @@ -186,7 +183,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { while i < candidates.len() { let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { self.candidate_should_be_dropped_in_favor_of( - sized_predicate, &candidates[i], &candidates[j], needs_infer, diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index b97ab39d991..771a3072af5 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -712,9 +712,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// and we desugared it so that the type of the expression is /// `Closure`, and `Closure` expects `i32` as argument. Then it /// is "as if" the compiler generated this impl: - /// - /// impl Fn(i32) for Closure { ... } - /// + /// ```ignore (illustrative) + /// impl Fn(i32) for Closure { ... } + /// ``` /// Now imagine our obligation is `Closure: Fn(usize)`. So far /// we have matched the self type `Closure`. At this point we'll /// compare the `i32` to `usize` and generate an error. diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index e69bf6eca90..6584d33032a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -741,39 +741,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if reached_depth >= stack.depth { debug!(?result, "CACHE MISS"); self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result); - - stack.cache().on_completion( - stack.dfn, - |fresh_trait_pred, provisional_result, provisional_dep_node| { - // Create a new `DepNode` that has dependencies on: - // * The `DepNode` for the original evaluation that resulted in a provisional cache - // entry being crated - // * The `DepNode` for the *current* evaluation, which resulted in us completing - // provisional caches entries and inserting them into the evaluation cache - // - // This ensures that when a query reads this entry from the evaluation cache, - // it will end up (transitively) depending on all of the incr-comp dependencies - // created during the evaluation of this trait. For example, evaluating a trait - // will usually require us to invoke `type_of(field_def_id)` to determine the - // constituent types, and we want any queries reading from this evaluation - // cache entry to end up with a transitive `type_of(field_def_id`)` dependency. - // - // By using `in_task`, we're also creating an edge from the *current* query - // to the newly-created `combined_dep_node`. This is probably redundant, - // but it's better to add too many dep graph edges than to add too few - // dep graph edges. - let ((), combined_dep_node) = self.in_task(|this| { - this.tcx().dep_graph.read_index(provisional_dep_node); - this.tcx().dep_graph.read_index(dep_node); - }); - self.insert_evaluation_cache( - param_env, - fresh_trait_pred, - combined_dep_node, - provisional_result.max(result), - ); - }, - ); + stack.cache().on_completion(stack.dfn); } else { debug!(?result, "PROVISIONAL"); debug!( @@ -782,13 +750,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fresh_trait_pred, stack.depth, reached_depth, ); - stack.cache().insert_provisional( - stack.dfn, - reached_depth, - fresh_trait_pred, - result, - dep_node, - ); + stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_pred, result); } Ok(result) @@ -1553,7 +1515,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// See the comment for "SelectionCandidate" for more details. fn candidate_should_be_dropped_in_favor_of( &mut self, - sized_predicate: bool, victim: &EvaluatedCandidate<'tcx>, other: &EvaluatedCandidate<'tcx>, needs_infer: bool, @@ -1625,16 +1586,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Drop otherwise equivalent non-const fn pointer candidates (FnPointerCandidate { .. }, FnPointerCandidate { is_const: false }) => true, - // If obligation is a sized predicate or the where-clause bound is - // global, prefer the projection or object candidate. See issue - // #50825 and #89352. - (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => { - sized_predicate || is_global(cand) - } - (ParamCandidate(ref cand), ObjectCandidate(_) | ProjectionCandidate(_)) => { - !(sized_predicate || is_global(cand)) - } - // Global bounds from the where clause should be ignored // here (see issue #50825). Otherwise, we have a where // clause so don't go around looking for impls. @@ -1650,8 +1601,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } - | TraitAliasCandidate(..), + | TraitAliasCandidate(..) + | ObjectCandidate(_) + | ProjectionCandidate(_), ) => !is_global(cand), + (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => { + // Prefer these to a global where-clause bound + // (see issue #50825). + is_global(cand) + } ( ImplCandidate(_) | ClosureCandidate @@ -1933,7 +1891,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// /// Here are some (simple) examples: /// - /// ``` + /// ```ignore (illustrative) /// (i32, u32) -> [i32, u32] /// Foo where struct Foo { x: i32, y: u32 } -> [i32, u32] /// Bar<i32> where struct Bar<T> { x: T, y: u32 } -> [i32, u32] @@ -2535,11 +2493,6 @@ struct ProvisionalEvaluation { from_dfn: usize, reached_depth: usize, result: EvaluationResult, - /// The `DepNodeIndex` created for the `evaluate_stack` call for this provisional - /// evaluation. When we create an entry in the evaluation cache using this provisional - /// cache entry (see `on_completion`), we use this `dep_node` to ensure that future reads from - /// the cache will have all of the necessary incr comp dependencies tracked. - dep_node: DepNodeIndex, } impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> { @@ -2582,7 +2535,6 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { reached_depth: usize, fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, result: EvaluationResult, - dep_node: DepNodeIndex, ) { debug!(?from_dfn, ?fresh_trait_pred, ?result, "insert_provisional"); @@ -2608,10 +2560,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { } } - map.insert( - fresh_trait_pred, - ProvisionalEvaluation { from_dfn, reached_depth, result, dep_node }, - ); + map.insert(fresh_trait_pred, ProvisionalEvaluation { from_dfn, reached_depth, result }); } /// Invoked when the node with dfn `dfn` does not get a successful @@ -2637,8 +2586,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { /// Invoked when the node at depth `depth` completed without /// depending on anything higher in the stack (if that completion /// was a failure, then `on_failure` should have been invoked - /// already). The callback `op` will be invoked for each - /// provisional entry that we can now confirm. + /// already). /// /// Note that we may still have provisional cache items remaining /// in the cache when this is done. For example, if there is a @@ -2659,19 +2607,26 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { /// would be 2, representing the C node, and hence we would /// remove the result for D, which has DFN 3, but not the results for /// A and B, which have DFNs 0 and 1 respectively). - fn on_completion( - &self, - dfn: usize, - mut op: impl FnMut(ty::PolyTraitPredicate<'tcx>, EvaluationResult, DepNodeIndex), - ) { + /// + /// Note that we *do not* attempt to cache these cycle participants + /// in the evaluation cache. Doing so would require carefully computing + /// the correct `DepNode` to store in the cache entry: + /// cycle participants may implicitly depend on query results + /// related to other participants in the cycle, due to our logic + /// which examines the evaluation stack. + /// + /// We used to try to perform this caching, + /// but it lead to multiple incremental compilation ICEs + /// (see #92987 and #96319), and was very hard to understand. + /// Fortunately, removing the caching didn't seem to + /// have a performance impact in practice. + fn on_completion(&self, dfn: usize) { debug!(?dfn, "on_completion"); for (fresh_trait_pred, eval) in self.map.borrow_mut().drain_filter(|_k, eval| eval.from_dfn >= dfn) { debug!(?fresh_trait_pred, ?eval, "on_completion"); - - op(fresh_trait_pred, eval.result, eval.dep_node); } } } diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 328e0d2e0e9..49c8c56083f 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -52,7 +52,7 @@ pub struct OverlapError { /// /// For example, consider the following scenario: /// -/// ```rust +/// ```ignore (illustrative) /// trait Foo { ... } /// impl<T, U> Foo for (T, U) { ... } // target impl /// impl<V> Foo for (V, V) { ... } // source impl @@ -64,7 +64,7 @@ pub struct OverlapError { /// where-clauses add some trickiness here, because they can be used to "define" /// an argument indirectly: /// -/// ```rust +/// ```ignore (illustrative) /// impl<'a, I, T: 'a> Iterator for Cloned<I> /// where I: Iterator<Item = &'a T>, T: Clone /// ``` diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index b4ed5b95b10..de0ade64247 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -170,7 +170,7 @@ struct WfPredicates<'a, 'tcx> { /// predicates. This is a kind of hack to address #43784. The /// underlying problem in that issue was a trait structure like: /// -/// ``` +/// ```ignore (illustrative) /// trait Foo: Copy { } /// trait Bar: Foo { } /// impl<T: Bar> Foo for T { } diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index b015e40b683..c63e9c31d53 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -122,16 +122,16 @@ rustc_index::newtype_index! { /// A [De Bruijn index][dbi] is a standard means of representing /// regions (and perhaps later types) in a higher-ranked setting. In /// particular, imagine a type like this: - /// - /// for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char) - /// ^ ^ | | | - /// | | | | | - /// | +------------+ 0 | | - /// | | | - /// +----------------------------------+ 1 | - /// | | - /// +----------------------------------------------+ 0 - /// + /// ```ignore (illustrative) + /// for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char) + /// // ^ ^ | | | + /// // | | | | | + /// // | +------------+ 0 | | + /// // | | | + /// // +----------------------------------+ 1 | + /// // | | + /// // +----------------------------------------------+ 0 + /// ``` /// In this type, there are two binders (the outer fn and the inner /// fn). We need to be able to determine, for any given region, which /// fn type it is bound by, the inner or the outer one. There are @@ -203,7 +203,7 @@ impl DebruijnIndex { /// it will now be bound at INNERMOST. This is an appropriate thing to do /// when moving a region out from inside binders: /// - /// ``` + /// ```ignore (illustrative) /// for<'a> fn(for<'b> for<'c> fn(&'a u32), _) /// // Binder: D3 D2 D1 ^^ /// ``` @@ -471,9 +471,9 @@ impl Variance { /// variance with which the argument appears. /// /// Example 1: - /// - /// *mut Vec<i32> - /// + /// ```ignore (illustrative) + /// *mut Vec<i32> + /// ``` /// Here, the "ambient" variance starts as covariant. `*mut T` is /// invariant with respect to `T`, so the variance in which the /// `Vec<i32>` appears is `Covariant.xform(Invariant)`, which @@ -483,9 +483,9 @@ impl Variance { /// (again) in `Invariant`. /// /// Example 2: - /// - /// fn(*const Vec<i32>, *mut Vec<i32) - /// + /// ```ignore (illustrative) + /// fn(*const Vec<i32>, *mut Vec<i32) + /// ``` /// The ambient variance is covariant. A `fn` type is /// contravariant with respect to its parameters, so the variance /// within which both pointer types appear is diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 794e711b6c8..38c29d3874c 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -3,7 +3,10 @@ use crate::astconv::{ AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, }; -use crate::errors::AssocTypeBindingNotAllowed; +use crate::errors::{ + AssocTypeBindingNotAllowed, ExplicitGenericArgsWithImplTrait, + ExplicitGenericArgsWithImplTraitFeature, +}; use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs}; use rustc_ast::ast::ParamKindOrd; use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan}; @@ -636,29 +639,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }) .collect::<Vec<_>>(); - let mut err = struct_span_err! { - tcx.sess, - spans.clone(), - E0632, - "cannot provide explicit generic arguments when `impl Trait` is \ - used in argument position" - }; - - for span in spans { - err.span_label(span, "explicit generic argument not allowed"); - } - - err.note( - "see issue #83701 <https://github.com/rust-lang/rust/issues/83701> \ - for more information", - ); + let mut err = tcx.sess.create_err(ExplicitGenericArgsWithImplTrait { spans }); if tcx.sess.is_nightly_build() { - err.help( - "add `#![feature(explicit_generic_args_with_impl_trait)]` \ - to the crate attributes to enable", - ); + err.subdiagnostic(ExplicitGenericArgsWithImplTraitFeature); } - err.emit(); } diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index fd1b7bfa0b1..93f6b4a6b5a 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -294,9 +294,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// /// Example: /// - /// ``` - /// T: std::ops::Index<usize, Output = u32> - /// ^1 ^^^^^^^^^^^^^^2 ^^^^3 ^^^^^^^^^^^4 + /// ```ignore (illustrative) + /// T: std::ops::Index<usize, Output = u32> + /// // ^1 ^^^^^^^^^^^^^^2 ^^^^3 ^^^^^^^^^^^4 /// ``` /// /// 1. The `self_ty` here would refer to the type `T`. @@ -310,7 +310,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// /// For (generic) associated types /// - /// ``` + /// ```ignore (illustrative) /// <Vec<u8> as Iterable<u8>>::Iter::<'a> /// ``` /// @@ -756,7 +756,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// /// Example: /// - /// ``` + /// ```ignore (illustrative) /// poly_trait_ref = Iterator<Item = u32> /// self_ty = Foo /// ``` @@ -1021,10 +1021,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// /// Example: /// - /// ``` + /// ```ignore (illustrative) /// fn foo<T: Bar + Baz>() { } - /// ^ ^^^^^^^^^ ast_bounds - /// param_ty + /// // ^ ^^^^^^^^^ ast_bounds + /// // param_ty /// ``` /// /// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 580fb7c3e0f..90b59df472c 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -59,7 +59,7 @@ pub fn check_legal_trait_for_method_call( enum CallStep<'tcx> { Builtin(Ty<'tcx>), - DeferredClosure(ty::FnSig<'tcx>), + DeferredClosure(DefId, ty::FnSig<'tcx>), /// E.g., enum variant constructors. Overloaded(MethodCallee<'tcx>), } @@ -107,8 +107,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.confirm_builtin_call(call_expr, callee_expr, callee_ty, arg_exprs, expected) } - Some(CallStep::DeferredClosure(fn_sig)) => { - self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, fn_sig) + Some(CallStep::DeferredClosure(def_id, fn_sig)) => { + self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, def_id, fn_sig) } Some(CallStep::Overloaded(method_callee)) => { @@ -171,7 +171,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_substs: substs, }, ); - return Some(CallStep::DeferredClosure(closure_sig)); + return Some(CallStep::DeferredClosure(def_id, closure_sig)); } } @@ -533,6 +533,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, + closure_def_id: DefId, fn_sig: ty::FnSig<'tcx>, ) -> Ty<'tcx> { // `fn_sig` is the *signature* of the closure being called. We @@ -555,7 +556,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs, fn_sig.c_variadic, TupleArgumentsFlag::TupleArguments, - None, + Some(closure_def_id), ); fn_sig.output() diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 6a4c0c2091e..4627b58c9bc 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -27,6 +27,7 @@ use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_ty_utils::representability::{self, Representability}; +use rustc_hir::def::DefKind; use std::iter; use std::ops::ControlFlow; @@ -711,28 +712,35 @@ fn check_opaque_meets_bounds<'tcx>( }); } -pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { +pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { debug!( "check_item_type(it.def_id={:?}, it.name={})", - it.def_id, - tcx.def_path_str(it.def_id.to_def_id()) + id.def_id, + tcx.def_path_str(id.def_id.to_def_id()) ); let _indenter = indenter(); - match it.kind { - // Consts can play a role in type-checking, so they are included here. - hir::ItemKind::Static(..) => { - tcx.ensure().typeck(it.def_id); - maybe_check_static_with_link_section(tcx, it.def_id, it.span); - check_static_inhabited(tcx, it.def_id, it.span); + match tcx.def_kind(id.def_id) { + DefKind::Static(..) => { + tcx.ensure().typeck(id.def_id); + maybe_check_static_with_link_section(tcx, id.def_id, tcx.def_span(id.def_id)); + check_static_inhabited(tcx, id.def_id, tcx.def_span(id.def_id)); } - hir::ItemKind::Const(..) => { - tcx.ensure().typeck(it.def_id); + DefKind::Const => { + tcx.ensure().typeck(id.def_id); } - hir::ItemKind::Enum(ref enum_definition, _) => { - check_enum(tcx, it.span, &enum_definition.variants, it.def_id); + DefKind::Enum => { + let item = tcx.hir().item(id); + let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else { + return; + }; + check_enum(tcx, item.span, &enum_definition.variants, item.def_id); } - hir::ItemKind::Fn(..) => {} // entirely within check_item_body - hir::ItemKind::Impl(ref impl_) => { + DefKind::Fn => {} // entirely within check_item_body + DefKind::Impl => { + let it = tcx.hir().item(id); + let hir::ItemKind::Impl(ref impl_) = it.kind else { + return; + }; debug!("ItemKind::Impl {} with id {:?}", it.ident, it.def_id); if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.def_id) { check_impl_items_against_trait( @@ -745,7 +753,11 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { check_on_unimplemented(tcx, it); } } - hir::ItemKind::Trait(_, _, _, _, ref items) => { + DefKind::Trait => { + let it = tcx.hir().item(id); + let hir::ItemKind::Trait(_, _, _, _, ref items) = it.kind else { + return; + }; check_on_unimplemented(tcx, it); for item in items.iter() { @@ -771,28 +783,36 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { } } } - hir::ItemKind::Struct(..) => { - check_struct(tcx, it.def_id, it.span); + DefKind::Struct => { + check_struct(tcx, id.def_id, tcx.def_span(id.def_id)); } - hir::ItemKind::Union(..) => { - check_union(tcx, it.def_id, it.span); + DefKind::Union => { + check_union(tcx, id.def_id, tcx.def_span(id.def_id)); } - hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { + DefKind::OpaqueTy => { + let item = tcx.hir().item(id); + let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else { + return; + }; // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting // `async-std` (and `pub async fn` in general). // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! // See https://github.com/rust-lang/rust/issues/75100 if !tcx.sess.opts.actually_rustdoc { - let substs = InternalSubsts::identity_for_item(tcx, it.def_id.to_def_id()); - check_opaque(tcx, it.def_id, substs, it.span, &origin); + let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id()); + check_opaque(tcx, item.def_id, substs, item.span, &origin); } } - hir::ItemKind::TyAlias(..) => { - let pty_ty = tcx.type_of(it.def_id); - let generics = tcx.generics_of(it.def_id); + DefKind::TyAlias => { + let pty_ty = tcx.type_of(id.def_id); + let generics = tcx.generics_of(id.def_id); check_type_params_are_used(tcx, &generics, pty_ty); } - hir::ItemKind::ForeignMod { abi, items } => { + DefKind::ForeignMod => { + let it = tcx.hir().item(id); + let hir::ItemKind::ForeignMod { abi, items } = it.kind else { + return; + }; check_abi(tcx, it.hir_id(), it.span, abi); if abi == Abi::RustIntrinsic { @@ -851,7 +871,7 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { } } } - _ => { /* nothing to do */ } + _ => {} } } @@ -1451,7 +1471,10 @@ pub(super) fn check_type_params_are_used<'tcx>( } pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckItemTypesVisitor { tcx }); + let module = tcx.hir_module_items(module_def_id); + for id in module.items() { + check_item_type(tcx, id); + } } pub(super) use wfcheck::check_item_well_formed; diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 320be9b44d4..50e55bdc4af 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -371,7 +371,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// # Examples /// - /// ``` + /// ```ignore (illustrative) /// fn with_closure<F>(_: F) /// where F: Fn(&u32) -> &u32 { .. } /// diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index b44baf83cbe..c563d57ed96 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -21,7 +21,7 @@ //! When inferring the generic arguments of functions, the argument //! order is relevant, which can lead to the following edge case: //! -//! ```rust +//! ```ignore (illustrative) //! fn foo<T>(a: T, b: T) { //! // ... //! } @@ -1210,7 +1210,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// Example: /// -/// ``` +/// ```ignore (illustrative) /// let mut coerce = CoerceMany::new(expected_ty); /// for expr in exprs { /// let expr_ty = fcx.check_expr_with_expectation(expr, expected); diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index f377cf3678e..bfd2c32fd7e 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -439,7 +439,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Identify some cases where `as_ref()` would be appropriate and suggest it. /// /// Given the following code: - /// ``` + /// ```compile_fail,E0308 /// struct Foo; /// fn takes_ref(_: &Foo) {} /// let ref opt = Some(Foo); @@ -449,7 +449,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Suggest using `opt.as_ref().map(|param| takes_ref(param));` instead. /// /// It only checks for `Option` and `Result` and won't work with - /// ``` + /// ```ignore (illustrative) /// opt.map(|param| { takes_ref(param) }); /// ``` fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Span, &'static str, String)> { @@ -566,7 +566,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// This function is used to determine potential "simple" improvements or users' errors and /// provide them useful help. For example: /// - /// ``` + /// ```compile_fail,E0308 /// fn some_fn(s: &str) {} /// /// let x = "hey!".to_owned(); diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_typeck/src/check/fallback.rs index 85132317824..15788f410f1 100644 --- a/compiler/rustc_typeck/src/check/fallback.rs +++ b/compiler/rustc_typeck/src/check/fallback.rs @@ -144,6 +144,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// code. The most common case is something like this: /// /// ```rust + /// # fn foo() -> i32 { 4 } /// match foo() { /// 22 => Default::default(), // call this type `?D` /// _ => return, // return has type `!` @@ -168,7 +169,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// fallback to use based on whether there is a coercion pattern /// like this: /// - /// ``` + /// ```ignore (not-rust) /// ?Diverging -> ?V /// ?NonDiverging -> ?V /// ?V != ?NonDiverging diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 616aa11f00a..277743e4a46 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -24,7 +24,7 @@ use rustc_infer::infer::TypeTrace; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::{self, Span}; @@ -394,6 +394,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { break 'errors; } + self.set_tainted_by_errors(); + // The algorithm here is inspired by levenshtein distance and longest common subsequence. // We'll try to detect 4 different types of mistakes: // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs @@ -429,9 +431,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors.drain_filter(|error| { let Error::Invalid(input_idx, Compatibility::Incompatible(error)) = error else { return false }; let expected_ty = expected_input_tys[*input_idx]; - let provided_ty = final_arg_types[*input_idx].map(|ty| ty.0).unwrap(); + let Some(Some((provided_ty, _))) = final_arg_types.get(*input_idx) else { return false }; let cause = &self.misc(provided_args[*input_idx].span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = TypeTrace::types(cause, true, expected_ty, *provided_ty); if let Some(e) = error { if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) { self.report_and_explain_type_error(trace, e).emit(); @@ -502,6 +504,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { TupleMatchFound::Single => { let expected_ty = expected_input_tys[0]; let provided_ty = final_arg_types[0].map(|ty| ty.0).unwrap(); + let expected_ty = self.resolve_vars_if_possible(expected_ty); + let provided_ty = self.resolve_vars_if_possible(provided_ty); let cause = &self.misc(provided_args[0].span); let compatibility = demand_compatible(0, &mut final_arg_types); let type_error = match compatibility { @@ -523,24 +527,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("arguments to this {} are incorrect", call_name), ); // Call out where the function is defined - if let Some(def_id) = fn_def_id && let Some(def_span) = tcx.def_ident_span(def_id) { - let mut spans: MultiSpan = def_span.into(); - - let params = tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .map(|id| tcx.hir().body(id).params) - .flatten(); - - for param in params { - spans.push_span_label(param.span, String::new()); - } - - let def_kind = tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } + label_fn_like(tcx, &mut err, fn_def_id); err.emit(); break 'errors; } @@ -558,24 +545,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { DiagnosticId::Error(err_code.to_owned()), ); // Call out where the function is defined - if let Some(def_id) = fn_def_id && let Some(def_span) = tcx.def_ident_span(def_id) { - let mut spans: MultiSpan = def_span.into(); - - let params = tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .map(|id| tcx.hir().body(id).params) - .flatten(); - - for param in params { - spans.push_span_label(param.span, String::new()); - } - - let def_kind = tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } + label_fn_like(tcx, &mut err, fn_def_id); err.multipart_suggestion( "use parentheses to construct a tuple", vec![(start, '('.to_string()), (end, ')'.to_string())], @@ -597,13 +567,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let expected_ty = expected_input_tys[*input_idx]; let provided_ty = final_arg_types[*input_idx].map(|ty| ty.0).unwrap(); + let expected_ty = self.resolve_vars_if_possible(expected_ty); + let provided_ty = self.resolve_vars_if_possible(provided_ty); let cause = &self.misc(provided_args[*input_idx].span); let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); let mut err = self.report_and_explain_type_error(trace, error); self.emit_coerce_suggestions( &mut err, &provided_args[*input_idx], - final_arg_types[*input_idx].map(|ty| ty.0).unwrap(), + provided_ty, final_arg_types[*input_idx].map(|ty| ty.1).unwrap(), None, None, @@ -613,24 +585,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("arguments to this {} are incorrect", call_name), ); // Call out where the function is defined - if let Some(def_id) = fn_def_id && let Some(def_span) = tcx.def_ident_span(def_id) { - let mut spans: MultiSpan = def_span.into(); - - let params = tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .map(|id| tcx.hir().body(id).params) - .flatten(); - - for param in params { - spans.push_span_label(param.span, String::new()); - } - - let def_kind = tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } + label_fn_like(tcx, &mut err, fn_def_id); err.emit(); break 'errors; } @@ -678,9 +633,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match error { Error::Invalid(input_idx, compatibility) => { let expected_ty = expected_input_tys[input_idx]; + let provided_ty = final_arg_types + .get(input_idx) + .and_then(|x| x.as_ref()) + .map(|ty| ty.0) + .unwrap_or(tcx.ty_error()); + let expected_ty = self.resolve_vars_if_possible(expected_ty); + let provided_ty = self.resolve_vars_if_possible(provided_ty); if let Compatibility::Incompatible(error) = &compatibility { - let provided_ty = final_arg_types[input_idx].map(|ty| ty.0).unwrap(); - let cause = &self.misc(provided_args[input_idx].span); + let cause = &self.misc( + provided_args.get(input_idx).map(|i| i.span).unwrap_or(call_span), + ); let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); if let Some(e) = error { self.note_type_err( @@ -695,14 +658,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.emit_coerce_suggestions( - &mut err, - &provided_args[input_idx], - final_arg_types[input_idx].map(|ty| ty.0).unwrap(), - final_arg_types[input_idx].map(|ty| ty.1).unwrap(), - None, - None, - ); + if let Some(expr) = provided_args.get(input_idx) { + self.emit_coerce_suggestions( + &mut err, + &expr, + final_arg_types[input_idx].map(|ty| ty.0).unwrap(), + final_arg_types[input_idx].map(|ty| ty.1).unwrap(), + None, + None, + ); + } } Error::Extra(arg_idx) => { let arg_type = if let Some((_, ty)) = final_arg_types[arg_idx] { @@ -940,24 +905,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Call out where the function is defined - if let Some(def_id) = fn_def_id && let Some(def_span) = tcx.def_ident_span(def_id) { - let mut spans: MultiSpan = def_span.into(); - - let params = tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .flat_map(|id| tcx.hir().body(id).params) - ; - - for param in params { - spans.push_span_label(param.span, String::new()); - } - - let def_kind = tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } + label_fn_like(tcx, &mut err, fn_def_id); // And add a suggestion block for all of the parameters let suggestion_text = match suggestion_text { @@ -980,7 +928,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); for (idx, arg) in matched_inputs.iter().enumerate() { let suggestion_text = if let Some(arg) = arg { - let arg_span = provided_args[*arg].span; + let arg_span = provided_args[*arg].span.source_callsite(); let arg_text = source_map.span_to_snippet(arg_span).unwrap(); arg_text } else { @@ -1467,7 +1415,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// A common error is to add an extra semicolon: /// - /// ``` + /// ```compile_fail,E0308 /// fn foo() -> usize { /// 22; /// } @@ -1782,3 +1730,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + +fn label_fn_like<'tcx>( + tcx: TyCtxt<'tcx>, + err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>, + def_id: Option<DefId>, +) { + let Some(def_id) = def_id else { + return; + }; + + if let Some(def_span) = tcx.def_ident_span(def_id) { + let mut spans: MultiSpan = def_span.into(); + + let params = tcx + .hir() + .get_if_local(def_id) + .and_then(|node| node.body_id()) + .into_iter() + .map(|id| tcx.hir().body(id).params) + .flatten(); + + for param in params { + spans.push_span_label(param.span, String::new()); + } + + let def_kind = tcx.def_kind(def_id); + err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); + } else { + match tcx.hir().get_if_local(def_id) { + Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(_, _, _, span, ..), + .. + })) => { + let spans: MultiSpan = (*span).into(); + + // Note: We don't point to param spans here because they overlap + // with the closure span itself + + err.span_note(spans, "closure defined here"); + } + _ => {} + } + } +} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 681d1e37f86..bd58a675448 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -72,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// When encountering an fn-like ctor that needs to unify with a value, check whether calling /// the ctor would successfully solve the type mismatch and if so, suggest it: - /// ``` + /// ```compile_fail,E0308 /// fn foo(x: usize) -> usize { x } /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` /// ``` @@ -151,7 +151,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => { sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", "); - match def_id.as_local().map(|def_id| hir.def_kind(def_id)) { + match def_id.as_local().map(|def_id| self.tcx.def_kind(def_id)) { Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => { msg = "instantiate this tuple variant"; } @@ -463,7 +463,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// A common error is to forget to add a semicolon at the end of a block, e.g., /// - /// ``` + /// ```compile_fail,E0308 + /// # fn bar_that_returns_u32() -> u32 { 4 } /// fn foo() { /// bar_that_returns_u32() /// } @@ -504,7 +505,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// A possible error is to forget to add a return type that is needed: /// - /// ``` + /// ```compile_fail,E0308 + /// # fn bar_that_returns_u32() -> u32 { 4 } /// fn foo() { /// bar_that_returns_u32() /// } @@ -569,7 +571,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// check whether the return type is a generic type with a trait bound /// only suggest this if the generic param is not present in the arguments /// if this is true, hint them towards changing the return type to `impl Trait` - /// ``` + /// ```compile_fail,E0308 /// fn cant_name_it<T: Fn() -> u32>() -> T { /// || 3 /// } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs index 721f251650f..df8db0da644 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs @@ -71,7 +71,7 @@ pub(super) fn build_control_flow_graph<'tcx>( /// ``` /// /// Rule 3: -/// ```rust +/// ```compile_fail,E0382 /// let mut a = (vec![0], vec![0]); /// drop(a); /// a.1 = vec![1]; diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs index 62ca728868b..5cd63cae8ad 100644 --- a/compiler/rustc_typeck/src/check/inherited.rs +++ b/compiler/rustc_typeck/src/check/inherited.rs @@ -17,11 +17,11 @@ use std::cell::RefCell; use std::ops::Deref; /// Closures defined within the function. For example: -/// -/// fn foo() { -/// bar(move|| { ... }) -/// } -/// +/// ```ignore (illustrative) +/// fn foo() { +/// bar(move|| { ... }) +/// } +/// ``` /// Here, the function `foo()` and the closure passed to /// `bar()` will each have their own `FnCtxt`, but they will /// share the inherited fields. diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index e04cc42b6d7..c7d0f61c601 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -27,6 +27,7 @@ use rustc_span::def_id::LocalDefId; use rustc_span::lev_distance::{ find_best_match_for_name_with_substrings, lev_distance_with_substrings, }; +use rustc_span::symbol::sym; use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; use rustc_trait_selection::autoderef::{self, Autoderef}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -200,8 +201,9 @@ pub struct Pick<'tcx> { pub import_ids: SmallVec<[LocalDefId; 1]>, /// Indicates that the source expression should be autoderef'd N times - /// - /// A = expr | *expr | **expr | ... + /// ```ignore (not-rust) + /// A = expr | *expr | **expr | ... + /// ``` pub autoderefs: usize, /// Indicates that we want to add an autoref (and maybe also unsize it), or if the receiver is @@ -642,16 +644,22 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.assemble_inherent_candidates_from_object(generalized_self_ty); self.assemble_inherent_impl_candidates_for_type(p.def_id()); + if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) { + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + } } ty::Adt(def, _) => { let def_id = def.did(); self.assemble_inherent_impl_candidates_for_type(def_id); - if Some(def_id) == self.tcx.lang_items().c_str() { + if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) { self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); } } ty::Foreign(did) => { self.assemble_inherent_impl_candidates_for_type(did); + if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + } } ty::Param(p) => { self.assemble_inherent_candidates_from_param(p); @@ -1631,7 +1639,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// /// Example (`src/test/ui/method-two-trait-defer-resolution-1.rs`): /// - /// ``` + /// ```ignore (illustrative) /// trait Foo { ... } /// impl Foo for Vec<i32> { ... } /// impl Foo for Vec<usize> { ... } diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 634ba2baf96..294a42a1148 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -368,16 +368,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.is_fn_ty(rcvr_ty, span) { if let SelfSource::MethodCall(expr) = source { let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() { - let local_id = def_id.expect_local(); - let hir_id = tcx.hir().local_def_id_to_hir_id(local_id); - let node = tcx.hir().get(hir_id); - let fields = node.tuple_fields(); - - if let Some(fields) = fields - && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) { - Some((fields, of)) + if let Some(local_id) = def_id.as_local() { + let hir_id = tcx.hir().local_def_id_to_hir_id(local_id); + let node = tcx.hir().get(hir_id); + let fields = node.tuple_fields(); + if let Some(fields) = fields + && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) { + Some((fields.len(), of)) + } else { + None + } } else { - None + // The logic here isn't smart but `associated_item_def_ids` + // doesn't work nicely on local. + if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) { + let parent_def_id = tcx.parent(*def_id); + Some((tcx.associated_item_def_ids(parent_def_id).len(), of)) + } else { + None + } } } else { None @@ -385,7 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the function is a tuple constructor, we recommend that they call it if let Some((fields, kind)) = suggest { - suggest_call_constructor(expr.span, kind, fields.len(), &mut err); + suggest_call_constructor(expr.span, kind, fields, &mut err); } else { // General case err.span_label( diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 76c955d6f69..cc62144e2b1 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -111,7 +111,6 @@ use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{HirIdMap, ImplicitSelfKind, Node}; use rustc_index::bit_set::BitSet; use rustc_index::vec::Idx; @@ -891,16 +890,19 @@ fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span) { /// Tupling means that all call-side arguments are packed into a tuple and /// passed as a single parameter. For example, if tupling is enabled, this /// function: -/// -/// fn f(x: (isize, isize)) -/// +/// ``` +/// fn f(x: (isize, isize)) {} +/// ``` /// Can be called as: -/// -/// f(1, 2); -/// +/// ```ignore UNSOLVED (can this be done in user code?) +/// # fn f(x: (isize, isize)) {} +/// f(1, 2); +/// ``` /// Instead of: -/// -/// f((1, 2)); +/// ``` +/// # fn f(x: (isize, isize)) {} +/// f((1, 2)); +/// ``` #[derive(Clone, Eq, PartialEq)] enum TupleArgumentsFlag { DontTupleArguments, @@ -933,19 +935,6 @@ impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> { } } -struct CheckItemTypesVisitor<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> ItemLikeVisitor<'tcx> for CheckItemTypesVisitor<'tcx> { - fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) { - check_item_type(self.tcx, i); - } - fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {} - fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} - fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {} -} - fn typeck_item_bodies(tcx: TyCtxt<'_>, (): ()) { tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id)); } diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 356763fab5e..5eba95b495d 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -1894,7 +1894,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// Syntactically, these look like `[pat_0, ..., pat_n]`. /// Semantically, we are type checking a pattern with structure: - /// ``` + /// ```ignore (not-rust) /// [before_0, ..., before_n, (slice, after_0, ... after_n)?] /// ``` /// The type of `slice`, if it is present, depends on the `expected` type. diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index e37e83e7487..20456cc427c 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -269,7 +269,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { /// /// Consider this silly example: /// - /// ``` + /// ```ignore UNSOLVED (does replacing @i32 with Box<i32> preserve the desired semantics for the example?) /// fn borrow(x: &i32) -> &i32 {x} /// fn foo(x: @i32) -> i32 { // block: B /// let b = borrow(x); // region: <R0> diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 9dbb8132932..fee2efd5804 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -5,9 +5,9 @@ //! immutable "borrow kind" (see `ty::BorrowKind` for details) and then //! "escalating" the kind as needed. The borrow kind proceeds according to //! the following lattice: -//! -//! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow -//! +//! ```ignore (not-rust) +//! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow +//! ``` //! So, for example, if we see an assignment `x = 5` to an upvar `x`, we //! will promote its borrow kind to mutable borrow. If we see an `&mut x` //! we'll do the same. Naturally, this applies not just to the upvar, but @@ -447,16 +447,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// the existing min_capture map that is stored in TypeckResults. /// /// Eg: - /// ```rust,no_run + /// ``` + /// #[derive(Debug)] /// struct Point { x: i32, y: i32 } /// - /// let s: String; // hir_id_s - /// let mut p: Point; // his_id_p + /// let s = String::from("s"); // hir_id_s + /// let mut p = Point { x: 2, y: -2 }; // his_id_p /// let c = || { - /// println!("{s}"); // L1 + /// println!("{s:?}"); // L1 /// p.x += 10; // L2 /// println!("{}" , p.y); // L3 - /// println!("{p}"); // L4 + /// println!("{p:?}"); // L4 /// drop(s); // L5 /// }; /// ``` @@ -465,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// InferBorrowKind results in a structure like this: /// - /// ```text + /// ```ignore (illustrative) /// { /// Place(base: hir_id_s, projections: [], ....) -> { /// capture_kind_expr: hir_id_L5, @@ -487,10 +488,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// path_expr_id: hir_id_L4, /// capture_kind: ByValue /// }, + /// } /// ``` /// /// After the min capture analysis, we get: - /// ```text + /// ```ignore (illustrative) /// { /// hir_id_s -> [ /// Place(base: hir_id_s, projections: [], ....) -> { @@ -506,6 +508,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// capture_kind: ByValue /// }, /// ], + /// } /// ``` fn compute_min_captures( &self, @@ -914,6 +917,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { reasons.auto_traits.extend(auto_trait_reasons); reasons.drop_order = drop_order; + // `auto_trait_reasons` are in hashset order, so sort them to put the + // diagnostics we emit later in a cross-platform-consistent order. + reasons.auto_traits.sort_unstable(); + reasons } @@ -1281,27 +1288,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// - Ty(place): Type of place /// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_by_move_projs` /// respectively. - /// ``` + /// ```ignore (illustrative) /// (Ty(w), [ &[p, x], &[c] ]) - /// | - /// ---------------------------- - /// | | - /// v v + /// // | + /// // ---------------------------- + /// // | | + /// // v v /// (Ty(w.p), [ &[x] ]) (Ty(w.c), [ &[] ]) // I(1) - /// | | - /// v v + /// // | | + /// // v v /// (Ty(w.p), [ &[x] ]) false - /// | - /// | - /// ------------------------------- - /// | | - /// v v + /// // | + /// // | + /// // ------------------------------- + /// // | | + /// // v v /// (Ty((w.p).x), [ &[] ]) (Ty((w.p).y), []) // IMP 2 - /// | | - /// v v + /// // | | + /// // v v /// false NeedsSignificantDrop(Ty(w.p.y)) - /// | - /// v + /// // | + /// // v /// true /// ``` /// @@ -1319,7 +1326,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// Consider another example: /// - /// ```rust + /// ```ignore (pseudo-rust) /// struct X; /// impl Drop for X {} /// @@ -1724,14 +1731,14 @@ struct InferBorrowKind<'a, 'tcx> { /// s.str2 via a MutableBorrow /// /// ```rust,no_run - /// struct SomeStruct { str1: String, str2: String } + /// struct SomeStruct { str1: String, str2: String }; /// /// // Assume that the HirId for the variable definition is `V1` - /// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") } + /// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") }; /// /// let fix_s = |new_s2| { /// // Assume that the HirId for the expression `s.str1` is `E1` - /// println!("Updating SomeStruct with str1=", s.str1); + /// println!("Updating SomeStruct with str1={0}", s.str1); /// // Assume that the HirId for the expression `*s.str2` is `E2` /// s.str2 = new_s2; /// }; @@ -1739,7 +1746,7 @@ struct InferBorrowKind<'a, 'tcx> { /// /// For closure `fix_s`, (at a high level) the map contains /// - /// ``` + /// ```ignore (illustrative) /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow } /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } /// ``` @@ -2067,7 +2074,7 @@ fn migration_suggestion_for_2229( /// Consider the following example /// ```rust,no_run /// struct Point { x: i32, y: i32 } -/// let mut p: Point { x: 10, y: 10 }; +/// let mut p = Point { x: 10, y: 10 }; /// /// let c = || { /// p.x += 10; @@ -2213,7 +2220,10 @@ fn determine_place_ancestry_relation<'tcx>( /// /// Reason we only drop the last deref is because of the following edge case: /// -/// ```rust +/// ``` +/// # struct A { field_of_a: Box<i32> } +/// # struct B {} +/// # struct C<'a>(&'a i32); /// struct MyStruct<'a> { /// a: &'static A, /// b: B, @@ -2221,7 +2231,7 @@ fn determine_place_ancestry_relation<'tcx>( /// } /// /// fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { -/// let c = || drop(&*m.a.field_of_a); +/// || drop(&*m.a.field_of_a) /// // Here we really do want to capture `*m.a` because that outlives `'static` /// /// // If we capture `m`, then the closure no longer outlives `'static' diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index ec2b7c13ff3..829485367e0 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -37,7 +37,7 @@ use std::ops::ControlFlow; /// Helper type of a temporary returned by `.for_item(...)`. /// This is necessary because we can't write the following bound: /// -/// ```rust +/// ```ignore (illustrative) /// F: for<'b, 'tcx> where 'tcx FnOnce(FnCtxt<'b, 'tcx>) /// ``` struct CheckWfFcxBuilder<'tcx> { diff --git a/compiler/rustc_typeck/src/check_unused.rs b/compiler/rustc_typeck/src/check_unused.rs index d52886a09bd..1310467aeb9 100644 --- a/compiler/rustc_typeck/src/check_unused.rs +++ b/compiler/rustc_typeck/src/check_unused.rs @@ -17,7 +17,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } for id in tcx.hir().items() { - if matches!(tcx.hir().def_kind(id.def_id), DefKind::Use) { + if matches!(tcx.def_kind(id.def_id), DefKind::Use) { if tcx.visibility(id.def_id).is_public() { continue; } @@ -101,7 +101,7 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { let mut crates_to_lint = vec![]; for id in tcx.hir().items() { - if matches!(tcx.hir().def_kind(id.def_id), DefKind::ExternCrate) { + if matches!(tcx.def_kind(id.def_id), DefKind::ExternCrate) { let item = tcx.hir().item(id); if let hir::ItemKind::ExternCrate(orig_name) = item.kind { crates_to_lint.push(ExternCrateToLint { diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_typeck/src/coherence/inherent_impls.rs index 5ad0c4ac52d..b9d4167dbff 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls.rs @@ -9,8 +9,8 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::fast_reject::{simplify_type, SimplifiedType, TreatParams}; use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -19,7 +19,9 @@ use rustc_span::Span; /// On-demand query: yields a map containing all types mapped to their inherent impls. pub fn crate_inherent_impls(tcx: TyCtxt<'_>, (): ()) -> CrateInherentImpls { let mut collect = InherentCollect { tcx, impls_map: Default::default() }; - tcx.hir().visit_all_item_likes(&mut collect); + for id in tcx.hir().items() { + collect.check_item(id); + } collect.impls_map } @@ -46,92 +48,67 @@ struct InherentCollect<'tcx> { impls_map: CrateInherentImpls, } -impl<'tcx> ItemLikeVisitor<'_> for InherentCollect<'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - let hir::ItemKind::Impl(hir::Impl { of_trait: None, self_ty: ty, ref items, .. }) = item.kind else { +const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible"; +const INTO_DEFINING_CRATE: &str = + "consider moving this inherent impl into the crate defining the type if possible"; +const ADD_ATTR_TO_TY: &str = "alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type \ + and `#[rustc_allow_incoherent_impl]` to the relevant impl items"; +const ADD_ATTR: &str = + "alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items"; + +impl<'tcx> InherentCollect<'tcx> { + fn check_def_id(&mut self, item: &hir::Item<'_>, self_ty: Ty<'tcx>, def_id: DefId) { + let impl_def_id = item.def_id; + if let Some(def_id) = def_id.as_local() { + // Add the implementation to the mapping from implementation to base + // type def ID, if there is a base type for this implementation and + // the implementation does not have any associated traits. + let vec = self.impls_map.inherent_impls.entry(def_id).or_default(); + vec.push(impl_def_id.to_def_id()); return; - }; + } - let self_ty = self.tcx.type_of(item.def_id); - match *self_ty.kind() { - ty::Adt(def, _) => { - let def_id = def.did(); - if !def_id.is_local() && Some(def_id) == self.tcx.lang_items().c_str() { - self.check_primitive_impl(item.def_id, self_ty, items, ty.span) - } else { - self.check_def_id(item, def_id); - } - } - ty::Foreign(did) => { - self.check_def_id(item, did); - } - ty::Dynamic(data, ..) if data.principal_def_id().is_some() => { - self.check_def_id(item, data.principal_def_id().unwrap()); - } - ty::Dynamic(..) => { + if self.tcx.features().rustc_attrs { + let hir::ItemKind::Impl(&hir::Impl { items, .. }) = item.kind else { + bug!("expected `impl` item: {:?}", item); + }; + + if !self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) { struct_span_err!( self.tcx.sess, - ty.span, - E0785, - "cannot define inherent `impl` for a dyn auto trait" + item.span, + E0390, + "cannot define inherent `impl` for a type outside of the crate where the type is defined", ) - .span_label(ty.span, "impl requires at least one non-auto trait") - .note("define and implement a new trait or type instead") + .help(INTO_DEFINING_CRATE) + .span_help(item.span, ADD_ATTR_TO_TY) .emit(); + return; } - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Array(..) - | ty::Slice(_) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Never - | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span), - ty::FnPtr(_) | ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => { - let mut err = struct_span_err!( - self.tcx.sess, - ty.span, - E0118, - "no nominal type found for inherent implementation" - ); - err.span_label(ty.span, "impl requires a nominal type") - .note("either implement a trait on it or create a newtype to wrap it instead"); - - err.emit(); - } - ty::FnDef(..) - | ty::Closure(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::Bound(..) - | ty::Placeholder(_) - | ty::Infer(_) => { - bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty); + for impl_item in items { + if !self + .tcx + .has_attr(impl_item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl) + { + struct_span_err!( + self.tcx.sess, + item.span, + E0390, + "cannot define inherent `impl` for a type outside of the crate where the type is defined", + ) + .help(INTO_DEFINING_CRATE) + .span_help(impl_item.span, ADD_ATTR) + .emit(); + return; + } } - ty::Error(_) => {} - } - } - - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} - - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} - fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {} -} - -impl<'tcx> InherentCollect<'tcx> { - fn check_def_id(&mut self, item: &hir::Item<'_>, def_id: DefId) { - if let Some(def_id) = def_id.as_local() { - // Add the implementation to the mapping from implementation to base - // type def ID, if there is a base type for this implementation and - // the implementation does not have any associated traits. - let vec = self.impls_map.inherent_impls.entry(def_id).or_default(); - vec.push(item.def_id.to_def_id()); + if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsPlaceholders) { + self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id); + } else { + bug!("unexpected self type: {:?}", self_ty); + } } else { struct_span_err!( self.tcx.sess, @@ -153,9 +130,6 @@ impl<'tcx> InherentCollect<'tcx> { items: &[hir::ImplItemRef], span: Span, ) { - const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible"; - const ADD_ATTR: &str = - "alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items"; if !self.tcx.hir().rustc_coherence_is_core() { if self.tcx.features().rustc_attrs { for item in items { @@ -201,4 +175,74 @@ impl<'tcx> InherentCollect<'tcx> { bug!("unexpected primitive type: {:?}", ty); } } + + fn check_item(&mut self, id: hir::ItemId) { + if !matches!(self.tcx.def_kind(id.def_id), DefKind::Impl) { + return; + } + + let item = self.tcx.hir().item(id); + let hir::ItemKind::Impl(hir::Impl { of_trait: None, self_ty: ty, ref items, .. }) = item.kind else { + return; + }; + + let self_ty = self.tcx.type_of(item.def_id); + match *self_ty.kind() { + ty::Adt(def, _) => { + self.check_def_id(item, self_ty, def.did()); + } + ty::Foreign(did) => { + self.check_def_id(item, self_ty, did); + } + ty::Dynamic(data, ..) if data.principal_def_id().is_some() => { + self.check_def_id(item, self_ty, data.principal_def_id().unwrap()); + } + ty::Dynamic(..) => { + struct_span_err!( + self.tcx.sess, + ty.span, + E0785, + "cannot define inherent `impl` for a dyn auto trait" + ) + .span_label(ty.span, "impl requires at least one non-auto trait") + .note("define and implement a new trait or type instead") + .emit(); + } + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::Never + | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span), + ty::FnPtr(_) | ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => { + let mut err = struct_span_err!( + self.tcx.sess, + ty.span, + E0118, + "no nominal type found for inherent implementation" + ); + + err.span_label(ty.span, "impl requires a nominal type") + .note("either implement a trait on it or create a newtype to wrap it instead"); + + err.emit(); + } + ty::FnDef(..) + | ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Bound(..) + | ty::Placeholder(_) + | ty::Infer(_) => { + bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty); + } + ty::Error(_) => {} + } + } } diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs index cf71e0f300c..db67c1f7c9e 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::struct_span_err; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_index::vec::IndexVec; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::{self, TyCtxt}; @@ -12,7 +12,10 @@ use smallvec::SmallVec; use std::collections::hash_map::Entry; pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, (): ()) { - tcx.hir().visit_all_item_likes(&mut InherentOverlapChecker { tcx }); + let mut inherent_overlap_checker = InherentOverlapChecker { tcx }; + for id in tcx.hir().items() { + inherent_overlap_checker.check_item(id); + } } struct InherentOverlapChecker<'tcx> { @@ -121,200 +124,184 @@ impl<'tcx> InherentOverlapChecker<'tcx> { || true, ); } -} -impl<'tcx> ItemLikeVisitor<'_> for InherentOverlapChecker<'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - match item.kind { - hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Trait(..) - | hir::ItemKind::Union(..) => { - let impls = self.tcx.inherent_impls(item.def_id); + fn check_item(&mut self, id: hir::ItemId) { + let def_kind = self.tcx.def_kind(id.def_id); + if !matches!(def_kind, DefKind::Enum | DefKind::Struct | DefKind::Trait | DefKind::Union) { + return; + } - // If there is only one inherent impl block, - // there is nothing to overlap check it with - if impls.len() <= 1 { - return; - } + let impls = self.tcx.inherent_impls(id.def_id); - let overlap_mode = OverlapMode::get(self.tcx, item.def_id.to_def_id()); + // If there is only one inherent impl block, + // there is nothing to overlap check it with + if impls.len() <= 1 { + return; + } - let impls_items = impls - .iter() - .map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id))) - .collect::<SmallVec<[_; 8]>>(); + let overlap_mode = OverlapMode::get(self.tcx, id.def_id.to_def_id()); - // Perform a O(n^2) algorithm for small n, - // otherwise switch to an allocating algorithm with - // faster asymptotic runtime. - const ALLOCATING_ALGO_THRESHOLD: usize = 500; - if impls.len() < ALLOCATING_ALGO_THRESHOLD { - for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() { - for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] { - if self.impls_have_common_items(impl_items1, impl_items2) { - self.check_for_overlapping_inherent_impls( - overlap_mode, - impl1_def_id, - impl2_def_id, - ); - } - } + let impls_items = impls + .iter() + .map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id))) + .collect::<SmallVec<[_; 8]>>(); + + // Perform a O(n^2) algorithm for small n, + // otherwise switch to an allocating algorithm with + // faster asymptotic runtime. + const ALLOCATING_ALGO_THRESHOLD: usize = 500; + if impls.len() < ALLOCATING_ALGO_THRESHOLD { + for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() { + for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] { + if self.impls_have_common_items(impl_items1, impl_items2) { + self.check_for_overlapping_inherent_impls( + overlap_mode, + impl1_def_id, + impl2_def_id, + ); } - } else { - // Build a set of connected regions of impl blocks. - // Two impl blocks are regarded as connected if they share - // an item with the same unhygienic identifier. - // After we have assembled the connected regions, - // run the O(n^2) algorithm on each connected region. - // This is advantageous to running the algorithm over the - // entire graph when there are many connected regions. + } + } + } else { + // Build a set of connected regions of impl blocks. + // Two impl blocks are regarded as connected if they share + // an item with the same unhygienic identifier. + // After we have assembled the connected regions, + // run the O(n^2) algorithm on each connected region. + // This is advantageous to running the algorithm over the + // entire graph when there are many connected regions. - rustc_index::newtype_index! { - pub struct RegionId { - ENCODABLE = custom + rustc_index::newtype_index! { + pub struct RegionId { + ENCODABLE = custom + } + } + struct ConnectedRegion { + idents: SmallVec<[Symbol; 8]>, + impl_blocks: FxHashSet<usize>, + } + let mut connected_regions: IndexVec<RegionId, _> = Default::default(); + // Reverse map from the Symbol to the connected region id. + let mut connected_region_ids = FxHashMap::default(); + + for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() { + if impl_items.len() == 0 { + continue; + } + // First obtain a list of existing connected region ids + let mut idents_to_add = SmallVec::<[Symbol; 8]>::new(); + let mut ids = impl_items + .in_definition_order() + .filter_map(|item| { + let entry = connected_region_ids.entry(item.name); + if let Entry::Occupied(e) = &entry { + Some(*e.get()) + } else { + idents_to_add.push(item.name); + None } + }) + .collect::<SmallVec<[RegionId; 8]>>(); + // Sort the id list so that the algorithm is deterministic + ids.sort_unstable(); + ids.dedup(); + let ids = ids; + match &ids[..] { + // Create a new connected region + [] => { + let id_to_set = connected_regions.next_index(); + // Update the connected region ids + for ident in &idents_to_add { + connected_region_ids.insert(*ident, id_to_set); + } + connected_regions.insert( + id_to_set, + ConnectedRegion { + idents: idents_to_add, + impl_blocks: std::iter::once(i).collect(), + }, + ); } - struct ConnectedRegion { - idents: SmallVec<[Symbol; 8]>, - impl_blocks: FxHashSet<usize>, + // Take the only id inside the list + &[id_to_set] => { + let region = connected_regions[id_to_set].as_mut().unwrap(); + region.impl_blocks.insert(i); + region.idents.extend_from_slice(&idents_to_add); + // Update the connected region ids + for ident in &idents_to_add { + connected_region_ids.insert(*ident, id_to_set); + } } - let mut connected_regions: IndexVec<RegionId, _> = Default::default(); - // Reverse map from the Symbol to the connected region id. - let mut connected_region_ids = FxHashMap::default(); - - for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() { - if impl_items.len() == 0 { - continue; + // We have multiple connected regions to merge. + // In the worst case this might add impl blocks + // one by one and can thus be O(n^2) in the size + // of the resulting final connected region, but + // this is no issue as the final step to check + // for overlaps runs in O(n^2) as well. + &[id_to_set, ..] => { + let mut region = connected_regions.remove(id_to_set).unwrap(); + region.impl_blocks.insert(i); + region.idents.extend_from_slice(&idents_to_add); + // Update the connected region ids + for ident in &idents_to_add { + connected_region_ids.insert(*ident, id_to_set); } - // First obtain a list of existing connected region ids - let mut idents_to_add = SmallVec::<[Symbol; 8]>::new(); - let mut ids = impl_items - .in_definition_order() - .filter_map(|item| { - let entry = connected_region_ids.entry(item.name); - if let Entry::Occupied(e) = &entry { - Some(*e.get()) - } else { - idents_to_add.push(item.name); - None - } - }) - .collect::<SmallVec<[RegionId; 8]>>(); - // Sort the id list so that the algorithm is deterministic - ids.sort_unstable(); - ids.dedup(); - let ids = ids; - match &ids[..] { - // Create a new connected region - [] => { - let id_to_set = connected_regions.next_index(); - // Update the connected region ids - for ident in &idents_to_add { - connected_region_ids.insert(*ident, id_to_set); - } - connected_regions.insert( - id_to_set, - ConnectedRegion { - idents: idents_to_add, - impl_blocks: std::iter::once(i).collect(), - }, - ); - } - // Take the only id inside the list - &[id_to_set] => { - let region = connected_regions[id_to_set].as_mut().unwrap(); - region.impl_blocks.insert(i); - region.idents.extend_from_slice(&idents_to_add); - // Update the connected region ids - for ident in &idents_to_add { - connected_region_ids.insert(*ident, id_to_set); - } - } - // We have multiple connected regions to merge. - // In the worst case this might add impl blocks - // one by one and can thus be O(n^2) in the size - // of the resulting final connected region, but - // this is no issue as the final step to check - // for overlaps runs in O(n^2) as well. - &[id_to_set, ..] => { - let mut region = connected_regions.remove(id_to_set).unwrap(); - region.impl_blocks.insert(i); - region.idents.extend_from_slice(&idents_to_add); - // Update the connected region ids - for ident in &idents_to_add { - connected_region_ids.insert(*ident, id_to_set); - } - - // Remove other regions from ids. - for &id in ids.iter() { - if id == id_to_set { - continue; - } - let r = connected_regions.remove(id).unwrap(); - for ident in r.idents.iter() { - connected_region_ids.insert(*ident, id_to_set); - } - region.idents.extend_from_slice(&r.idents); - region.impl_blocks.extend(r.impl_blocks); - } - connected_regions.insert(id_to_set, region); + // Remove other regions from ids. + for &id in ids.iter() { + if id == id_to_set { + continue; + } + let r = connected_regions.remove(id).unwrap(); + for ident in r.idents.iter() { + connected_region_ids.insert(*ident, id_to_set); } + region.idents.extend_from_slice(&r.idents); + region.impl_blocks.extend(r.impl_blocks); } + + connected_regions.insert(id_to_set, region); } + } + } - debug!( - "churning through {} components (sum={}, avg={}, var={}, max={})", - connected_regions.len(), - impls.len(), - impls.len() / connected_regions.len(), - { - let avg = impls.len() / connected_regions.len(); - let s = connected_regions - .iter() - .flatten() - .map(|r| r.impl_blocks.len() as isize - avg as isize) - .map(|v| v.abs() as usize) - .sum::<usize>(); - s / connected_regions.len() - }, - connected_regions - .iter() - .flatten() - .map(|r| r.impl_blocks.len()) - .max() - .unwrap() - ); - // List of connected regions is built. Now, run the overlap check - // for each pair of impl blocks in the same connected region. - for region in connected_regions.into_iter().flatten() { - let mut impl_blocks = - region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>(); - impl_blocks.sort_unstable(); - for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() { - let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx]; - for &impl2_items_idx in impl_blocks[(i + 1)..].iter() { - let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx]; - if self.impls_have_common_items(impl_items1, impl_items2) { - self.check_for_overlapping_inherent_impls( - overlap_mode, - impl1_def_id, - impl2_def_id, - ); - } - } + debug!( + "churning through {} components (sum={}, avg={}, var={}, max={})", + connected_regions.len(), + impls.len(), + impls.len() / connected_regions.len(), + { + let avg = impls.len() / connected_regions.len(); + let s = connected_regions + .iter() + .flatten() + .map(|r| r.impl_blocks.len() as isize - avg as isize) + .map(|v| v.abs() as usize) + .sum::<usize>(); + s / connected_regions.len() + }, + connected_regions.iter().flatten().map(|r| r.impl_blocks.len()).max().unwrap() + ); + // List of connected regions is built. Now, run the overlap check + // for each pair of impl blocks in the same connected region. + for region in connected_regions.into_iter().flatten() { + let mut impl_blocks = + region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>(); + impl_blocks.sort_unstable(); + for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() { + let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx]; + for &impl2_items_idx in impl_blocks[(i + 1)..].iter() { + let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx]; + if self.impls_have_common_items(impl_items1, impl_items2) { + self.check_for_overlapping_inherent_impls( + overlap_mode, + impl1_def_id, + impl2_def_id, + ); } } } } - _ => {} } } - - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} - - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} - - fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {} } diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs index 19e68f0b14f..f57986a985c 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs @@ -44,6 +44,59 @@ fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua }; let sp = tcx.sess.source_map().guess_head_span(item.span); let tr = impl_.of_trait.as_ref().unwrap(); + + // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples, + // and #84660 where it would otherwise allow unsoundness. + if trait_ref.has_opaque_types() { + trace!("{:#?}", item); + // First we find the opaque type in question. + for ty in trait_ref.substs { + for ty in ty.walk() { + let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue }; + let ty::Opaque(def_id, _) = *ty.kind() else { continue }; + trace!(?def_id); + + // Then we search for mentions of the opaque type's type alias in the HIR + struct SpanFinder<'tcx> { + sp: Span, + def_id: DefId, + tcx: TyCtxt<'tcx>, + } + impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> { + #[instrument(level = "trace", skip(self, _id))] + fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) { + // You can't mention an opaque type directly, so we look for type aliases + if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res { + // And check if that type alias's type contains the opaque type we're looking for + for arg in self.tcx.type_of(def_id).walk() { + if let GenericArgKind::Type(ty) = arg.unpack() { + if let ty::Opaque(def_id, _) = *ty.kind() { + if def_id == self.def_id { + // Finally we update the span to the mention of the type alias + self.sp = path.span; + return; + } + } + } + } + } + hir::intravisit::walk_path(self, path) + } + } + + let mut visitor = SpanFinder { sp, def_id, tcx }; + hir::intravisit::walk_item(&mut visitor, item); + let reported = tcx + .sess + .struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait") + .span_note(tcx.def_span(def_id), "type alias impl trait defined here") + .emit(); + return Err(reported); + } + } + span_bug!(sp, "opaque type not found, but `has_opaque_types` is set") + } + match traits::orphan_check(tcx, item.def_id.to_def_id()) { Ok(()) => {} Err(err) => emit_orphan_check_error( @@ -143,58 +196,6 @@ fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGua } } - // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples, - // and #84660 where it would otherwise allow unsoundness. - if trait_ref.has_opaque_types() { - trace!("{:#?}", item); - // First we find the opaque type in question. - for ty in trait_ref.substs { - for ty in ty.walk() { - let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue }; - let ty::Opaque(def_id, _) = *ty.kind() else { continue }; - trace!(?def_id); - - // Then we search for mentions of the opaque type's type alias in the HIR - struct SpanFinder<'tcx> { - sp: Span, - def_id: DefId, - tcx: TyCtxt<'tcx>, - } - impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> { - #[instrument(level = "trace", skip(self, _id))] - fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) { - // You can't mention an opaque type directly, so we look for type aliases - if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res { - // And check if that type alias's type contains the opaque type we're looking for - for arg in self.tcx.type_of(def_id).walk() { - if let GenericArgKind::Type(ty) = arg.unpack() { - if let ty::Opaque(def_id, _) = *ty.kind() { - if def_id == self.def_id { - // Finally we update the span to the mention of the type alias - self.sp = path.span; - return; - } - } - } - } - } - hir::intravisit::walk_path(self, path) - } - } - - let mut visitor = SpanFinder { sp, def_id, tcx }; - hir::intravisit::walk_item(&mut visitor, item); - let reported = tcx - .sess - .struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait") - .span_note(tcx.def_span(def_id), "type alias impl trait defined here") - .emit(); - return Err(reported); - } - } - span_bug!(sp, "opaque type not found, but `has_opaque_types` is set") - } - Ok(()) } diff --git a/compiler/rustc_typeck/src/coherence/unsafety.rs b/compiler/rustc_typeck/src/coherence/unsafety.rs index f7aabf2406f..3cfc96ccbfd 100644 --- a/compiler/rustc_typeck/src/coherence/unsafety.rs +++ b/compiler/rustc_typeck/src/coherence/unsafety.rs @@ -3,101 +3,83 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; -use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::def::DefKind; use rustc_hir::Unsafety; use rustc_middle::ty::TyCtxt; pub fn check(tcx: TyCtxt<'_>) { - let mut unsafety = UnsafetyChecker { tcx }; - tcx.hir().visit_all_item_likes(&mut unsafety); + for id in tcx.hir().items() { + if matches!(tcx.def_kind(id.def_id), DefKind::Impl) { + let item = tcx.hir().item(id); + if let hir::ItemKind::Impl(ref impl_) = item.kind { + check_unsafety_coherence( + tcx, + item, + Some(&impl_.generics), + impl_.unsafety, + impl_.polarity, + ); + } + } + } } -struct UnsafetyChecker<'tcx> { +fn check_unsafety_coherence<'tcx>( tcx: TyCtxt<'tcx>, -} - -impl<'tcx> UnsafetyChecker<'tcx> { - fn check_unsafety_coherence( - &mut self, - item: &hir::Item<'_>, - impl_generics: Option<&hir::Generics<'_>>, - unsafety: hir::Unsafety, - polarity: hir::ImplPolarity, - ) { - if let Some(trait_ref) = self.tcx.impl_trait_ref(item.def_id) { - let trait_def = self.tcx.trait_def(trait_ref.def_id); - let unsafe_attr = impl_generics.and_then(|generics| { - generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle") - }); - match (trait_def.unsafety, unsafe_attr, unsafety, polarity) { - (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { - struct_span_err!( - self.tcx.sess, - item.span, - E0199, - "implementing the trait `{}` is not unsafe", - trait_ref.print_only_trait_path() - ) - .emit(); - } - - (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => { - struct_span_err!( - self.tcx.sess, - item.span, - E0200, - "the trait `{}` requires an `unsafe impl` declaration", - trait_ref.print_only_trait_path() - ) - .emit(); - } + item: &hir::Item<'_>, + impl_generics: Option<&hir::Generics<'_>>, + unsafety: hir::Unsafety, + polarity: hir::ImplPolarity, +) { + if let Some(trait_ref) = tcx.impl_trait_ref(item.def_id) { + let trait_def = tcx.trait_def(trait_ref.def_id); + let unsafe_attr = impl_generics.and_then(|generics| { + generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle") + }); + match (trait_def.unsafety, unsafe_attr, unsafety, polarity) { + (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => { + struct_span_err!( + tcx.sess, + item.span, + E0199, + "implementing the trait `{}` is not unsafe", + trait_ref.print_only_trait_path() + ) + .emit(); + } - ( - Unsafety::Normal, - Some(attr_name), - Unsafety::Normal, - hir::ImplPolarity::Positive, - ) => { - struct_span_err!( - self.tcx.sess, - item.span, - E0569, - "requires an `unsafe impl` declaration due to `#[{}]` attribute", - attr_name - ) - .emit(); - } + (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => { + struct_span_err!( + tcx.sess, + item.span, + E0200, + "the trait `{}` requires an `unsafe impl` declaration", + trait_ref.print_only_trait_path() + ) + .emit(); + } - (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { - // Reported in AST validation - self.tcx.sess.delay_span_bug(item.span, "unsafe negative impl"); - } - (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_)) - | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) - | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) - | (Unsafety::Normal, None, Unsafety::Normal, _) => { - // OK - } + (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => { + struct_span_err!( + tcx.sess, + item.span, + E0569, + "requires an `unsafe impl` declaration due to `#[{}]` attribute", + attr_name + ) + .emit(); } - } - } -} -impl<'tcx> ItemLikeVisitor<'_> for UnsafetyChecker<'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - if let hir::ItemKind::Impl(ref impl_) = item.kind { - self.check_unsafety_coherence( - item, - Some(&impl_.generics), - impl_.unsafety, - impl_.polarity, - ); + (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => { + // Reported in AST validation + tcx.sess.delay_span_bug(item.span, "unsafe negative impl"); + } + (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_)) + | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) + | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) + | (Unsafety::Normal, None, Unsafety::Normal, _) => { + // OK + } } } - - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} - - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} - - fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {} } diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index 75ad584f419..4b6f80ce57a 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -14,6 +14,7 @@ use rustc_span::{Span, DUMMY_SP}; use super::ItemCtxt; use super::{bad_placeholder, is_suggestable_infer_ty}; +use crate::errors::UnconstrainedOpaqueType; /// Computes the relevant generic parameter for a potential generic const argument. /// @@ -531,7 +532,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { /// In particular, definitions of opaque types can only use other generics as arguments, /// and they cannot repeat an argument. Example: /// -/// ```rust +/// ```ignore (illustrative) /// type Foo<A, B> = impl Bar<A, B>; /// /// // Okay -- `Foo` is applied to two distinct, generic types. @@ -682,13 +683,10 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { match locator.found { Some(hidden) => hidden.ty, None => { - let span = tcx.def_span(def_id); - let name = tcx.item_name(tcx.local_parent(def_id).to_def_id()); - let label = format!( - "`{}` must be used in combination with a concrete type within the same module", - name - ); - tcx.sess.struct_span_err(span, "unconstrained opaque type").note(&label).emit(); + tcx.sess.emit_err(UnconstrainedOpaqueType { + span: tcx.def_span(def_id), + name: tcx.item_name(tcx.local_parent(def_id).to_def_id()), + }); tcx.ty_error() } } diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_typeck/src/constrained_generic_params.rs index 909c99adab5..7f2e57e6109 100644 --- a/compiler/rustc_typeck/src/constrained_generic_params.rs +++ b/compiler/rustc_typeck/src/constrained_generic_params.rs @@ -59,7 +59,7 @@ struct ParameterCollector { impl<'tcx> TypeVisitor<'tcx> for ParameterCollector { fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { match *t.kind() { - ty::Projection(..) | ty::Opaque(..) if !self.include_nonconstraining => { + ty::Projection(..) if !self.include_nonconstraining => { // projections are not injective return ControlFlow::CONTINUE; } @@ -109,9 +109,9 @@ pub fn identify_constrained_generic_params<'tcx>( /// constrained before it is used, if that is possible, and add the /// parameters so constrained to `input_parameters`. For example, /// imagine the following impl: -/// -/// impl<T: Debug, U: Iterator<Item = T>> Trait for U -/// +/// ```ignore (illustrative) +/// impl<T: Debug, U: Iterator<Item = T>> Trait for U +/// ``` /// The impl's predicates are collected from left to right. Ignoring /// the implicit `Sized` bounds, these are /// * T: Debug diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs index 3d2f93537e4..a3e7108caae 100644 --- a/compiler/rustc_typeck/src/errors.rs +++ b/compiler/rustc_typeck/src/errors.rs @@ -228,3 +228,25 @@ pub enum ExpectedReturnTypeLabel<'tcx> { expected: Ty<'tcx>, }, } + +#[derive(SessionDiagnostic)] +#[error(slug = "typeck-unconstrained-opaque-type")] +#[note] +pub struct UnconstrainedOpaqueType { + #[primary_span] + pub span: Span, + pub name: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error(code = "E0632", slug = "typeck-explicit-generic-args-with-impl-trait")] +#[note] +pub struct ExplicitGenericArgsWithImplTrait { + #[primary_span] + #[label] + pub spans: Vec<Span>, +} + +#[derive(SessionSubdiagnostic)] +#[help(slug = "typeck-explicit-generic-args-with-impl-trait-feature")] +pub struct ExplicitGenericArgsWithImplTraitFeature; diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 2bcf2d3b2ed..e69ba99dcef 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -698,12 +698,13 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { /// In the following example the closures `c` only captures `p.x` even though `incr` /// is a capture of the nested closure /// - /// ```rust,ignore(cannot-test-this-because-pseudo-code) - /// let p = ..; + /// ``` + /// struct P { x: i32 } + /// let mut p = P { x: 4 }; /// let c = || { /// let incr = 10; /// let nested = || p.x += incr; - /// } + /// }; /// ``` /// /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs index 8b376e26dee..c089d25d222 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -14,8 +14,8 @@ use min_specialization::check_min_specialization; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::struct_span_err; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_span::Span; @@ -63,35 +63,23 @@ pub fn impl_wf_check(tcx: TyCtxt<'_>) { fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { let min_specialization = tcx.features().min_specialization; - tcx.hir() - .visit_item_likes_in_module(module_def_id, &mut ImplWfCheck { tcx, min_specialization }); -} - -pub fn provide(providers: &mut Providers) { - *providers = Providers { check_mod_impl_wf, ..*providers }; -} - -struct ImplWfCheck<'tcx> { - tcx: TyCtxt<'tcx>, - min_specialization: bool, -} - -impl<'tcx> ItemLikeVisitor<'tcx> for ImplWfCheck<'tcx> { - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - if let hir::ItemKind::Impl(ref impl_) = item.kind { - enforce_impl_params_are_constrained(self.tcx, item.def_id, impl_.items); - enforce_impl_items_are_distinct(self.tcx, impl_.items); - if self.min_specialization { - check_min_specialization(self.tcx, item.def_id.to_def_id(), item.span); + let module = tcx.hir_module_items(module_def_id); + for id in module.items() { + if matches!(tcx.def_kind(id.def_id), DefKind::Impl) { + let item = tcx.hir().item(id); + if let hir::ItemKind::Impl(ref impl_) = item.kind { + enforce_impl_params_are_constrained(tcx, item.def_id, impl_.items); + enforce_impl_items_are_distinct(tcx, impl_.items); + if min_specialization { + check_min_specialization(tcx, item.def_id.to_def_id(), item.span); + } } } } +} - fn visit_trait_item(&mut self, _trait_item: &'tcx hir::TraitItem<'tcx>) {} - - fn visit_impl_item(&mut self, _impl_item: &'tcx hir::ImplItem<'tcx>) {} - - fn visit_foreign_item(&mut self, _foreign_item: &'tcx hir::ForeignItem<'tcx>) {} +pub fn provide(providers: &mut Providers) { + *providers = Providers { check_mod_impl_wf, ..*providers }; } fn enforce_impl_params_are_constrained( diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs index 6cef3e9d940..22db15f4d4e 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -29,7 +29,7 @@ //! //! Suppose we have the following always applicable impl: //! -//! ```rust +//! ```ignore (illustrative) //! impl<T> SpecExtend<T> for std::vec::IntoIter<T> { /* specialized impl */ } //! impl<T, I: Iterator<Item=T>> SpecExtend<T> for I { /* default impl */ } //! ``` diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 03b3d68d59f..fe285820ba6 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -57,7 +57,6 @@ This API is completely unstable and subject to change. #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(crate_visibility_modifier)] diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs index 85e9a670ffb..3d7b6593a64 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_typeck/src/mem_categorization.rs @@ -9,12 +9,12 @@ //! expressions of the following forms (the actual enum has many more //! possibilities, naturally, but they are all variants of these base //! forms): -//! -//! E = rvalue // some computed rvalue -//! | x // address of a local variable or argument -//! | *E // deref of a ptr -//! | E.comp // access to an interior component -//! +//! ```ignore (not-rust) +//! E = rvalue // some computed rvalue +//! | x // address of a local variable or argument +//! | *E // deref of a ptr +//! | E.comp // access to an interior component +//! ``` //! Imagine a routine ToAddr(Expr) that evaluates an expression and returns an //! address where the result is to be found. If Expr is a place, then this //! is the address of the place. If `Expr` is an rvalue, this is the address of diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs index 6f842c6e71a..13d22b13ed4 100644 --- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs +++ b/compiler/rustc_typeck/src/outlives/implicit_infer.rs @@ -35,7 +35,7 @@ pub fn infer_predicates<'tcx>( debug!("InferVisitor::visit_item(item={:?})", item_did); let mut item_required_predicates = RequiredPredicates::default(); - match tcx.hir().def_kind(item_did) { + match tcx.def_kind(item_did) { DefKind::Union | DefKind::Enum | DefKind::Struct => { let adt_def = tcx.adt_def(item_did.to_def_id()); @@ -211,15 +211,15 @@ fn insert_required_predicates_to_be_wf<'tcx>( /// We also have to check the explicit predicates /// declared on the type. +/// ```ignore (illustrative) +/// struct Foo<'a, T> { +/// field1: Bar<T> +/// } /// -/// struct Foo<'a, T> { -/// field1: Bar<T> -/// } -/// -/// struct Bar<U> where U: 'static, U: Foo { -/// ... -/// } -/// +/// struct Bar<U> where U: 'static, U: Foo { +/// ... +/// } +/// ``` /// Here, we should fetch the explicit predicates, which /// will give us `U: 'static` and `U: Foo`. The latter we /// can ignore, but we will want to process `U: 'static`, diff --git a/compiler/rustc_typeck/src/outlives/outlives_bounds.rs b/compiler/rustc_typeck/src/outlives/outlives_bounds.rs index 435df9c00f4..87f844fafe6 100644 --- a/compiler/rustc_typeck/src/outlives/outlives_bounds.rs +++ b/compiler/rustc_typeck/src/outlives/outlives_bounds.rs @@ -26,9 +26,9 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { /// calling that fn, and hence the *callee* can assume that its /// argument types are well-formed. This may imply certain relationships /// between generic parameters. For example: - /// - /// fn foo<'a,T>(x: &'a T) - /// + /// ``` + /// fn foo<'a,T>(x: &'a T) {} + /// ``` /// can only be called with a `'a` and `T` such that `&'a T` is WF. /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. /// diff --git a/compiler/rustc_typeck/src/outlives/test.rs b/compiler/rustc_typeck/src/outlives/test.rs index b3efd9f9ec3..eb0e1203405 100644 --- a/compiler/rustc_typeck/src/outlives/test.rs +++ b/compiler/rustc_typeck/src/outlives/test.rs @@ -1,28 +1,21 @@ use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; pub fn test_inferred_outlives(tcx: TyCtxt<'_>) { - tcx.hir().visit_all_item_likes(&mut OutlivesTest { tcx }); -} - -struct OutlivesTest<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> ItemLikeVisitor<'tcx> for OutlivesTest<'tcx> { - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { + for id in tcx.hir().items() { // For unit testing: check for a special "rustc_outlives" // attribute and report an error with various results if found. - if self.tcx.has_attr(item.def_id.to_def_id(), sym::rustc_outlives) { - let inferred_outlives_of = self.tcx.inferred_outlives_of(item.def_id); - struct_span_err!(self.tcx.sess, item.span, E0640, "{:?}", inferred_outlives_of).emit(); + if tcx.has_attr(id.def_id.to_def_id(), sym::rustc_outlives) { + let inferred_outlives_of = tcx.inferred_outlives_of(id.def_id); + struct_span_err!( + tcx.sess, + tcx.def_span(id.def_id), + E0640, + "{:?}", + inferred_outlives_of + ) + .emit(); } } - - fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {} - fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} - fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {} } diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_typeck/src/variance/constraints.rs index 76755de4964..690c362d853 100644 --- a/compiler/rustc_typeck/src/variance/constraints.rs +++ b/compiler/rustc_typeck/src/variance/constraints.rs @@ -5,7 +5,7 @@ use hir::def_id::{DefId, LocalDefId}; use rustc_hir as hir; -use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::def::DefKind; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -34,11 +34,11 @@ pub struct Constraint<'a> { /// To build constraints, we visit one item (type, trait) at a time /// and look at its contents. So e.g., if we have -/// -/// struct Foo<T> { -/// b: Bar<T> -/// } -/// +/// ```ignore (illustrative) +/// struct Foo<T> { +/// b: Bar<T> +/// } +/// ``` /// then while we are visiting `Bar<T>`, the `CurrentItem` would have /// the `DefId` and the start of `Foo`'s inferreds. pub struct CurrentItem { @@ -62,61 +62,71 @@ pub fn add_constraints_from_crate<'a, 'tcx>( constraints: Vec::new(), }; - tcx.hir().visit_all_item_likes(&mut constraint_cx); + let crate_items = tcx.hir_crate_items(()); + + for id in crate_items.items() { + constraint_cx.check_item(id); + } + + for id in crate_items.trait_items() { + if let DefKind::AssocFn = tcx.def_kind(id.def_id) { + constraint_cx.check_node_helper(id.hir_id()); + } + } + + for id in crate_items.impl_items() { + if let DefKind::AssocFn = tcx.def_kind(id.def_id) { + constraint_cx.check_node_helper(id.hir_id()); + } + } + + for id in crate_items.foreign_items() { + if let DefKind::Fn = tcx.def_kind(id.def_id) { + constraint_cx.check_node_helper(id.hir_id()); + } + } constraint_cx } -impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - match item.kind { - hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { - self.visit_node_helper(item.hir_id()); - - if let hir::VariantData::Tuple(..) = *struct_def { - self.visit_node_helper(struct_def.ctor_hir_id().unwrap()); +impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { + fn check_item(&mut self, id: hir::ItemId) { + let def_kind = self.tcx().def_kind(id.def_id); + match def_kind { + DefKind::Struct | DefKind::Union => { + let item = self.tcx().hir().item(id); + + if let hir::ItemKind::Struct(ref struct_def, _) + | hir::ItemKind::Union(ref struct_def, _) = item.kind + { + self.check_node_helper(item.hir_id()); + + if let hir::VariantData::Tuple(..) = *struct_def { + self.check_node_helper(struct_def.ctor_hir_id().unwrap()); + } } } + DefKind::Enum => { + let item = self.tcx().hir().item(id); - hir::ItemKind::Enum(ref enum_def, _) => { - self.visit_node_helper(item.hir_id()); + if let hir::ItemKind::Enum(ref enum_def, _) = item.kind { + self.check_node_helper(item.hir_id()); - for variant in enum_def.variants { - if let hir::VariantData::Tuple(..) = variant.data { - self.visit_node_helper(variant.data.ctor_hir_id().unwrap()); + for variant in enum_def.variants { + if let hir::VariantData::Tuple(..) = variant.data { + self.check_node_helper(variant.data.ctor_hir_id().unwrap()); + } } } } - - hir::ItemKind::Fn(..) => { - self.visit_node_helper(item.hir_id()); + DefKind::Fn => { + self.check_node_helper(id.hir_id()); } - _ => {} } } - fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { - if let hir::TraitItemKind::Fn(..) = trait_item.kind { - self.visit_node_helper(trait_item.hir_id()); - } - } - - fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { - if let hir::ImplItemKind::Fn(..) = impl_item.kind { - self.visit_node_helper(impl_item.hir_id()); - } - } - - fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) { - if let hir::ForeignItemKind::Fn(..) = foreign_item.kind { - self.visit_node_helper(foreign_item.hir_id()); - } - } -} - -impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { - fn visit_node_helper(&mut self, id: hir::HirId) { + fn check_node_helper(&mut self, id: hir::HirId) { let tcx = self.terms_cx.tcx; let def_id = tcx.hir().local_def_id(id); self.build_constraints_for_item(def_id); diff --git a/compiler/rustc_typeck/src/variance/terms.rs b/compiler/rustc_typeck/src/variance/terms.rs index 36fbfc21ff5..ab64befe5dc 100644 --- a/compiler/rustc_typeck/src/variance/terms.rs +++ b/compiler/rustc_typeck/src/variance/terms.rs @@ -11,7 +11,7 @@ use rustc_arena::DroplessArena; use rustc_hir as hir; -use rustc_hir::itemlikevisit::ItemLikeVisitor; +use rustc_hir::def::DefKind; use rustc_hir::HirIdMap; use rustc_middle::ty::{self, TyCtxt}; use std::fmt; @@ -79,7 +79,29 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>( // // - https://rustc-dev-guide.rust-lang.org/query.html // - https://rustc-dev-guide.rust-lang.org/variance.html - tcx.hir().visit_all_item_likes(&mut terms_cx); + let crate_items = tcx.hir_crate_items(()); + + for id in crate_items.items() { + terms_cx.check_item(id); + } + + for id in crate_items.trait_items() { + if let DefKind::AssocFn = tcx.def_kind(id.def_id) { + terms_cx.add_inferreds_for_item(id.hir_id()); + } + } + + for id in crate_items.impl_items() { + if let DefKind::AssocFn = tcx.def_kind(id.def_id) { + terms_cx.add_inferreds_for_item(id.hir_id()); + } + } + + for id in crate_items.foreign_items() { + if let DefKind::Fn = tcx.def_kind(id.def_id) { + terms_cx.add_inferreds_for_item(id.hir_id()); + } + } terms_cx } @@ -124,54 +146,42 @@ impl<'a, 'tcx> TermsContext<'a, 'tcx> { (start..(start + count)).map(|i| &*arena.alloc(InferredTerm(InferredIndex(i)))), ); } -} -impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for TermsContext<'a, 'tcx> { - fn visit_item(&mut self, item: &hir::Item<'_>) { - debug!("add_inferreds for item {}", self.tcx.hir().node_to_string(item.hir_id())); + fn check_item(&mut self, id: hir::ItemId) { + debug!("add_inferreds for item {}", self.tcx.hir().node_to_string(id.hir_id())); + + let def_kind = self.tcx.def_kind(id.def_id); + match def_kind { + DefKind::Struct | DefKind::Union => { + let item = self.tcx.hir().item(id); - match item.kind { - hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { - self.add_inferreds_for_item(item.hir_id()); + if let hir::ItemKind::Struct(ref struct_def, _) + | hir::ItemKind::Union(ref struct_def, _) = item.kind + { + self.add_inferreds_for_item(item.hir_id()); - if let hir::VariantData::Tuple(..) = *struct_def { - self.add_inferreds_for_item(struct_def.ctor_hir_id().unwrap()); + if let hir::VariantData::Tuple(..) = *struct_def { + self.add_inferreds_for_item(struct_def.ctor_hir_id().unwrap()); + } } } + DefKind::Enum => { + let item = self.tcx.hir().item(id); - hir::ItemKind::Enum(ref enum_def, _) => { - self.add_inferreds_for_item(item.hir_id()); + if let hir::ItemKind::Enum(ref enum_def, _) = item.kind { + self.add_inferreds_for_item(item.hir_id()); - for variant in enum_def.variants { - if let hir::VariantData::Tuple(..) = variant.data { - self.add_inferreds_for_item(variant.data.ctor_hir_id().unwrap()); + for variant in enum_def.variants { + if let hir::VariantData::Tuple(..) = variant.data { + self.add_inferreds_for_item(variant.data.ctor_hir_id().unwrap()); + } } } } - - hir::ItemKind::Fn(..) => { - self.add_inferreds_for_item(item.hir_id()); + DefKind::Fn => { + self.add_inferreds_for_item(id.hir_id()); } - _ => {} } } - - fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { - if let hir::TraitItemKind::Fn(..) = trait_item.kind { - self.add_inferreds_for_item(trait_item.hir_id()); - } - } - - fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { - if let hir::ImplItemKind::Fn(..) = impl_item.kind { - self.add_inferreds_for_item(impl_item.hir_id()); - } - } - - fn visit_foreign_item(&mut self, foreign_item: &hir::ForeignItem<'_>) { - if let hir::ForeignItemKind::Fn(..) = foreign_item.kind { - self.add_inferreds_for_item(foreign_item.hir_id()); - } - } } diff --git a/compiler/rustc_typeck/src/variance/test.rs b/compiler/rustc_typeck/src/variance/test.rs index d6959075d88..2ba87db880b 100644 --- a/compiler/rustc_typeck/src/variance/test.rs +++ b/compiler/rustc_typeck/src/variance/test.rs @@ -1,28 +1,14 @@ use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::sym; pub fn test_variance(tcx: TyCtxt<'_>) { - tcx.hir().visit_all_item_likes(&mut VarianceTest { tcx }); -} - -struct VarianceTest<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> ItemLikeVisitor<'tcx> for VarianceTest<'tcx> { - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - // For unit testing: check for a special "rustc_variance" - // attribute and report an error with various results if found. - if self.tcx.has_attr(item.def_id.to_def_id(), sym::rustc_variance) { - let variances_of = self.tcx.variances_of(item.def_id); - struct_span_err!(self.tcx.sess, item.span, E0208, "{:?}", variances_of).emit(); + // For unit testing: check for a special "rustc_variance" + // attribute and report an error with various results if found. + for id in tcx.hir().items() { + if tcx.has_attr(id.def_id.to_def_id(), sym::rustc_variance) { + let variances_of = tcx.variances_of(id.def_id); + struct_span_err!(tcx.sess, tcx.def_span(id.def_id), E0208, "{:?}", variances_of).emit(); } } - - fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem<'tcx>) {} - fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem<'tcx>) {} - fn visit_foreign_item(&mut self, _: &'tcx hir::ForeignItem<'tcx>) {} } diff --git a/library/alloc/benches/binary_heap.rs b/library/alloc/benches/binary_heap.rs index 491243e22c7..917e71f250e 100644 --- a/library/alloc/benches/binary_heap.rs +++ b/library/alloc/benches/binary_heap.rs @@ -1,11 +1,11 @@ use std::collections::BinaryHeap; -use rand::{seq::SliceRandom, thread_rng}; +use rand::seq::SliceRandom; use test::{black_box, Bencher}; #[bench] fn bench_find_smallest_1000(b: &mut Bencher) { - let mut rng = thread_rng(); + let mut rng = crate::bench_rng(); let mut vec: Vec<u32> = (0..100_000).collect(); vec.shuffle(&mut rng); @@ -47,7 +47,7 @@ fn bench_peek_mut_deref_mut(b: &mut Bencher) { #[bench] fn bench_from_vec(b: &mut Bencher) { - let mut rng = thread_rng(); + let mut rng = crate::bench_rng(); let mut vec: Vec<u32> = (0..100_000).collect(); vec.shuffle(&mut rng); @@ -64,7 +64,7 @@ fn bench_into_sorted_vec(b: &mut Bencher) { #[bench] fn bench_push(b: &mut Bencher) { let mut bheap = BinaryHeap::with_capacity(50_000); - let mut rng = thread_rng(); + let mut rng = crate::bench_rng(); let mut vec: Vec<u32> = (0..50_000).collect(); vec.shuffle(&mut rng); diff --git a/library/alloc/benches/btree/map.rs b/library/alloc/benches/btree/map.rs index 89c21929dbc..1f6b87fb0e4 100644 --- a/library/alloc/benches/btree/map.rs +++ b/library/alloc/benches/btree/map.rs @@ -3,7 +3,7 @@ use std::iter::Iterator; use std::ops::RangeBounds; use std::vec::Vec; -use rand::{seq::SliceRandom, thread_rng, Rng}; +use rand::{seq::SliceRandom, Rng}; use test::{black_box, Bencher}; macro_rules! map_insert_rand_bench { @@ -13,7 +13,7 @@ macro_rules! map_insert_rand_bench { let n: usize = $n; let mut map = $map::new(); // setup - let mut rng = thread_rng(); + let mut rng = crate::bench_rng(); for _ in 0..n { let i = rng.gen::<usize>() % n; @@ -60,7 +60,7 @@ macro_rules! map_from_iter_rand_bench { pub fn $name(b: &mut Bencher) { let n: usize = $n; // setup - let mut rng = thread_rng(); + let mut rng = crate::bench_rng(); let mut vec = Vec::with_capacity(n); for _ in 0..n { @@ -106,7 +106,7 @@ macro_rules! map_find_rand_bench { let n: usize = $n; // setup - let mut rng = thread_rng(); + let mut rng = crate::bench_rng(); let mut keys: Vec<_> = (0..n).map(|_| rng.gen::<usize>() % n).collect(); for &k in &keys { @@ -169,7 +169,7 @@ map_find_seq_bench! {find_seq_10_000, 10_000, BTreeMap} fn bench_iteration(b: &mut Bencher, size: i32) { let mut map = BTreeMap::<i32, i32>::new(); - let mut rng = thread_rng(); + let mut rng = crate::bench_rng(); for _ in 0..size { map.insert(rng.gen(), rng.gen()); @@ -199,7 +199,7 @@ pub fn iteration_100000(b: &mut Bencher) { fn bench_iteration_mut(b: &mut Bencher, size: i32) { let mut map = BTreeMap::<i32, i32>::new(); - let mut rng = thread_rng(); + let mut rng = crate::bench_rng(); for _ in 0..size { map.insert(rng.gen(), rng.gen()); diff --git a/library/alloc/benches/btree/set.rs b/library/alloc/benches/btree/set.rs index 07bf5093727..3f4b0e0f14a 100644 --- a/library/alloc/benches/btree/set.rs +++ b/library/alloc/benches/btree/set.rs @@ -1,10 +1,10 @@ use std::collections::BTreeSet; -use rand::{thread_rng, Rng}; +use rand::Rng; use test::Bencher; fn random(n: usize) -> BTreeSet<usize> { - let mut rng = thread_rng(); + let mut rng = crate::bench_rng(); let mut set = BTreeSet::new(); while set.len() < n { set.insert(rng.gen()); diff --git a/library/alloc/benches/lib.rs b/library/alloc/benches/lib.rs index 38a8f65f169..7dc0f7cebd5 100644 --- a/library/alloc/benches/lib.rs +++ b/library/alloc/benches/lib.rs @@ -17,3 +17,11 @@ mod str; mod string; mod vec; mod vec_deque; + +/// Returns a `rand::Rng` seeded with a consistent seed. +/// +/// This is done to avoid introducing nondeterminism in benchmark results. +fn bench_rng() -> rand_xorshift::XorShiftRng { + const SEED: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + rand::SeedableRng::from_seed(SEED) +} diff --git a/library/alloc/benches/slice.rs b/library/alloc/benches/slice.rs index e20c043286e..bd6f38f2f10 100644 --- a/library/alloc/benches/slice.rs +++ b/library/alloc/benches/slice.rs @@ -1,8 +1,7 @@ use std::{mem, ptr}; use rand::distributions::{Alphanumeric, Standard}; -use rand::{thread_rng, Rng, SeedableRng}; -use rand_xorshift::XorShiftRng; +use rand::Rng; use test::{black_box, Bencher}; #[bench] @@ -152,7 +151,7 @@ fn zero_1kb_mut_iter(b: &mut Bencher) { #[bench] fn random_inserts(b: &mut Bencher) { - let mut rng = thread_rng(); + let mut rng = crate::bench_rng(); b.iter(|| { let mut v = vec![(0, 0); 30]; for _ in 0..100 { @@ -164,7 +163,7 @@ fn random_inserts(b: &mut Bencher) { #[bench] fn random_removes(b: &mut Bencher) { - let mut rng = thread_rng(); + let mut rng = crate::bench_rng(); b.iter(|| { let mut v = vec![(0, 0); 130]; for _ in 0..100 { @@ -182,20 +181,18 @@ fn gen_descending(len: usize) -> Vec<u64> { (0..len as u64).rev().collect() } -const SEED: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - fn gen_random(len: usize) -> Vec<u64> { - let mut rng = XorShiftRng::from_seed(SEED); + let mut rng = crate::bench_rng(); (&mut rng).sample_iter(&Standard).take(len).collect() } fn gen_random_bytes(len: usize) -> Vec<u8> { - let mut rng = XorShiftRng::from_seed(SEED); + let mut rng = crate::bench_rng(); (&mut rng).sample_iter(&Standard).take(len).collect() } fn gen_mostly_ascending(len: usize) -> Vec<u64> { - let mut rng = XorShiftRng::from_seed(SEED); + let mut rng = crate::bench_rng(); let mut v = gen_ascending(len); for _ in (0usize..).take_while(|x| x * x <= len) { let x = rng.gen::<usize>() % len; @@ -206,7 +203,7 @@ fn gen_mostly_ascending(len: usize) -> Vec<u64> { } fn gen_mostly_descending(len: usize) -> Vec<u64> { - let mut rng = XorShiftRng::from_seed(SEED); + let mut rng = crate::bench_rng(); let mut v = gen_descending(len); for _ in (0usize..).take_while(|x| x * x <= len) { let x = rng.gen::<usize>() % len; @@ -217,7 +214,7 @@ fn gen_mostly_descending(len: usize) -> Vec<u64> { } fn gen_strings(len: usize) -> Vec<String> { - let mut rng = XorShiftRng::from_seed(SEED); + let mut rng = crate::bench_rng(); let mut v = vec![]; for _ in 0..len { let n = rng.gen::<usize>() % 20 + 1; @@ -227,7 +224,7 @@ fn gen_strings(len: usize) -> Vec<String> { } fn gen_big_random(len: usize) -> Vec<[u64; 16]> { - let mut rng = XorShiftRng::from_seed(SEED); + let mut rng = crate::bench_rng(); (&mut rng).sample_iter(&Standard).map(|x| [x; 16]).take(len).collect() } diff --git a/library/alloc/benches/vec.rs b/library/alloc/benches/vec.rs index 333b3c20d1d..efc47327e8a 100644 --- a/library/alloc/benches/vec.rs +++ b/library/alloc/benches/vec.rs @@ -476,7 +476,7 @@ fn bench_in_place_recycle(b: &mut Bencher) { #[bench] fn bench_in_place_zip_recycle(b: &mut Bencher) { let mut data = vec![0u8; 1000]; - let mut rng = rand::thread_rng(); + let mut rng = crate::bench_rng(); let mut subst = vec![0u8; 1000]; rng.fill_bytes(&mut subst[..]); @@ -495,7 +495,7 @@ fn bench_in_place_zip_recycle(b: &mut Bencher) { #[bench] fn bench_in_place_zip_iter_mut(b: &mut Bencher) { let mut data = vec![0u8; 256]; - let mut rng = rand::thread_rng(); + let mut rng = crate::bench_rng(); let mut subst = vec![0u8; 1000]; rng.fill_bytes(&mut subst[..]); diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 639e7f213ea..c07536f0d0c 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1369,6 +1369,12 @@ impl<T: ?Sized + Hasher, A: Allocator> Hasher for Box<T, A> { fn write_isize(&mut self, i: isize) { (**self).write_isize(i) } + fn write_length_prefix(&mut self, len: usize) { + (**self).write_length_prefix(len) + } + fn write_str(&mut self, s: &str) { + (**self).write_str(s) + } } #[cfg(not(no_global_oom_handling))] diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index c178d3e3b03..264c217c9ef 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1990,7 +1990,7 @@ impl<'a, K: Ord + Copy, V: Copy> Extend<(&'a K, &'a V)> for BTreeMap<K, V> { #[stable(feature = "rust1", since = "1.0.0")] impl<K: Hash, V: Hash> Hash for BTreeMap<K, V> { fn hash<H: Hasher>(&self, state: &mut H) { - self.len().hash(state); + state.write_length_prefix(self.len()); for elt in self { elt.hash(state); } diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 736b38370ab..67dc4f30f31 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1944,7 +1944,7 @@ impl<T: fmt::Debug> fmt::Debug for LinkedList<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T: Hash> Hash for LinkedList<T> { fn hash<H: Hasher>(&self, state: &mut H) { - self.len().hash(state); + state.write_length_prefix(self.len()); for elt in self { elt.hash(state); } diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 5f1a6848ae6..04900ead579 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -2899,7 +2899,7 @@ impl<T: Ord, A: Allocator> Ord for VecDeque<T, A> { #[stable(feature = "rust1", since = "1.0.0")] impl<T: Hash, A: Allocator> Hash for VecDeque<T, A> { fn hash<H: Hasher>(&self, state: &mut H) { - self.len().hash(state); + state.write_length_prefix(self.len()); // It's not possible to use Hash::hash_slice on slices // returned by as_slices method as their length can vary // in otherwise identical deques. diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 4d2dc4ecee0..ecebc7ed9ac 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -117,6 +117,7 @@ #![feature(extend_one)] #![feature(fmt_internals)] #![feature(fn_traits)] +#![feature(hasher_prefixfree_extras)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(layout_for_ptr)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 4bcc78ae0f4..52957456473 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -374,19 +374,26 @@ impl<T> Rc<T> { } } - /// Constructs a new `Rc<T>` using a closure `data_fn` that has access to a - /// weak reference to the constructing `Rc<T>`. + /// Constructs a new `Rc<T>` while giving you a `Weak<T>` to the allocation, + /// to allow you to construct a `T` which holds a weak pointer to itself. /// /// Generally, a structure circularly referencing itself, either directly or - /// indirectly, should not hold a strong reference to prevent a memory leak. - /// In `data_fn`, initialization of `T` can make use of the weak reference - /// by cloning and storing it inside `T` for use at a later time. + /// indirectly, should not hold a strong reference to itself to prevent a memory leak. + /// Using this function, you get access to the weak pointer during the + /// initialization of `T`, before the `Rc<T>` is created, such that you can + /// clone and store it inside the `T`. + /// + /// `new_cyclic` first allocates the managed allocation for the `Rc<T>`, + /// then calls your closure, giving it a `Weak<T>` to this allocation, + /// and only afterwards completes the construction of the `Rc<T>` by placing + /// the `T` returned from your closure into the allocation. /// /// Since the new `Rc<T>` is not fully-constructed until `Rc<T>::new_cyclic` - /// returns, calling [`upgrade`] on the weak reference inside `data_fn` will + /// returns, calling [`upgrade`] on the weak reference inside your closure will /// fail and result in a `None` value. /// /// # Panics + /// /// If `data_fn` panics, the panic is propagated to the caller, and the /// temporary [`Weak<T>`] is dropped normally. /// @@ -403,7 +410,12 @@ impl<T> Rc<T> { /// impl Gadget { /// /// Construct a reference counted Gadget. /// fn new() -> Rc<Self> { - /// Rc::new_cyclic(|me| Gadget { me: me.clone() }) + /// // `me` is a `Weak<Gadget>` pointing at the new allocation of the + /// // `Rc` we're constructing. + /// Rc::new_cyclic(|me| { + /// // Create the actual struct here. + /// Gadget { me: me.clone() } + /// }) /// } /// /// /// Return a reference counted pointer to Self. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 1e2caddcacb..06aecd9cc1e 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -25,7 +25,7 @@ use core::ptr::{self, NonNull}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; use core::sync::atomic; -use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; +use core::sync::atomic::Ordering::{Acquire, Relaxed, Release}; #[cfg(not(no_global_oom_handling))] use crate::alloc::handle_alloc_error; @@ -351,23 +351,31 @@ impl<T> Arc<T> { unsafe { Self::from_inner(Box::leak(x).into()) } } - /// Constructs a new `Arc<T>` using a closure `data_fn` that has access to - /// a weak reference to the constructing `Arc<T>`. + /// Constructs a new `Arc<T>` while giving you a `Weak<T>` to the allocation, + /// to allow you to construct a `T` which holds a weak pointer to itself. /// /// Generally, a structure circularly referencing itself, either directly or - /// indirectly, should not hold a strong reference to prevent a memory leak. - /// In `data_fn`, initialization of `T` can make use of the weak reference - /// by cloning and storing it inside `T` for use at a later time. + /// indirectly, should not hold a strong reference to itself to prevent a memory leak. + /// Using this function, you get access to the weak pointer during the + /// initialization of `T`, before the `Arc<T>` is created, such that you can + /// clone and store it inside the `T`. /// - /// Since the new `Arc<T>` is not fully-constructed until - /// `Arc<T>::new_cyclic` returns, calling [`upgrade`] on the weak - /// reference inside `data_fn` will fail and result in a `None` value. + /// `new_cyclic` first allocates the managed allocation for the `Arc<T>`, + /// then calls your closure, giving it a `Weak<T>` to this allocation, + /// and only afterwards completes the construction of the `Arc<T>` by placing + /// the `T` returned from your closure into the allocation. + /// + /// Since the new `Arc<T>` is not fully-constructed until `Arc<T>::new_cyclic` + /// returns, calling [`upgrade`] on the weak reference inside your closure will + /// fail and result in a `None` value. /// /// # Panics + /// /// If `data_fn` panics, the panic is propagated to the caller, and the /// temporary [`Weak<T>`] is dropped normally. /// /// # Example + /// /// ``` /// # #![allow(dead_code)] /// use std::sync::{Arc, Weak}; @@ -379,7 +387,12 @@ impl<T> Arc<T> { /// impl Gadget { /// /// Construct a reference counted Gadget. /// fn new() -> Arc<Self> { - /// Arc::new_cyclic(|me| Gadget { me: me.clone() }) + /// // `me` is a `Weak<Gadget>` pointing at the new allocation of the + /// // `Arc` we're constructing. + /// Arc::new_cyclic(|me| { + /// // Create the actual struct here. + /// Gadget { me: me.clone() } + /// }) /// } /// /// /// Return a reference counted pointer to Self. @@ -971,7 +984,7 @@ impl<T: ?Sized> Arc<T> { #[must_use] #[stable(feature = "arc_counts", since = "1.15.0")] pub fn weak_count(this: &Self) -> usize { - let cnt = this.inner().weak.load(SeqCst); + let cnt = this.inner().weak.load(Acquire); // If the weak count is currently locked, the value of the // count was 0 just before taking the lock. if cnt == usize::MAX { 0 } else { cnt - 1 } @@ -1001,7 +1014,7 @@ impl<T: ?Sized> Arc<T> { #[must_use] #[stable(feature = "arc_counts", since = "1.15.0")] pub fn strong_count(this: &Self) -> usize { - this.inner().strong.load(SeqCst) + this.inner().strong.load(Acquire) } /// Increments the strong reference count on the `Arc<T>` associated with the @@ -1963,7 +1976,7 @@ impl<T: ?Sized> Weak<T> { #[must_use] #[stable(feature = "weak_counts", since = "1.41.0")] pub fn strong_count(&self) -> usize { - if let Some(inner) = self.inner() { inner.strong.load(SeqCst) } else { 0 } + if let Some(inner) = self.inner() { inner.strong.load(Acquire) } else { 0 } } /// Gets an approximation of the number of `Weak` pointers pointing to this @@ -1982,8 +1995,8 @@ impl<T: ?Sized> Weak<T> { pub fn weak_count(&self) -> usize { self.inner() .map(|inner| { - let weak = inner.weak.load(SeqCst); - let strong = inner.strong.load(SeqCst); + let weak = inner.weak.load(Acquire); + let strong = inner.strong.load(Acquire); if strong == 0 { 0 } else { diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 6a0c6cd961d..2a7df9556cf 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -25,6 +25,7 @@ test = true [dev-dependencies] rand = "0.7" +rand_xorshift = "0.2" [features] # Make panics and failed asserts immediately abort without formatting any message diff --git a/library/core/benches/lib.rs b/library/core/benches/lib.rs index f1f1ae6e463..a6c174d2fca 100644 --- a/library/core/benches/lib.rs +++ b/library/core/benches/lib.rs @@ -18,3 +18,11 @@ mod ops; mod pattern; mod slice; mod str; + +/// Returns a `rand::Rng` seeded with a consistent seed. +/// +/// This is done to avoid introducing nondeterminism in benchmark results. +fn bench_rng() -> rand_xorshift::XorShiftRng { + const SEED: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + rand::SeedableRng::from_seed(SEED) +} diff --git a/library/core/benches/num/int_log/mod.rs b/library/core/benches/num/int_log/mod.rs index 6a219bcdd55..19864d2d467 100644 --- a/library/core/benches/num/int_log/mod.rs +++ b/library/core/benches/num/int_log/mod.rs @@ -17,7 +17,7 @@ macro_rules! int_log_bench { #[bench] fn $random(bench: &mut Bencher) { - let mut rng = rand::thread_rng(); + let mut rng = crate::bench_rng(); /* Exponentially distributed random numbers from the whole range of the type. */ let numbers: Vec<$t> = (0..256) .map(|_| { @@ -34,7 +34,7 @@ macro_rules! int_log_bench { #[bench] fn $random_small(bench: &mut Bencher) { - let mut rng = rand::thread_rng(); + let mut rng = crate::bench_rng(); /* Exponentially distributed random numbers from the range 0..256. */ let numbers: Vec<$t> = (0..256) .map(|_| { diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs index 36000f8f389..f7a8aa0d921 100644 --- a/library/core/src/bool.rs +++ b/library/core/src/bool.rs @@ -9,12 +9,10 @@ impl bool { /// # Examples /// /// ``` - /// #![feature(bool_to_option)] - /// /// assert_eq!(false.then_some(0), None); /// assert_eq!(true.then_some(0), Some(0)); /// ``` - #[unstable(feature = "bool_to_option", issue = "80967")] + #[stable(feature = "bool_to_option", since = "1.62.0")] #[rustc_const_unstable(feature = "const_bool_to_option", issue = "91917")] #[inline] pub const fn then_some<T>(self, t: T) -> Option<T> diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 15d9d013997..ca335d53c7c 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -77,7 +77,7 @@ use crate::str; #[derive(Hash)] #[cfg_attr(not(test), rustc_diagnostic_item = "CStr")] #[unstable(feature = "core_c_str", issue = "94079")] -#[cfg_attr(not(bootstrap), lang = "CStr")] +#[cfg_attr(not(bootstrap), rustc_has_incoherent_inherent_impls)] // FIXME: // `fn from` in `impl From<&CStr> for Box<CStr>` current implementation relies // on `CStr` being layout-compatible with `[u8]`. diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 6c49521c223..a38b8fb739a 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -58,29 +58,25 @@ type_alias! { "c_char.md", c_char = c_char_definition::c_char, NonZero_c_char = // is fixed. #[cfg(all())] #[doc(cfg(all()))] } + type_alias! { "c_schar.md", c_schar = i8, NonZero_c_schar = NonZeroI8; } type_alias! { "c_uchar.md", c_uchar = u8, NonZero_c_uchar = NonZeroU8; } type_alias! { "c_short.md", c_short = i16, NonZero_c_short = NonZeroI16; } type_alias! { "c_ushort.md", c_ushort = u16, NonZero_c_ushort = NonZeroU16; } -#[cfg(any(target_arch = "avr", target_arch = "msp430"))] -type_alias! { "c_int.md", c_int = i16, NonZero_c_int = NonZeroI16; } -#[cfg(not(any(target_arch = "avr", target_arch = "msp430")))] -type_alias! { "c_int.md", c_int = i32, NonZero_c_int = NonZeroI32; } -type_alias! { "c_uint.md", c_uint = u32, NonZero_c_uint = NonZeroU32; } -type_alias! { "c_long.md", c_long = i32, NonZero_c_long = NonZeroI32; -#[doc(cfg(all()))] -#[cfg(any(target_pointer_width = "32", windows))] } -type_alias! { "c_ulong.md", c_ulong = u32, NonZero_c_ulong = NonZeroU32; -#[doc(cfg(all()))] -#[cfg(any(target_pointer_width = "32", windows))] } -type_alias! { "c_long.md", c_long = i64, NonZero_c_long = NonZeroI64; -#[doc(cfg(all()))] -#[cfg(all(target_pointer_width = "64", not(windows)))] } -type_alias! { "c_ulong.md", c_ulong = u64, NonZero_c_ulong = NonZeroU64; -#[doc(cfg(all()))] -#[cfg(all(target_pointer_width = "64", not(windows)))] } + +type_alias! { "c_int.md", c_int = c_int_definition::c_int, NonZero_c_int = c_int_definition::NonZero_c_int; +#[doc(cfg(all()))] } +type_alias! { "c_uint.md", c_uint = c_int_definition::c_uint, NonZero_c_uint = c_int_definition::NonZero_c_uint; +#[doc(cfg(all()))] } + +type_alias! { "c_long.md", c_long = c_long_definition::c_long, NonZero_c_long = c_long_definition::NonZero_c_long; +#[doc(cfg(all()))] } +type_alias! { "c_ulong.md", c_ulong = c_long_definition::c_ulong, NonZero_c_ulong = c_long_definition::NonZero_c_ulong; +#[doc(cfg(all()))] } + type_alias! { "c_longlong.md", c_longlong = i64, NonZero_c_longlong = NonZeroI64; } type_alias! { "c_ulonglong.md", c_ulonglong = u64, NonZero_c_ulonglong = NonZeroU64; } + type_alias_no_nz! { "c_float.md", c_float = f32; } type_alias_no_nz! { "c_double.md", c_double = f64; } @@ -159,6 +155,39 @@ mod c_char_definition { } } +mod c_int_definition { + cfg_if! { + if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] { + pub type c_int = i16; + pub type NonZero_c_int = crate::num::NonZeroI16; + pub type c_uint = u16; + pub type NonZero_c_uint = crate::num::NonZeroU16; + } else { + pub type c_int = i32; + pub type NonZero_c_int = crate::num::NonZeroI32; + pub type c_uint = u32; + pub type NonZero_c_uint = crate::num::NonZeroU32; + } + } +} + +mod c_long_definition { + cfg_if! { + if #[cfg(all(target_pointer_width = "64", not(windows)))] { + pub type c_long = i64; + pub type NonZero_c_long = crate::num::NonZeroI64; + pub type c_ulong = u64; + pub type NonZero_c_ulong = crate::num::NonZeroU64; + } else { + // The minimal size of `long` in the C standard is 32 bits + pub type c_long = i32; + pub type NonZero_c_long = crate::num::NonZeroI32; + pub type c_ulong = u32; + pub type NonZero_c_ulong = crate::num::NonZeroU32; + } + } +} + // N.B., for LLVM to recognize the void pointer type and by extension // functions like malloc(), we need to have it represented as i8* in // LLVM bitcode. The enum used here ensures this and prevents misuse diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index fac959ac734..feedc6456b8 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -600,7 +600,7 @@ impl Display for Arguments<'_> { /// /// Types that do not wish to use the standard suite of debug representations /// provided by the `Formatter` trait (`debug_struct`, `debug_tuple`, -/// `debut_list`, `debug_set`, `debug_map`) can do something totally custom by +/// `debug_list`, `debug_set`, `debug_map`) can do something totally custom by /// manually writing an arbitrary representation to the `Formatter`. /// /// ``` diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index 45c9df0c930..9d64c786d67 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -333,6 +333,12 @@ pub trait Hasher { /// /// println!("Hash is {:x}!", hasher.finish()); /// ``` + /// + /// # Note to Implementers + /// + /// You generally should not do length-prefixing as part of implementing + /// this method. It's up to the [`Hash`] implementation to call + /// [`Hasher::write_length_prefix`] before sequences that need it. #[stable(feature = "rust1", since = "1.0.0")] fn write(&mut self, bytes: &[u8]); @@ -409,6 +415,127 @@ pub trait Hasher { fn write_isize(&mut self, i: isize) { self.write_usize(i as usize) } + + /// Writes a length prefix into this hasher, as part of being prefix-free. + /// + /// If you're implementing [`Hash`] for a custom collection, call this before + /// writing its contents to this `Hasher`. That way + /// `(collection![1, 2, 3], collection![4, 5])` and + /// `(collection![1, 2], collection![3, 4, 5])` will provide different + /// sequences of values to the `Hasher` + /// + /// The `impl<T> Hash for [T]` includes a call to this method, so if you're + /// hashing a slice (or array or vector) via its `Hash::hash` method, + /// you should **not** call this yourself. + /// + /// This method is only for providing domain separation. If you want to + /// hash a `usize` that represents part of the *data*, then it's important + /// that you pass it to [`Hasher::write_usize`] instead of to this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(hasher_prefixfree_extras)] + /// # // Stubs to make the `impl` below pass the compiler + /// # struct MyCollection<T>(Option<T>); + /// # impl<T> MyCollection<T> { + /// # fn len(&self) -> usize { todo!() } + /// # } + /// # impl<'a, T> IntoIterator for &'a MyCollection<T> { + /// # type Item = T; + /// # type IntoIter = std::iter::Empty<T>; + /// # fn into_iter(self) -> Self::IntoIter { todo!() } + /// # } + /// + /// use std::hash::{Hash, Hasher}; + /// impl<T: Hash> Hash for MyCollection<T> { + /// fn hash<H: Hasher>(&self, state: &mut H) { + /// state.write_length_prefix(self.len()); + /// for elt in self { + /// elt.hash(state); + /// } + /// } + /// } + /// ``` + /// + /// # Note to Implementers + /// + /// If you've decided that your `Hasher` is willing to be susceptible to + /// Hash-DoS attacks, then you might consider skipping hashing some or all + /// of the `len` provided in the name of increased performance. + #[inline] + #[unstable(feature = "hasher_prefixfree_extras", issue = "96762")] + fn write_length_prefix(&mut self, len: usize) { + self.write_usize(len); + } + + /// Writes a single `str` into this hasher. + /// + /// If you're implementing [`Hash`], you generally do not need to call this, + /// as the `impl Hash for str` does, so you should prefer that instead. + /// + /// This includes the domain separator for prefix-freedom, so you should + /// **not** call `Self::write_length_prefix` before calling this. + /// + /// # Note to Implementers + /// + /// There are at least two reasonable default ways to implement this. + /// Which one will be the default is not yet decided, so for now + /// you probably want to override it specifically. + /// + /// ## The general answer + /// + /// It's always correct to implement this with a length prefix: + /// + /// ``` + /// # #![feature(hasher_prefixfree_extras)] + /// # struct Foo; + /// # impl std::hash::Hasher for Foo { + /// # fn finish(&self) -> u64 { unimplemented!() } + /// # fn write(&mut self, _bytes: &[u8]) { unimplemented!() } + /// fn write_str(&mut self, s: &str) { + /// self.write_length_prefix(s.len()); + /// self.write(s.as_bytes()); + /// } + /// # } + /// ``` + /// + /// And, if your `Hasher` works in `usize` chunks, this is likely a very + /// efficient way to do it, as anything more complicated may well end up + /// slower than just running the round with the length. + /// + /// ## If your `Hasher` works byte-wise + /// + /// One nice thing about `str` being UTF-8 is that the `b'\xFF'` byte + /// never happens. That means that you can append that to the byte stream + /// being hashed and maintain prefix-freedom: + /// + /// ``` + /// # #![feature(hasher_prefixfree_extras)] + /// # struct Foo; + /// # impl std::hash::Hasher for Foo { + /// # fn finish(&self) -> u64 { unimplemented!() } + /// # fn write(&mut self, _bytes: &[u8]) { unimplemented!() } + /// fn write_str(&mut self, s: &str) { + /// self.write(s.as_bytes()); + /// self.write_u8(0xff); + /// } + /// # } + /// ``` + /// + /// This does require that your implementation not add extra padding, and + /// thus generally requires that you maintain a buffer, running a round + /// only once that buffer is full (or `finish` is called). + /// + /// That's because if `write` pads data out to a fixed chunk size, it's + /// likely that it does it in such a way that `"a"` and `"a\x00"` would + /// end up hashing the same sequence of things, introducing conflicts. + #[inline] + #[unstable(feature = "hasher_prefixfree_extras", issue = "96762")] + fn write_str(&mut self, s: &str) { + self.write(s.as_bytes()); + self.write_u8(0xff); + } } #[stable(feature = "indirect_hasher_impl", since = "1.22.0")] @@ -455,6 +582,12 @@ impl<H: Hasher + ?Sized> Hasher for &mut H { fn write_isize(&mut self, i: isize) { (**self).write_isize(i) } + fn write_length_prefix(&mut self, len: usize) { + (**self).write_length_prefix(len) + } + fn write_str(&mut self, s: &str) { + (**self).write_str(s) + } } /// A trait for creating instances of [`Hasher`]. @@ -709,8 +842,7 @@ mod impls { impl Hash for str { #[inline] fn hash<H: Hasher>(&self, state: &mut H) { - state.write(self.as_bytes()); - state.write_u8(0xff) + state.write_str(self); } } @@ -767,7 +899,7 @@ mod impls { impl<T: Hash> Hash for [T] { #[inline] fn hash<H: Hasher>(&self, state: &mut H) { - self.len().hash(state); + state.write_length_prefix(self.len()); Hash::hash_slice(self, state) } } diff --git a/library/core/src/hash/sip.rs b/library/core/src/hash/sip.rs index b9443e30074..9d7daf1f1a0 100644 --- a/library/core/src/hash/sip.rs +++ b/library/core/src/hash/sip.rs @@ -234,6 +234,11 @@ impl super::Hasher for SipHasher { } #[inline] + fn write_str(&mut self, s: &str) { + self.0.hasher.write_str(s); + } + + #[inline] fn finish(&self) -> u64 { self.0.hasher.finish() } @@ -247,6 +252,11 @@ impl super::Hasher for SipHasher13 { } #[inline] + fn write_str(&mut self, s: &str) { + self.hasher.write_str(s); + } + + #[inline] fn finish(&self) -> u64 { self.hasher.finish() } @@ -308,6 +318,14 @@ impl<S: Sip> super::Hasher for Hasher<S> { } #[inline] + fn write_str(&mut self, s: &str) { + // This hasher works byte-wise, and `0xFF` cannot show up in a `str`, + // so just hashing the one extra byte is enough to be prefix-free. + self.write(s.as_bytes()); + self.write_u8(0xFF); + } + + #[inline] fn finish(&self) -> u64 { let mut state = self.state; diff --git a/library/core/src/internal_macros.rs b/library/core/src/internal_macros.rs index 7ef78e0b48a..5d4c9ba7395 100644 --- a/library/core/src/internal_macros.rs +++ b/library/core/src/internal_macros.rs @@ -190,7 +190,7 @@ macro_rules! impl_fn_for_zst { /// /// # Example /// -/// ``` +/// ```ignore(cannot-test-this-because-non-exported-macro) /// cfg_if! { /// if #[cfg(unix)] { /// fn foo() { /* unix specific functionality */ } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index bddbe2b9b0d..4e32c514d5f 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -981,7 +981,7 @@ extern "rust-intrinsic" { /// /// Turning a pointer into a `usize`: /// - /// ``` + /// ```no_run /// let ptr = &0; /// let ptr_num_transmute = unsafe { /// std::mem::transmute::<&i32, usize>(ptr) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 1bf44734740..b5c7982a5a8 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2015,7 +2015,12 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0 or the division results in overflow. + /// This function will panic if `rhs` is zero. + /// + /// ## Overflow behavior + /// + /// On overflow, this function will panic if overflow checks are enabled (default in debug + /// mode) and wrap if overflow checks are disabled (default in release mode). /// /// # Examples /// @@ -2050,7 +2055,12 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0 or the division results in overflow. + /// This function will panic if `rhs` is zero. + /// + /// ## Overflow behavior + /// + /// On overflow, this function will panic if overflow checks are enabled (default in debug + /// mode) and wrap if overflow checks are disabled (default in release mode). /// /// # Examples /// @@ -2088,7 +2098,12 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0 or the operation results in overflow. + /// This function will panic if `rhs` is zero. + /// + /// ## Overflow behavior + /// + /// On overflow, this function will panic if overflow checks are enabled (default in debug + /// mode) and wrap if overflow checks are disabled (default in release mode). /// /// # Examples /// @@ -2157,7 +2172,6 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - #[rustc_inherit_overflow_checks] pub const fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> { // This would otherwise fail when calculating `r` when self == T::MIN. if rhs == -1 { diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index ce52e4773ce..048d6bafcde 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2020,7 +2020,7 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. /// /// # Examples /// @@ -2034,7 +2034,6 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] - #[rustc_inherit_overflow_checks] pub const fn div_floor(self, rhs: Self) -> Self { self / rhs } @@ -2043,7 +2042,12 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0. + /// This function will panic if `rhs` is zero. + /// + /// ## Overflow behavior + /// + /// On overflow, this function will panic if overflow checks are enabled (default in debug + /// mode) and wrap if overflow checks are disabled (default in release mode). /// /// # Examples /// @@ -2073,7 +2077,12 @@ macro_rules! uint_impl { /// /// # Panics /// - /// This function will panic if `rhs` is 0 or the operation results in overflow. + /// This function will panic if `rhs` is zero. + /// + /// ## Overflow behavior + /// + /// On overflow, this function will panic if overflow checks are enabled (default in debug + /// mode) and wrap if overflow checks are disabled (default in release mode). /// /// # Examples /// @@ -2097,7 +2106,7 @@ macro_rules! uint_impl { } /// Calculates the smallest value greater than or equal to `self` that - /// is a multiple of `rhs`. Returns `None` is `rhs` is zero or the + /// is a multiple of `rhs`. Returns `None` if `rhs` is zero or the /// operation would result in overflow. /// /// # Examples @@ -2115,7 +2124,6 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - #[rustc_inherit_overflow_checks] pub const fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> { match try_opt!(self.checked_rem(rhs)) { 0 => Some(self), diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 7ef2e95542b..45964c3a444 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -518,7 +518,7 @@ impl<T: ?Sized> *const T { } /// Calculates the distance between two pointers. The returned value is in - /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`. + /// units of T: the distance in bytes divided by `mem::size_of::<T>()`. /// /// This function is the inverse of [`offset`]. /// diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 56f9c84f5af..ed80cdc9bf9 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -287,7 +287,7 @@ impl<T: ?Sized> *mut T { /// For the mutable counterpart see [`as_mut`]. /// /// [`as_uninit_ref`]: #method.as_uninit_ref-1 - /// [`as_mut`]: #method.as_mut + /// [`as_mut`]: #method.as_mut-1 /// /// # Safety /// @@ -696,7 +696,7 @@ impl<T: ?Sized> *mut T { } /// Calculates the distance between two pointers. The returned value is in - /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`. + /// units of T: the distance in bytes divided by `mem::size_of::<T>()`. /// /// This function is the inverse of [`offset`]. /// diff --git a/library/core/tests/hash/mod.rs b/library/core/tests/hash/mod.rs index a173e461c60..5bc6aac1778 100644 --- a/library/core/tests/hash/mod.rs +++ b/library/core/tests/hash/mod.rs @@ -20,6 +20,10 @@ impl Hasher for MyHasher { self.hash += *byte as u64; } } + fn write_str(&mut self, s: &str) { + self.write(s.as_bytes()); + self.write_u8(0xFF); + } fn finish(&self) -> u64 { self.hash } diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 485fa305c00..31b764a6e2d 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -3,7 +3,6 @@ #![feature(array_methods)] #![feature(array_windows)] #![feature(bench_black_box)] -#![feature(bool_to_option)] #![feature(box_syntax)] #![feature(cell_update)] #![feature(const_assume)] @@ -37,6 +36,7 @@ #![feature(future_join)] #![feature(future_poll_fn)] #![feature(array_from_fn)] +#![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(try_find)] #![feature(inline_const)] diff --git a/library/core/tests/num/i128.rs b/library/core/tests/num/i128.rs index 72c0b225991..1ddd20f33d0 100644 --- a/library/core/tests/num/i128.rs +++ b/library/core/tests/num/i128.rs @@ -1 +1 @@ -int_module!(i128, i128); +int_module!(i128); diff --git a/library/core/tests/num/i16.rs b/library/core/tests/num/i16.rs index f5544b914b7..c7aa9fff964 100644 --- a/library/core/tests/num/i16.rs +++ b/library/core/tests/num/i16.rs @@ -1 +1 @@ -int_module!(i16, i16); +int_module!(i16); diff --git a/library/core/tests/num/i32.rs b/library/core/tests/num/i32.rs index 4acc760ffac..efd5b1596a8 100644 --- a/library/core/tests/num/i32.rs +++ b/library/core/tests/num/i32.rs @@ -1,4 +1,4 @@ -int_module!(i32, i32); +int_module!(i32); #[test] fn test_arith_operation() { diff --git a/library/core/tests/num/i64.rs b/library/core/tests/num/i64.rs index fa4d2ab6638..93d23c10adf 100644 --- a/library/core/tests/num/i64.rs +++ b/library/core/tests/num/i64.rs @@ -1 +1 @@ -int_module!(i64, i64); +int_module!(i64); diff --git a/library/core/tests/num/i8.rs b/library/core/tests/num/i8.rs index ccec6915fe0..887d4f17d25 100644 --- a/library/core/tests/num/i8.rs +++ b/library/core/tests/num/i8.rs @@ -1 +1 @@ -int_module!(i8, i8); +int_module!(i8); diff --git a/library/core/tests/num/int_macros.rs b/library/core/tests/num/int_macros.rs index d2d655ea2c7..8b84a78e6be 100644 --- a/library/core/tests/num/int_macros.rs +++ b/library/core/tests/num/int_macros.rs @@ -1,9 +1,9 @@ macro_rules! int_module { - ($T:ident, $T_i:ident) => { + ($T:ident) => { #[cfg(test)] mod tests { use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; - use core::$T_i::*; + use core::$T::*; use crate::num; diff --git a/library/core/tests/num/u128.rs b/library/core/tests/num/u128.rs index 716d1836f2c..a7b0f9effef 100644 --- a/library/core/tests/num/u128.rs +++ b/library/core/tests/num/u128.rs @@ -1 +1 @@ -uint_module!(u128, u128); +uint_module!(u128); diff --git a/library/core/tests/num/u16.rs b/library/core/tests/num/u16.rs index 435b914224c..010596a34a5 100644 --- a/library/core/tests/num/u16.rs +++ b/library/core/tests/num/u16.rs @@ -1 +1 @@ -uint_module!(u16, u16); +uint_module!(u16); diff --git a/library/core/tests/num/u32.rs b/library/core/tests/num/u32.rs index 71dc005dea3..687d3bbaa90 100644 --- a/library/core/tests/num/u32.rs +++ b/library/core/tests/num/u32.rs @@ -1 +1 @@ -uint_module!(u32, u32); +uint_module!(u32); diff --git a/library/core/tests/num/u64.rs b/library/core/tests/num/u64.rs index b498ebc5204..ee55071e949 100644 --- a/library/core/tests/num/u64.rs +++ b/library/core/tests/num/u64.rs @@ -1 +1 @@ -uint_module!(u64, u64); +uint_module!(u64); diff --git a/library/core/tests/num/u8.rs b/library/core/tests/num/u8.rs index 68e938be704..12b038ce0f7 100644 --- a/library/core/tests/num/u8.rs +++ b/library/core/tests/num/u8.rs @@ -1 +1 @@ -uint_module!(u8, u8); +uint_module!(u8); diff --git a/library/core/tests/num/uint_macros.rs b/library/core/tests/num/uint_macros.rs index 49f8f1f13fa..93ae620c233 100644 --- a/library/core/tests/num/uint_macros.rs +++ b/library/core/tests/num/uint_macros.rs @@ -1,9 +1,9 @@ macro_rules! uint_module { - ($T:ident, $T_i:ident) => { + ($T:ident) => { #[cfg(test)] mod tests { use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; - use core::$T_i::*; + use core::$T::*; use std::str::FromStr; use crate::num; diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index f1c5eaad868..6f7c6305afc 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -703,11 +703,12 @@ pub enum Delimiter { /// `[ ... ]` #[stable(feature = "proc_macro_lib2", since = "1.29.0")] Bracket, - /// `Ø ... Ø` + /// `/*«*/ ... /*»*/` /// An invisible delimiter, that may, for example, appear around tokens coming from a /// "macro variable" `$var`. It is important to preserve operator priorities in cases like /// `$var * 3` where `$var` is `1 + 2`. - /// Invisible delimiters might not survive roundtrip of a token stream through a string. + /// Invisible delimiters are not directly writable in normal Rust code except as comments. + /// Therefore, they might not survive a roundtrip of a token stream through a string. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] None, } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index f2d65832060..d0d7d480fe8 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -15,7 +15,7 @@ cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } -libc = { version = "0.2.116", default-features = false, features = ['rustc-dep-of-std'] } +libc = { version = "0.2.125", default-features = false, features = ['rustc-dep-of-std'] } compiler_builtins = { version = "0.1.71" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 49b6cd4232c..63c527b64da 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -42,8 +42,6 @@ //! [`GlobalAlloc`] trait. This type can be provided by an external library: //! //! ```rust,ignore (demonstrates crates.io usage) -//! extern crate jemallocator; -//! //! use jemallocator::Jemalloc; //! //! #[global_allocator] diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 0c638192264..e38368790e6 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -3006,12 +3006,20 @@ impl Default for DefaultHasher { #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] impl Hasher for DefaultHasher { + // The underlying `SipHasher13` doesn't override the other + // `write_*` methods, so it's ok not to forward them here. + #[inline] fn write(&mut self, msg: &[u8]) { self.0.write(msg) } #[inline] + fn write_str(&mut self, s: &str) { + self.0.write_str(s); + } + + #[inline] fn finish(&self) -> u64 { self.0.finish() } diff --git a/library/std/src/env.rs b/library/std/src/env.rs index f03d298d869..e287a93da7b 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -644,36 +644,23 @@ pub fn temp_dir() -> PathBuf { /// /// # Security /// -/// The output of this function should not be used in anything that might have -/// security implications. For example: -/// -/// ``` -/// fn main() { -/// println!("{:?}", std::env::current_exe()); -/// } -/// ``` -/// -/// On Linux systems, if this is compiled as `foo`: -/// -/// ```bash -/// $ rustc foo.rs -/// $ ./foo -/// Ok("/home/alex/foo") -/// ``` -/// -/// And you make a hard link of the program: -/// -/// ```bash -/// $ ln foo bar -/// ``` -/// -/// When you run it, you won’t get the path of the original executable, you’ll -/// get the path of the hard link: -/// -/// ```bash -/// $ ./bar -/// Ok("/home/alex/bar") -/// ``` +/// The output of this function should not be trusted for anything +/// that might have security implications. Basically, if users can run +/// the executable, they can change the output arbitrarily. +/// +/// As an example, you can easily introduce a race condition. It goes +/// like this: +/// +/// 1. You get the path to the current executable using `current_exe()`, and +/// store it in a variable. +/// 2. Time passes. A malicious actor removes the current executable, and +/// replaces it with a malicious one. +/// 3. You then use the stored path to re-execute the current +/// executable. +/// +/// You expected to safely execute the current executable, but you're +/// instead executing something completely different. The code you +/// just executed run with your privileges. /// /// This sort of behavior has been known to [lead to privilege escalation] when /// used incorrectly. diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 9b5e5d6c0cc..dd316bdb2c6 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -1222,6 +1222,23 @@ impl OsStr { } } +#[unstable(feature = "slice_concat_ext", issue = "27747")] +impl<S: Borrow<OsStr>> alloc::slice::Join<&OsStr> for [S] { + type Output = OsString; + + fn join(slice: &Self, sep: &OsStr) -> OsString { + let Some(first) = slice.first() else { + return OsString::new(); + }; + let first = first.borrow().to_owned(); + slice[1..].iter().fold(first, |mut a, b| { + a.push(sep); + a.push(b.borrow()); + a + }) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Borrow<OsStr> for OsString { #[inline] diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index 283f2b577e8..d7926749aae 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -85,6 +85,20 @@ fn test_os_string_reserve_exact() { } #[test] +fn test_os_string_join() { + let strings = [OsStr::new("hello"), OsStr::new("dear"), OsStr::new("world")]; + assert_eq!("hello", strings[..1].join(OsStr::new(" "))); + assert_eq!("hello dear world", strings.join(OsStr::new(" "))); + assert_eq!("hellodearworld", strings.join(OsStr::new(""))); + assert_eq!("hello.\n dear.\n world", strings.join(OsStr::new(".\n "))); + + assert_eq!("dear world", strings[1..].join(&OsString::from(" "))); + + let strings_abc = [OsString::from("a"), OsString::from("b"), OsString::from("c")]; + assert_eq!("a b c", strings_abc.join(OsStr::new(" "))); +} + +#[test] fn test_os_string_default() { let os_string: OsString = Default::default(); assert_eq!("", &os_string); diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 433b4d53013..369a251696e 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1930,6 +1930,8 @@ pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { /// Ok(()) /// } /// ``` +#[doc(alias = "realpath")] +#[doc(alias = "GetFinalPathNameByHandle")] #[stable(feature = "fs_canonicalize", since = "1.5.0")] pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { fs_imp::canonicalize(path.as_ref()) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 97c30c42282..c394865d886 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -241,6 +241,7 @@ #![feature(intra_doc_pointers)] #![feature(lang_items)] #![feature(let_chains)] +#![feature(let_else)] #![feature(linkage)] #![feature(min_specialization)] #![feature(must_not_suspend)] @@ -270,6 +271,7 @@ #![feature(exact_size_is_empty)] #![feature(extend_one)] #![feature(float_minimum_maximum)] +#![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(int_error_internals)] #![feature(maybe_uninit_slice)] @@ -300,6 +302,7 @@ #![feature(toowned_clone_into)] #![feature(try_reserve_kind)] #![feature(vec_into_raw_parts)] +#![feature(slice_concat_trait)] // // Library features (unwind): #![feature(panic_unwind)] diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index 036f2919976..f629a1a0f99 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -138,6 +138,7 @@ pub struct Ipv4Addr { /// /// To convert from an IPv4 address to an IPv4-mapped IPv6 address, use [`Ipv4Addr::to_ipv6_mapped`]. /// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-mapped IPv6 address to the canonical IPv4 address. +/// Note that this will also convert the IPv6 loopback address `::1` to `0.0.0.1`. /// /// [IETF RFC 4291 Section 2.5.5.2]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2 /// @@ -1673,7 +1674,9 @@ impl Ipv6Addr { /// or an [IPv4-mapped] address as defined in [IETF RFC 4291 section 2.5.5.2], /// otherwise returns [`None`]. /// - /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d` + /// Note that this will return an [`IPv4` address] for the IPv6 loopback address `::1`. + /// + /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`. `::1` becomes `0.0.0.1`. /// All addresses *not* starting with either all zeroes or `::ffff` will return `None`. /// /// [`IPv4` address]: Ipv4Addr diff --git a/library/std/src/os/linux/fs.rs b/library/std/src/os/linux/fs.rs index 9d18ccbeb24..23267c44685 100644 --- a/library/std/src/os/linux/fs.rs +++ b/library/std/src/os/linux/fs.rs @@ -356,19 +356,34 @@ impl MetadataExt for Metadata { self.as_inner().as_inner().st_size as u64 } fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 + let file_attr = self.as_inner(); + #[cfg(all(target_env = "gnu", target_pointer_width = "32"))] + if let Some(atime) = file_attr.stx_atime() { + return atime.tv_sec; + } + file_attr.as_inner().st_atime as i64 } fn st_atime_nsec(&self) -> i64 { self.as_inner().as_inner().st_atime_nsec as i64 } fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 + let file_attr = self.as_inner(); + #[cfg(all(target_env = "gnu", target_pointer_width = "32"))] + if let Some(mtime) = file_attr.stx_mtime() { + return mtime.tv_sec; + } + file_attr.as_inner().st_mtime as i64 } fn st_mtime_nsec(&self) -> i64 { self.as_inner().as_inner().st_mtime_nsec as i64 } fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 + let file_attr = self.as_inner(); + #[cfg(all(target_env = "gnu", target_pointer_width = "32"))] + if let Some(ctime) = file_attr.stx_ctime() { + return ctime.tv_sec; + } + file_attr.as_inner().st_ctime as i64 } fn st_ctime_nsec(&self) -> i64 { self.as_inner().as_inner().st_ctime_nsec as i64 diff --git a/library/std/src/sync/condvar/tests.rs b/library/std/src/sync/condvar/tests.rs index f7a00676daa..24f467f0b03 100644 --- a/library/std/src/sync/condvar/tests.rs +++ b/library/std/src/sync/condvar/tests.rs @@ -188,24 +188,3 @@ fn wait_timeout_wake() { break; } } - -#[test] -#[should_panic] -#[cfg(all(unix, not(target_os = "linux"), not(target_os = "android")))] -fn two_mutexes() { - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - - let mut g = m.lock().unwrap(); - let _t = thread::spawn(move || { - let _g = m2.lock().unwrap(); - c2.notify_one(); - }); - g = c.wait(g).unwrap(); - drop(g); - - let m = Mutex::new(()); - let _ = c.wait(m.lock().unwrap()).unwrap(); -} diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index 3ea0a6c3937..aacc893ba06 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -192,6 +192,7 @@ unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {} points can cause deadlocks, delays, \ and cause Futures to not implement `Send`"] #[stable(feature = "rust1", since = "1.0.0")] +#[clippy::has_significant_drop] pub struct MutexGuard<'a, T: ?Sized + 'a> { lock: &'a Mutex<T>, poison: poison::Guard, diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index ed62fa977be..9ec0903f037 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -99,6 +99,7 @@ unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {} points can cause deadlocks, delays, \ and cause Futures to not implement `Send`"] #[stable(feature = "rust1", since = "1.0.0")] +#[clippy::has_significant_drop] pub struct RwLockReadGuard<'a, T: ?Sized + 'a> { lock: &'a RwLock<T>, } @@ -122,6 +123,7 @@ unsafe impl<T: ?Sized + Sync> Sync for RwLockReadGuard<'_, T> {} points can cause deadlocks, delays, \ and cause Future's to not implement `Send`"] #[stable(feature = "rust1", since = "1.0.0")] +#[clippy::has_significant_drop] pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> { lock: &'a RwLock<T>, poison: poison::Guard, diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 27fc7accdae..61fb2814018 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -113,10 +113,19 @@ cfg_has_statx! {{ // This is needed to check if btime is supported by the filesystem. stx_mask: u32, stx_btime: libc::statx_timestamp, + // With statx, we can overcome 32-bit `time_t` too. + #[cfg(target_pointer_width = "32")] + stx_atime: libc::statx_timestamp, + #[cfg(target_pointer_width = "32")] + stx_ctime: libc::statx_timestamp, + #[cfg(target_pointer_width = "32")] + stx_mtime: libc::statx_timestamp, + } - // We prefer `statx` on Linux if available, which contains file creation time. - // Default `stat64` contains no creation time. + // We prefer `statx` on Linux if available, which contains file creation time, + // as well as 64-bit timestamps of all kinds. + // Default `stat64` contains no creation time and may have 32-bit `time_t`. unsafe fn try_statx( fd: c_int, path: *const c_char, @@ -192,6 +201,13 @@ cfg_has_statx! {{ let extra = StatxExtraFields { stx_mask: buf.stx_mask, stx_btime: buf.stx_btime, + // Store full times to avoid 32-bit `time_t` truncation. + #[cfg(target_pointer_width = "32")] + stx_atime: buf.stx_atime, + #[cfg(target_pointer_width = "32")] + stx_ctime: buf.stx_ctime, + #[cfg(target_pointer_width = "32")] + stx_mtime: buf.stx_mtime, }; Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) })) @@ -310,6 +326,36 @@ cfg_has_statx! {{ fn from_stat64(stat: stat64) -> Self { Self { stat, statx_extra_fields: None } } + + #[cfg(target_pointer_width = "32")] + pub fn stx_mtime(&self) -> Option<&libc::statx_timestamp> { + if let Some(ext) = &self.statx_extra_fields { + if (ext.stx_mask & libc::STATX_MTIME) != 0 { + return Some(&ext.stx_mtime); + } + } + None + } + + #[cfg(target_pointer_width = "32")] + pub fn stx_atime(&self) -> Option<&libc::statx_timestamp> { + if let Some(ext) = &self.statx_extra_fields { + if (ext.stx_mask & libc::STATX_ATIME) != 0 { + return Some(&ext.stx_atime); + } + } + None + } + + #[cfg(target_pointer_width = "32")] + pub fn stx_ctime(&self) -> Option<&libc::statx_timestamp> { + if let Some(ext) = &self.statx_extra_fields { + if (ext.stx_mask & libc::STATX_CTIME) != 0 { + return Some(&ext.stx_ctime); + } + } + None + } } } else { impl FileAttr { @@ -335,24 +381,15 @@ impl FileAttr { #[cfg(target_os = "netbsd")] impl FileAttr { pub fn modified(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_mtime as libc::time_t, - tv_nsec: self.stat.st_mtimensec as libc::c_long, - })) + Ok(SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64)) } pub fn accessed(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_atime as libc::time_t, - tv_nsec: self.stat.st_atimensec as libc::c_long, - })) + Ok(SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64)) } pub fn created(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_birthtime as libc::time_t, - tv_nsec: self.stat.st_birthtimensec as libc::c_long, - })) + Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64)) } } @@ -360,34 +397,36 @@ impl FileAttr { impl FileAttr { #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))] pub fn modified(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_mtime as libc::time_t, - tv_nsec: self.stat.st_mtime_nsec as _, - })) + #[cfg(target_pointer_width = "32")] + cfg_has_statx! { + if let Some(mtime) = self.stx_mtime() { + return Ok(SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64)); + } + } + + Ok(SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64)) } #[cfg(any(target_os = "vxworks", target_os = "espidf"))] pub fn modified(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_mtime as libc::time_t, - tv_nsec: 0, - })) + Ok(SystemTime::new(self.stat.st_mtime as i64, 0)) } #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))] pub fn accessed(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_atime as libc::time_t, - tv_nsec: self.stat.st_atime_nsec as _, - })) + #[cfg(target_pointer_width = "32")] + cfg_has_statx! { + if let Some(atime) = self.stx_atime() { + return Ok(SystemTime::new(atime.tv_sec, atime.tv_nsec as i64)); + } + } + + Ok(SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64)) } #[cfg(any(target_os = "vxworks", target_os = "espidf"))] pub fn accessed(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_atime as libc::time_t, - tv_nsec: 0, - })) + Ok(SystemTime::new(self.stat.st_atime as i64, 0)) } #[cfg(any( @@ -397,10 +436,7 @@ impl FileAttr { target_os = "ios" ))] pub fn created(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_birthtime as libc::time_t, - tv_nsec: self.stat.st_birthtime_nsec as libc::c_long, - })) + Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64)) } #[cfg(not(any( @@ -413,10 +449,7 @@ impl FileAttr { cfg_has_statx! { if let Some(ext) = &self.statx_extra_fields { return if (ext.stx_mask & libc::STATX_BTIME) != 0 { - Ok(SystemTime::from(libc::timespec { - tv_sec: ext.stx_btime.tv_sec as libc::time_t, - tv_nsec: ext.stx_btime.tv_nsec as _, - })) + Ok(SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64)) } else { Err(io::const_io_error!( io::ErrorKind::Uncategorized, diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs index c12ee169e79..c1966d67078 100644 --- a/library/std/src/sys/unix/futex.rs +++ b/library/std/src/sys/unix/futex.rs @@ -1,7 +1,10 @@ #![cfg(any( target_os = "linux", target_os = "android", - all(target_os = "emscripten", target_feature = "atomics") + all(target_os = "emscripten", target_feature = "atomics"), + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", ))] use crate::sync::atomic::AtomicU32; @@ -12,7 +15,7 @@ use crate::time::Duration; /// Returns directly if the futex doesn't hold the expected value. /// /// Returns false on timeout, and true in all other cases. -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { use super::time::Timespec; use crate::ptr::null; @@ -21,8 +24,9 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - // Calculate the timeout as an absolute timespec. // // Overflows are rounded up to an infinite timeout (None). - let timespec = - timeout.and_then(|d| Some(Timespec::now(libc::CLOCK_MONOTONIC).checked_add_duration(&d)?)); + let timespec = timeout + .and_then(|d| Some(Timespec::now(libc::CLOCK_MONOTONIC).checked_add_duration(&d)?)) + .and_then(|t| t.to_timespec()); loop { // No need to wait if the value already changed. @@ -30,18 +34,43 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - return true; } - // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an - // absolute time rather than a relative time. let r = unsafe { - libc::syscall( - libc::SYS_futex, - futex as *const AtomicU32, - libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG, - expected, - timespec.as_ref().map_or(null(), |t| &t.t as *const libc::timespec), - null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET. - !0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT. - ) + cfg_if::cfg_if! { + if #[cfg(target_os = "freebsd")] { + // FreeBSD doesn't have futex(), but it has + // _umtx_op(UMTX_OP_WAIT_UINT_PRIVATE), which is nearly + // identical. It supports absolute timeouts through a flag + // in the _umtx_time struct. + let umtx_timeout = timespec.map(|t| libc::_umtx_time { + _timeout: t, + _flags: libc::UMTX_ABSTIME, + _clockid: libc::CLOCK_MONOTONIC as u32, + }); + let umtx_timeout_ptr = umtx_timeout.as_ref().map_or(null(), |t| t as *const _); + let umtx_timeout_size = umtx_timeout.as_ref().map_or(0, |t| crate::mem::size_of_val(t)); + libc::_umtx_op( + futex as *const AtomicU32 as *mut _, + libc::UMTX_OP_WAIT_UINT_PRIVATE, + expected as libc::c_ulong, + crate::ptr::invalid_mut(umtx_timeout_size), + umtx_timeout_ptr as *mut _, + ) + } else if #[cfg(any(target_os = "linux", target_os = "android"))] { + // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an + // absolute time rather than a relative time. + libc::syscall( + libc::SYS_futex, + futex as *const AtomicU32, + libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG, + expected, + timespec.as_ref().map_or(null(), |t| t as *const libc::timespec), + null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET. + !0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT. + ) + } else { + compile_error!("unknown target_os"); + } + } }; match (r < 0).then(super::os::errno) { @@ -56,31 +85,133 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - /// /// Returns true if this actually woke up such a thread, /// or false if no thread was waiting on this futex. +/// +/// On some platforms, this always returns false. +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn futex_wake(futex: &AtomicU32) -> bool { + let ptr = futex as *const AtomicU32; + let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG; + unsafe { libc::syscall(libc::SYS_futex, ptr, op, 1) > 0 } +} + +/// Wake up all threads that are waiting on futex_wait on this futex. #[cfg(any(target_os = "linux", target_os = "android"))] +pub fn futex_wake_all(futex: &AtomicU32) { + let ptr = futex as *const AtomicU32; + let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG; + unsafe { + libc::syscall(libc::SYS_futex, ptr, op, i32::MAX); + } +} + +// FreeBSD doesn't tell us how many threads are woken up, so this always returns false. +#[cfg(target_os = "freebsd")] pub fn futex_wake(futex: &AtomicU32) -> bool { + use crate::ptr::null_mut; unsafe { - libc::syscall( - libc::SYS_futex, - futex as *const AtomicU32, - libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG, + libc::_umtx_op( + futex as *const AtomicU32 as *mut _, + libc::UMTX_OP_WAKE_PRIVATE, 1, - ) > 0 + null_mut(), + null_mut(), + ) + }; + false +} + +#[cfg(target_os = "freebsd")] +pub fn futex_wake_all(futex: &AtomicU32) { + use crate::ptr::null_mut; + unsafe { + libc::_umtx_op( + futex as *const AtomicU32 as *mut _, + libc::UMTX_OP_WAKE_PRIVATE, + i32::MAX as libc::c_ulong, + null_mut(), + null_mut(), + ) + }; +} + +#[cfg(target_os = "openbsd")] +pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { + use crate::convert::TryInto; + use crate::ptr::{null, null_mut}; + let timespec = timeout.and_then(|d| { + Some(libc::timespec { + // Sleep forever if the timeout is longer than fits in a timespec. + tv_sec: d.as_secs().try_into().ok()?, + // This conversion never truncates, as subsec_nanos is always <1e9. + tv_nsec: d.subsec_nanos() as _, + }) + }); + + let r = unsafe { + libc::futex( + futex as *const AtomicU32 as *mut u32, + libc::FUTEX_WAIT, + expected as i32, + timespec.as_ref().map_or(null(), |t| t as *const libc::timespec), + null_mut(), + ) + }; + + r == 0 || super::os::errno() != libc::ETIMEDOUT +} + +#[cfg(target_os = "openbsd")] +pub fn futex_wake(futex: &AtomicU32) -> bool { + use crate::ptr::{null, null_mut}; + unsafe { + libc::futex(futex as *const AtomicU32 as *mut u32, libc::FUTEX_WAKE, 1, null(), null_mut()) + > 0 } } -/// Wake up all threads that are waiting on futex_wait on this futex. -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(target_os = "openbsd")] pub fn futex_wake_all(futex: &AtomicU32) { + use crate::ptr::{null, null_mut}; unsafe { - libc::syscall( - libc::SYS_futex, - futex as *const AtomicU32, - libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG, + libc::futex( + futex as *const AtomicU32 as *mut u32, + libc::FUTEX_WAKE, i32::MAX, + null(), + null_mut(), ); } } +#[cfg(target_os = "dragonfly")] +pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { + use crate::convert::TryFrom; + + // A timeout of 0 means infinite. + // We round smaller timeouts up to 1 millisecond. + // Overflows are rounded up to an infinite timeout. + let timeout_ms = + timeout.and_then(|d| Some(i32::try_from(d.as_millis()).ok()?.max(1))).unwrap_or(0); + + let r = unsafe { + libc::umtx_sleep(futex as *const AtomicU32 as *const i32, expected as i32, timeout_ms) + }; + + r == 0 || super::os::errno() != libc::ETIMEDOUT +} + +// DragonflyBSD doesn't tell us how many threads are woken up, so this always returns false. +#[cfg(target_os = "dragonfly")] +pub fn futex_wake(futex: &AtomicU32) -> bool { + unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, 1) }; + false +} + +#[cfg(target_os = "dragonfly")] +pub fn futex_wake_all(futex: &AtomicU32) { + unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, i32::MAX) }; +} + #[cfg(target_os = "emscripten")] extern "C" { fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int; diff --git a/library/std/src/sys/unix/locks/futex_rwlock.rs b/library/std/src/sys/unix/locks/futex_rwlock.rs index e42edb25858..57f6d58840d 100644 --- a/library/std/src/sys/unix/locks/futex_rwlock.rs +++ b/library/std/src/sys/unix/locks/futex_rwlock.rs @@ -284,6 +284,10 @@ impl RwLock { fn wake_writer(&self) -> bool { self.writer_notify.fetch_add(1, Release); futex_wake(&self.writer_notify) + // Note that FreeBSD and DragonFlyBSD don't tell us whether they woke + // up any threads or not, and always return `false` here. That still + // results in correct behaviour: it just means readers get woken up as + // well in case both readers and writers were waiting. } /// Spin for a while, but stop directly at the given condition. diff --git a/library/std/src/sys/unix/locks/mod.rs b/library/std/src/sys/unix/locks/mod.rs index 3e39c8b9b23..04c5c489fc9 100644 --- a/library/std/src/sys/unix/locks/mod.rs +++ b/library/std/src/sys/unix/locks/mod.rs @@ -3,6 +3,9 @@ cfg_if::cfg_if! { target_os = "linux", target_os = "android", all(target_os = "emscripten", target_feature = "atomics"), + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", ))] { mod futex; mod futex_rwlock; diff --git a/library/std/src/sys/unix/thread_parker.rs b/library/std/src/sys/unix/thread_parker.rs index fd83f2f73d6..30ed2ec7f54 100644 --- a/library/std/src/sys/unix/thread_parker.rs +++ b/library/std/src/sys/unix/thread_parker.rs @@ -3,7 +3,10 @@ #![cfg(not(any( target_os = "linux", target_os = "android", - all(target_os = "emscripten", target_feature = "atomics") + all(target_os = "emscripten", target_feature = "atomics"), + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", )))] use crate::cell::UnsafeCell; @@ -76,7 +79,8 @@ unsafe fn wait_timeout( (Timespec::now(libc::CLOCK_MONOTONIC), dur) }; - let timeout = now.checked_add_duration(&dur).map(|t| t.t).unwrap_or(TIMESPEC_MAX); + let timeout = + now.checked_add_duration(&dur).and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX); let r = libc::pthread_cond_timedwait(cond, lock, &timeout); debug_assert!(r == libc::ETIMEDOUT || r == 0); } diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index d43ceec9c8a..ac8355188bb 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -1,21 +1,63 @@ -use crate::cmp::Ordering; +use crate::fmt; use crate::time::Duration; -use core::hash::{Hash, Hasher}; - -pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; +pub use self::inner::Instant; use crate::convert::TryInto; const NSEC_PER_SEC: u64 = 1_000_000_000; +pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SystemTime { + pub(in crate::sys::unix) t: Timespec, +} -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(in crate::sys::unix) struct Timespec { - pub t: libc::timespec, + tv_sec: i64, + tv_nsec: i64, +} + +impl SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + SystemTime { t: Timespec::new(tv_sec, tv_nsec) } + } + + pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { + self.t.sub_timespec(&other.t) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) + } +} + +impl From<libc::timespec> for SystemTime { + fn from(t: libc::timespec) -> SystemTime { + SystemTime { t: Timespec::from(t) } + } +} + +impl fmt::Debug for SystemTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SystemTime") + .field("tv_sec", &self.t.tv_sec) + .field("tv_nsec", &self.t.tv_nsec) + .finish() + } } impl Timespec { const fn zero() -> Timespec { - Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } } + Timespec { tv_sec: 0, tv_nsec: 0 } + } + + fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + Timespec { tv_sec, tv_nsec } } pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { @@ -23,22 +65,22 @@ impl Timespec { // NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM // to optimize it into a branchless form (see also #75545): // - // 1. `self.t.tv_sec - other.t.tv_sec` shows up as a common expression + // 1. `self.tv_sec - other.tv_sec` shows up as a common expression // in both branches, i.e. the `else` must have its `- 1` // subtraction after the common one, not interleaved with it - // (it used to be `self.t.tv_sec - 1 - other.t.tv_sec`) + // (it used to be `self.tv_sec - 1 - other.tv_sec`) // // 2. the `Duration::new` call (or any other additional complexity) // is outside of the `if`-`else`, not duplicated in both branches // // Ideally this code could be rearranged such that it more // directly expresses the lower-cost behavior we want from it. - let (secs, nsec) = if self.t.tv_nsec >= other.t.tv_nsec { - ((self.t.tv_sec - other.t.tv_sec) as u64, (self.t.tv_nsec - other.t.tv_nsec) as u32) + let (secs, nsec) = if self.tv_nsec >= other.tv_nsec { + ((self.tv_sec - other.tv_sec) as u64, (self.tv_nsec - other.tv_nsec) as u32) } else { ( - (self.t.tv_sec - other.t.tv_sec - 1) as u64, - self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32, + (self.tv_sec - other.tv_sec - 1) as u64, + self.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.tv_nsec as u32, ) }; @@ -54,89 +96,65 @@ impl Timespec { pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { let mut secs = other .as_secs() - .try_into() // <- target type would be `libc::time_t` + .try_into() // <- target type would be `i64` .ok() - .and_then(|secs| self.t.tv_sec.checked_add(secs))?; + .and_then(|secs| self.tv_sec.checked_add(secs))?; // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; + let mut nsec = other.subsec_nanos() + self.tv_nsec as u32; if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; secs = secs.checked_add(1)?; } - Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) + Some(Timespec::new(secs, nsec as i64)) } pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { let mut secs = other .as_secs() - .try_into() // <- target type would be `libc::time_t` + .try_into() // <- target type would be `i64` .ok() - .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; + .and_then(|secs| self.tv_sec.checked_sub(secs))?; // Similar to above, nanos can't overflow. - let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; + let mut nsec = self.tv_nsec as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; secs = secs.checked_sub(1)?; } - Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) - } -} - -impl PartialEq for Timespec { - fn eq(&self, other: &Timespec) -> bool { - self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec - } -} - -impl Eq for Timespec {} - -impl PartialOrd for Timespec { - fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> { - Some(self.cmp(other)) + Some(Timespec::new(secs, nsec as i64)) } -} -impl Ord for Timespec { - fn cmp(&self, other: &Timespec) -> Ordering { - let me = (self.t.tv_sec, self.t.tv_nsec); - let other = (other.t.tv_sec, other.t.tv_nsec); - me.cmp(&other) + pub fn to_timespec(&self) -> Option<libc::timespec> { + use crate::convert::TryInto; + Some(libc::timespec { + tv_sec: self.tv_sec.try_into().ok()?, + tv_nsec: self.tv_nsec.try_into().ok()?, + }) } } -impl Hash for Timespec { - fn hash<H: Hasher>(&self, state: &mut H) { - self.t.tv_sec.hash(state); - self.t.tv_nsec.hash(state); +impl From<libc::timespec> for Timespec { + fn from(t: libc::timespec) -> Timespec { + Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) } } #[cfg(any(target_os = "macos", target_os = "ios"))] mod inner { - use crate::fmt; use crate::sync::atomic::{AtomicU64, Ordering}; use crate::sys::cvt; use crate::sys_common::mul_div_u64; use crate::time::Duration; - use super::Timespec; - use super::NSEC_PER_SEC; + use super::{SystemTime, Timespec, NSEC_PER_SEC}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct Instant { t: u64, } - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct SystemTime { - pub(in crate::sys::unix) t: Timespec, - } - - pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; - #[repr(C)] #[derive(Copy, Clone)] struct mach_timebase_info { @@ -178,41 +196,17 @@ mod inner { cvt(unsafe { libc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap(); return SystemTime::from(s); } + } - pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { - self.t.sub_timespec(&other.t) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - Some(SystemTime { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { - Some(SystemTime { t: self.t.checked_sub_duration(other)? }) + impl From<libc::timeval> for Timespec { + fn from(t: libc::timeval) -> Timespec { + Timespec::new(t.tv_sec as i64, 1000 * t.tv_usec as i64) } } impl From<libc::timeval> for SystemTime { fn from(t: libc::timeval) -> SystemTime { - SystemTime::from(libc::timespec { - tv_sec: t.tv_sec, - tv_nsec: (t.tv_usec * 1000) as libc::c_long, - }) - } - } - - impl From<libc::timespec> for SystemTime { - fn from(t: libc::timespec) -> SystemTime { - SystemTime { t: Timespec { t } } - } - } - - impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SystemTime") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) - .finish() + SystemTime { t: Timespec::from(t) } } } @@ -270,20 +264,13 @@ mod inner { use crate::sys::cvt; use crate::time::Duration; - use super::Timespec; + use super::{SystemTime, Timespec}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Instant { t: Timespec, } - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct SystemTime { - pub(in crate::sys::unix) t: Timespec, - } - - pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; - impl Instant { pub fn now() -> Instant { Instant { t: Timespec::now(libc::CLOCK_MONOTONIC) } @@ -305,8 +292,8 @@ mod inner { impl fmt::Debug for Instant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Instant") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) + .field("tv_sec", &self.t.tv_sec) + .field("tv_nsec", &self.t.tv_nsec) .finish() } } @@ -315,33 +302,6 @@ mod inner { pub fn now() -> SystemTime { SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) } } - - pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { - self.t.sub_timespec(&other.t) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - Some(SystemTime { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { - Some(SystemTime { t: self.t.checked_sub_duration(other)? }) - } - } - - impl From<libc::timespec> for SystemTime { - fn from(t: libc::timespec) -> SystemTime { - SystemTime { t: Timespec { t } } - } - } - - impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SystemTime") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) - .finish() - } } #[cfg(not(any(target_os = "dragonfly", target_os = "espidf")))] @@ -351,9 +311,36 @@ mod inner { impl Timespec { pub fn now(clock: clock_t) -> Timespec { + // Try to use 64-bit time in preparation for Y2038. + #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32"))] + { + use crate::sys::weak::weak; + + // __clock_gettime64 was added to 32-bit arches in glibc 2.34, + // and it handles both vDSO calls and ENOSYS fallbacks itself. + weak!(fn __clock_gettime64(libc::clockid_t, *mut __timespec64) -> libc::c_int); + + #[repr(C)] + struct __timespec64 { + tv_sec: i64, + #[cfg(target_endian = "big")] + _padding: i32, + tv_nsec: i32, + #[cfg(target_endian = "little")] + _padding: i32, + } + + if let Some(clock_gettime64) = __clock_gettime64.get() { + let mut t = MaybeUninit::uninit(); + cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); + let t = unsafe { t.assume_init() }; + return Timespec { tv_sec: t.tv_sec, tv_nsec: t.tv_nsec as i64 }; + } + } + let mut t = MaybeUninit::uninit(); cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); - Timespec { t: unsafe { t.assume_init() } } + Timespec::from(unsafe { t.assume_init() }) } } } diff --git a/library/std/src/sys_common/condvar/check.rs b/library/std/src/sys_common/condvar/check.rs index 7671850ac55..d0d0d596518 100644 --- a/library/std/src/sys_common/condvar/check.rs +++ b/library/std/src/sys_common/condvar/check.rs @@ -24,8 +24,14 @@ impl SameMutexCheck { } pub fn verify(&self, mutex: &MovableMutex) { let addr = mutex.raw() as *const imp::Mutex as *const () as *mut _; - match self.addr.compare_exchange(ptr::null_mut(), addr, Ordering::SeqCst, Ordering::SeqCst) - { + // Relaxed is okay here because we never read through `self.addr`, and only use it to + // compare addresses. + match self.addr.compare_exchange( + ptr::null_mut(), + addr, + Ordering::Relaxed, + Ordering::Relaxed, + ) { Ok(_) => {} // Stored the address Err(n) if n == addr => {} // Lost a race to store the same address _ => panic!("attempted to use a condition variable with two mutexes"), diff --git a/library/std/src/sys_common/thread.rs b/library/std/src/sys_common/thread.rs index f3a8bef8f71..76466b2b37b 100644 --- a/library/std/src/sys_common/thread.rs +++ b/library/std/src/sys_common/thread.rs @@ -4,7 +4,7 @@ use crate::sys::thread as imp; pub fn min_stack() -> usize { static MIN: atomic::AtomicUsize = atomic::AtomicUsize::new(0); - match MIN.load(Ordering::SeqCst) { + match MIN.load(Ordering::Relaxed) { 0 => {} n => return n - 1, } @@ -13,6 +13,6 @@ pub fn min_stack() -> usize { // 0 is our sentinel value, so ensure that we'll never see 0 after // initialization has run - MIN.store(amt + 1, Ordering::SeqCst); + MIN.store(amt + 1, Ordering::Relaxed); amt } diff --git a/library/std/src/sys_common/thread_parker/mod.rs b/library/std/src/sys_common/thread_parker/mod.rs index ea0204cd357..c789a388e05 100644 --- a/library/std/src/sys_common/thread_parker/mod.rs +++ b/library/std/src/sys_common/thread_parker/mod.rs @@ -3,6 +3,9 @@ cfg_if::cfg_if! { target_os = "linux", target_os = "android", all(target_arch = "wasm32", target_feature = "atomics"), + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", ))] { mod futex; pub use futex::Parker; diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index e38a574ca23..ab4338e1c85 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -998,6 +998,10 @@ class RustBuild(object): "library/backtrace", "library/stdarch" ] + # If build.vendor is set in config.toml, we must update rust-analyzer also. + # Otherwise, the bootstrap will fail (#96456). + if self.use_vendored_sources: + submodules.append("src/tools/rust-analyzer") filtered_submodules = [] submodules_names = [] for module in submodules: diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index edfe31319e8..bc5af79e0c6 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -284,7 +284,19 @@ impl StepDescription { } if !attempted_run { - panic!("error: no rules matched {}", path.display()); + eprintln!( + "error: no `{}` rules matched '{}'", + builder.kind.as_str(), + path.display() + ); + eprintln!( + "help: run `x.py {} --help --verbose` to show a list of available paths", + builder.kind.as_str() + ); + eprintln!( + "note: if you are adding a new Step to bootstrap itself, make sure you register it with `describe!`" + ); + std::process::exit(1); } } } @@ -1131,13 +1143,22 @@ impl<'a> Builder<'a> { rustflags.arg("-Zunstable-options"); } - // #[cfg(not(bootstrap)] + // FIXME(Urgau): This a hack as it shouldn't be gated on stage 0 but until `rustc_llvm` + // is made to work with `--check-cfg` which is currently not easly possible until cargo + // get some support for setting `--check-cfg` within build script, it's the least invasive + // hack that still let's us have cfg checking for the vast majority of the codebase. if stage != 0 { - // Enable cfg checking of cargo features - // FIXME: De-comment this when cargo beta get support for it - // cargo.arg("-Zcheck-cfg-features"); + // Enable cfg checking of cargo features for everything but std. + // + // Note: `std`, `alloc` and `core` imports some dependencies by #[path] (like + // backtrace, core_simd, std_float, ...), those dependencies have their own features + // but cargo isn't involved in the #[path] and so cannot pass the complete list of + // features, so for that reason we don't enable checking of features for std. + if mode != Mode::Std { + cargo.arg("-Zcheck-cfg-features"); + } - // Enable cfg checking of rustc well-known names + // Enable cfg checking of well known names/values rustflags .arg("-Zunstable-options") // Enable checking of well known names @@ -1405,7 +1426,12 @@ impl<'a> Builder<'a> { // FIXME(davidtwco): #[cfg(not(bootstrap))] - #95612 needs to be in the bootstrap compiler // for this conditional to be removed. if !target.contains("windows") || compiler.stage >= 1 { - if target.contains("linux") || target.contains("windows") { + let needs_unstable_opts = target.contains("linux") + || target.contains("windows") + || target.contains("bsd") + || target.contains("dragonfly"); + + if needs_unstable_opts { rustflags.arg("-Zunstable-options"); } match self.config.rust_split_debuginfo { diff --git a/src/bootstrap/defaults/config.codegen.toml b/src/bootstrap/defaults/config.codegen.toml index 011ff6821b7..088cbd1057e 100644 --- a/src/bootstrap/defaults/config.codegen.toml +++ b/src/bootstrap/defaults/config.codegen.toml @@ -1,4 +1,8 @@ # These defaults are meant for contributors to the compiler who modify codegen or LLVM +[build] +# Contributors working on the compiler will probably expect compiler docs to be generated. +compiler-docs = true + [llvm] # This enables debug-assertions in LLVM, # catching logic errors in codegen much earlier in the process. diff --git a/src/bootstrap/defaults/config.compiler.toml b/src/bootstrap/defaults/config.compiler.toml index 4d689d117bc..2f4ccb825c4 100644 --- a/src/bootstrap/defaults/config.compiler.toml +++ b/src/bootstrap/defaults/config.compiler.toml @@ -1,4 +1,8 @@ # These defaults are meant for contributors to the compiler who do not modify codegen or LLVM +[build] +# Contributors working on the compiler will probably expect compiler docs to be generated. +compiler-docs = true + [rust] # This enables `RUSTC_LOG=debug`, avoiding confusing situations # where adding `debug!()` appears to do nothing. diff --git a/src/bootstrap/defaults/config.tools.toml b/src/bootstrap/defaults/config.tools.toml index 88359fff191..6b6625342a6 100644 --- a/src/bootstrap/defaults/config.tools.toml +++ b/src/bootstrap/defaults/config.tools.toml @@ -14,6 +14,8 @@ download-rustc = "if-unchanged" [build] # Document with the in-tree rustdoc by default, since `download-rustc` makes it quick to compile. doc-stage = 2 +# Contributors working on tools will probably expect compiler docs to be generated, so they can figure out how to use the API. +compiler-docs = true [llvm] # Will download LLVM from CI if available on your platform. diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index b4b973b4247..5d32b4f801a 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -609,7 +609,7 @@ impl Build { /// This avoids contributors checking in a submodule change by accident. pub fn maybe_update_submodules(&self) { // WARNING: keep this in sync with the submodules hard-coded in bootstrap.py - const BOOTSTRAP_SUBMODULES: &[&str] = &[ + let mut bootstrap_submodules: Vec<&str> = vec![ "src/tools/rust-installer", "src/tools/cargo", "src/tools/rls", @@ -617,6 +617,11 @@ impl Build { "library/backtrace", "library/stdarch", ]; + // As in bootstrap.py, we include `rust-analyzer` if `build.vendor` was set in + // `config.toml`. + if self.config.vendor { + bootstrap_submodules.push("src/tools/rust-analyzer"); + } // Avoid running git when there isn't a git checkout. if !self.config.submodules(&self.rust_info) { return; @@ -632,7 +637,7 @@ impl Build { // Sample output: `submodule.src/rust-installer.path src/tools/rust-installer` let submodule = Path::new(line.splitn(2, ' ').nth(1).unwrap()); // avoid updating submodules twice - if !BOOTSTRAP_SUBMODULES.iter().any(|&p| Path::new(p) == submodule) + if !bootstrap_submodules.iter().any(|&p| Path::new(p) == submodule) && channel::GitInfo::new(false, submodule).is_git() { self.update_submodule(submodule); diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 4dfc02dea46..8f2347be90e 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -664,8 +664,6 @@ impl Step for Clippy { &[], ); - // clippy tests need to know about the stage sysroot - cargo.env("SYSROOT", builder.sysroot(compiler)); cargo.env("RUSTC_TEST_SUITE", builder.rustc(compiler)); cargo.env("RUSTC_LIB_PATH", builder.rustc_libdir(compiler)); let host_libs = builder.stage_out(compiler, Mode::ToolRustc).join(builder.cargo_dir()); diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index fc1c2f04fab..3b30e6de12a 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -250,6 +250,10 @@ pub fn prepare_tool_cargo( } } + // clippy tests need to know about the stage sysroot. Set them consistently while building to + // avoid rebuilding when running tests. + cargo.env("SYSROOT", builder.sysroot(compiler)); + // if tools are using lzma we want to force the build script to build its // own copy cargo.env("LZMA_API_STATIC", "1"); diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index 66333e2b992..7a34a7daaec 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -41,4 +41,4 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ /scripts/validate-error-codes.sh && \ # Runs checks to ensure that there are no ES5 issues in our JS code. es-check es6 ../src/librustdoc/html/static/js/*.js && \ - eslint ../src/librustdoc/html/static/js/*.js + eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh index b17398bb6b4..48eb88c9f92 100755 --- a/src/ci/scripts/install-clang.sh +++ b/src/ci/scripts/install-clang.sh @@ -9,7 +9,7 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" # Update both macOS's and Windows's tarballs when bumping the version here. -LLVM_VERSION="12.0.0" +LLVM_VERSION="14.0.2" if isMacOS; then # If the job selects a specific Xcode version, use that instead of diff --git a/src/doc/rustc/src/lints/levels.md b/src/doc/rustc/src/lints/levels.md index 7bd46fafadf..fbec3cd9baf 100644 --- a/src/doc/rustc/src/lints/levels.md +++ b/src/doc/rustc/src/lints/levels.md @@ -100,9 +100,8 @@ This lint level gives you that. 'force-warn' does for 'warn'. It's the same as 'deny' in that a lint at this level will produce an error, but unlike the 'deny' level, the 'forbid' level can not be overridden to be anything lower than an error. However, lint -levels may still be capped with `--cap-lints` (see below) so `rustc --cap- -lints warn` will make lints set to 'forbid' just -warn. +levels may still be capped with `--cap-lints` (see below) so `rustc --cap-lints warn` +will make lints set to 'forbid' just warn. ## Configuring warning levels diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 537ab48bbfc..30b3d6defb4 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -567,3 +567,10 @@ $ rustdoc src/lib.rs -Z unstable-options \ The example above check every well known names (`target_os`, `doc`, `test`, ... via `names()`) and check the values of `feature`: `foo` and `bar`. + +### `--generate-link-to-definition`: Generate links on types in source code + + * Tracking issue: [#89095](https://github.com/rust-lang/rust/issues/89095) + +This flag enables the generation of links in the source code pages which allow the reader +to jump to a type definition. diff --git a/src/doc/unstable-book/src/language-features/debugger-visualizer.md b/src/doc/unstable-book/src/language-features/debugger-visualizer.md new file mode 100644 index 00000000000..4ab482fffb9 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/debugger-visualizer.md @@ -0,0 +1,25 @@ +# `debugger_visualizer` + +The tracking issue for this feature is: [#95939] + +[#95939]: https://github.com/rust-lang/rust/issues/95939 + +------------------------ + +The `debugger_visualizer` attribute can be used to instruct the compiler +to embed a debugger visualizer file into the PDB/ELF generated by `rustc`. + +## Examples + +``` rust,ignore (partial-example) +#![feature(debugger_visualizer)] +#![debugger_visualizer(natvis_file = "foo.natvis")] +struct Foo { + +} +``` + +## Limitations + +Currently, this feature only supports embedding Natvis files on `-windows-msvc` +targets when using the MSVC linker via the `natvis_file` meta item. diff --git a/src/doc/unstable-book/src/language-features/plugin.md b/src/doc/unstable-book/src/language-features/plugin.md index 040f46f8b7c..56fe9a31bfe 100644 --- a/src/doc/unstable-book/src/language-features/plugin.md +++ b/src/doc/unstable-book/src/language-features/plugin.md @@ -102,7 +102,7 @@ The components of a lint plugin are: Lint passes are syntax traversals, but they run at a late stage of compilation where type information is available. `rustc`'s [built-in -lints](https://github.com/rust-lang/rust/blob/master/src/librustc_session/lint/builtin.rs) +lints](https://github.com/rust-lang/rust/blob/master/compiler/rustc_lint_defs/src/builtin.rs) mostly use the same infrastructure as lint plugins, and provide examples of how to access type information. diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 82e367427ef..93ccf60a1de 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1225,16 +1225,6 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx> fn visit_item(&mut self, item: &'hir hir::Item<'_>) { let name = match &item.kind { - hir::ItemKind::Macro(ref macro_def, _) => { - // FIXME(#88038): Non exported macros have historically not been tested, - // but we really ought to start testing them. - let def_id = item.def_id.to_def_id(); - if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) { - intravisit::walk_item(self, item); - return; - } - item.ident.to_string() - } hir::ItemKind::Impl(impl_) => { rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id) } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index b4d2772b31d..30190138750 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -248,7 +248,16 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { } // Index this method for searching later on. - if let Some(ref s) = item.name { + if let Some(ref s) = item.name.or_else(|| { + if item.is_stripped() { + None + } else if let clean::ImportItem(ref i) = *item.kind && + let clean::ImportKind::Simple(s) = i.kind { + Some(s) + } else { + None + } + }) { let (parent, is_inherent_impl_item) = match *item.kind { clean::StrippedItem(..) => ((None, None), false), clean::AssocConstItem(..) | clean::AssocTypeItem(..) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 9e76af98298..56a085c2982 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1440,6 +1440,10 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> { let mut map = FxHashMap::default(); // This is the list of IDs used in Javascript. map.insert("help".into(), 1); + map.insert("settings".into(), 1); + map.insert("not-displayed".into(), 1); + map.insert("alternative-display".into(), 1); + map.insert("search".into(), 1); // This is the list of IDs used in HTML generated in Rust (including the ones // used in tera template files). map.insert("mainThemeStyle".into(), 1); @@ -1449,7 +1453,6 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> { map.insert("settings-menu".into(), 1); map.insert("help-button".into(), 1); map.insert("main-content".into(), 1); - map.insert("search".into(), 1); map.insert("crate-search".into(), 1); map.insert("render-detail".into(), 1); map.insert("toggle-all-docs".into(), 1); diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 8e643107353..a30c533aa48 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -17,8 +17,8 @@ use super::print_item::{full_path, item_path, print_item}; use super::search_index::build_index; use super::write_shared::write_shared; use super::{ - collect_spans_and_sources, print_sidebar, scrape_examples_help, settings, AllTypes, - LinkFromSrc, NameDoc, StylePath, BASIC_KEYWORDS, + collect_spans_and_sources, print_sidebar, scrape_examples_help, AllTypes, LinkFromSrc, NameDoc, + StylePath, BASIC_KEYWORDS, }; use crate::clean::{self, types::ExternalLocation, ExternalCrate}; @@ -589,21 +589,18 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { page.root_path = "./"; let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>"; - let theme_names: Vec<String> = self - .shared - .style_files - .iter() - .map(StylePath::basename) - .collect::<Result<_, Error>>()?; let v = layout::render( &self.shared.layout, &page, sidebar, - settings( - self.shared.static_root_path.as_deref().unwrap_or("./"), - &self.shared.resource_suffix, - theme_names, - )?, + |buf: &mut Buffer| { + write!( + buf, + "<script defer src=\"{}settings{}.js\"></script>", + page.static_root_path.unwrap_or(""), + page.resource_suffix + ) + }, &self.shared.style_files, ); self.shared.fs.write(settings_file, v)?; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 7a4289b8e60..19582481910 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -334,134 +334,6 @@ impl AllTypes { } } -#[derive(Debug)] -enum Setting { - Section { - description: &'static str, - sub_settings: Vec<Setting>, - }, - Toggle { - js_data_name: &'static str, - description: &'static str, - default_value: bool, - }, - Select { - js_data_name: &'static str, - description: &'static str, - default_value: &'static str, - options: Vec<String>, - }, -} - -impl Setting { - fn display(&self, root_path: &str, suffix: &str) -> String { - match *self { - Setting::Section { description, ref sub_settings } => format!( - "<div class=\"setting-line\">\ - <div class=\"title\">{}</div>\ - <div class=\"sub-settings\">{}</div> - </div>", - description, - sub_settings.iter().map(|s| s.display(root_path, suffix)).collect::<String>() - ), - Setting::Toggle { js_data_name, description, default_value } => format!( - "<div class=\"setting-line\">\ - <label class=\"toggle\">\ - <input type=\"checkbox\" id=\"{}\" {}>\ - <span class=\"slider\"></span>\ - </label>\ - <div>{}</div>\ - </div>", - js_data_name, - if default_value { " checked" } else { "" }, - description, - ), - Setting::Select { js_data_name, description, default_value, ref options } => format!( - "<div class=\"setting-line\"><div class=\"radio-line\" id=\"{}\"><span class=\"setting-name\">{}</span><div class=\"choices\">{}</div></div></div>", - js_data_name, - description, - options - .iter() - .map(|opt| format!( - "<label for=\"{js_data_name}-{name}\" class=\"choice\"> - <input type=\"radio\" name=\"{js_data_name}\" id=\"{js_data_name}-{name}\" value=\"{name}\" {checked}>\ - {name}\ - </label>", - js_data_name = js_data_name, - name = opt, - checked = if opt == default_value { "checked" } else { "" }, - )) - .collect::<String>(), - ), - } - } -} - -impl From<(&'static str, &'static str, bool)> for Setting { - fn from(values: (&'static str, &'static str, bool)) -> Setting { - Setting::Toggle { js_data_name: values.0, description: values.1, default_value: values.2 } - } -} - -impl<T: Into<Setting>> From<(&'static str, Vec<T>)> for Setting { - fn from(values: (&'static str, Vec<T>)) -> Setting { - Setting::Section { - description: values.0, - sub_settings: values.1.into_iter().map(|v| v.into()).collect::<Vec<_>>(), - } - } -} - -fn settings(root_path: &str, suffix: &str, theme_names: Vec<String>) -> Result<String, Error> { - // (id, explanation, default value) - let settings: &[Setting] = &[ - Setting::from(("use-system-theme", "Use system theme", true)), - Setting::Select { - js_data_name: "theme", - description: "Theme", - default_value: "light", - options: theme_names.clone(), - }, - Setting::Select { - js_data_name: "preferred-light-theme", - description: "Preferred light theme", - default_value: "light", - options: theme_names.clone(), - }, - Setting::Select { - js_data_name: "preferred-dark-theme", - description: "Preferred dark theme", - default_value: "dark", - options: theme_names, - }, - ("auto-hide-large-items", "Auto-hide item contents for large items.", true).into(), - ("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(), - ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", false) - .into(), - ("go-to-only-result", "Directly go to item in search if there is only one result", false) - .into(), - ("line-numbers", "Show line numbers on code examples", false).into(), - ("disable-shortcuts", "Disable keyboard shortcuts", false).into(), - ]; - - Ok(format!( - "<div class=\"main-heading\"> - <h1 class=\"fqn\">\ - <span class=\"in-band\">Rustdoc settings</span>\ - </h1>\ - <span class=\"out-of-band\">\ - <a id=\"back\" href=\"javascript:void(0)\">Back</a>\ - </span>\ - </div>\ - <div class=\"settings\">{}</div>\ - <link rel=\"stylesheet\" href=\"{root_path}settings{suffix}.css\">\ - <script src=\"{root_path}settings{suffix}.js\"></script>", - settings.iter().map(|s| s.display(root_path, suffix)).collect::<String>(), - root_path = root_path, - suffix = suffix - )) -} - fn scrape_examples_help(shared: &SharedContext<'_>) -> String { let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned(); content.push_str(&format!( @@ -2670,7 +2542,16 @@ fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { let item_sections_in_use: FxHashSet<_> = items .iter() - .filter(|it| !it.is_stripped() && it.name.is_some()) + .filter(|it| { + !it.is_stripped() + && it + .name + .or_else(|| { + if let clean::ImportItem(ref i) = *it.kind && + let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None } + }) + .is_some() + }) .map(|it| item_ty_to_section(it.type_())) .collect(); for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) { diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index f1915920b6d..fbb3d3e4584 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -3,7 +3,7 @@ use clean::AttributesExt; use std::cmp::Ordering; use std::fmt; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::CtorKind; use rustc_hir::def_id::DefId; @@ -346,7 +346,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl w.write_str(ITEM_TABLE_ROW_OPEN); write!( w, - "<div class=\"item-left {stab}{add}import-item\">\ + "<div class=\"item-left {stab}{add}import-item\"{id}>\ <code>{vis}{imp}</code>\ </div>\ <div class=\"item-right docblock-short\">{stab_tags}</div>", @@ -355,6 +355,11 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl vis = myitem.visibility.print_with_space(myitem.item_id, cx), imp = import.print(cx), stab_tags = stab_tags.unwrap_or_default(), + id = match import.kind { + clean::ImportKind::Simple(s) => + format!(" id=\"{}\"", cx.derive_id(format!("reexport.{}", s))), + clean::ImportKind::Glob => String::new(), + }, ); w.write_str(ITEM_TABLE_ROW_CLOSE); } @@ -790,16 +795,18 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All); let cache = cx.cache(); + let mut extern_crates = FxHashSet::default(); if let Some(implementors) = cache.implementors.get(&it.item_id.expect_def_id()) { // The DefId is for the first Type found with that name. The bool is // if any Types with the same name but different DefId have been found. let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default(); for implementor in implementors { - match implementor.inner_impl().for_ { - clean::Type::Path { ref path } - | clean::BorrowedRef { type_: box clean::Type::Path { ref path }, .. } - if !path.is_assoc_ty() => - { + if let Some(did) = implementor.inner_impl().for_.without_borrowed_ref().def_id(cx.cache()) && + !did.is_local() { + extern_crates.insert(did.krate); + } + match implementor.inner_impl().for_.without_borrowed_ref() { + clean::Type::Path { ref path } if !path.is_assoc_ty() => { let did = path.def_id(); let &mut (prev_did, ref mut has_duplicates) = implementor_dups.entry(path.last()).or_insert((did, false)); @@ -898,20 +905,96 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra } } + // Include implementors in crates that depend on the current crate. + // + // This is complicated by the way rustdoc is invoked, which is basically + // the same way rustc is invoked: it gets called, one at a time, for each + // crate. When building the rustdocs for the current crate, rustdoc can + // see crate metadata for its dependencies, but cannot see metadata for its + // dependents. + // + // To make this work, we generate a "hook" at this stage, and our + // dependents can "plug in" to it when they build. For simplicity's sake, + // it's [JSONP]: a JavaScript file with the data we need (and can parse), + // surrounded by a tiny wrapper that the Rust side ignores, but allows the + // JavaScript side to include without having to worry about Same Origin + // Policy. The code for *that* is in `write_shared.rs`. + // + // This is further complicated by `#[doc(inline)]`. We want all copies + // of an inlined trait to reference the same JS file, to address complex + // dependency graphs like this one (lower crates depend on higher crates): + // + // ```text + // -------------------------------------------- + // | crate A: trait Foo | + // -------------------------------------------- + // | | + // -------------------------------- | + // | crate B: impl A::Foo for Bar | | + // -------------------------------- | + // | | + // --------------------------------------------- + // | crate C: #[doc(inline)] use A::Foo as Baz | + // | impl Baz for Quux | + // --------------------------------------------- + // ``` + // + // Basically, we want `C::Baz` and `A::Foo` to show the same set of + // impls, which is easier if they both treat `/implementors/A/trait.Foo.js` + // as the Single Source of Truth. + // + // We also want the `impl Baz for Quux` to be written to + // `trait.Foo.js`. However, when we generate plain HTML for `C::Baz`, + // we're going to want to generate plain HTML for `impl Baz for Quux` too, + // because that'll load faster, and it's better for SEO. And we don't want + // the same impl to show up twice on the same page. + // + // To make this work, the implementors JS file has a structure kinda + // like this: + // + // ```js + // JSONP({ + // "B": {"impl A::Foo for Bar"}, + // "C": {"impl Baz for Quux"}, + // }); + // ``` + // + // First of all, this means we can rebuild a crate, and it'll replace its own + // data if something changes. That is, `rustdoc` is idempotent. The other + // advantage is that we can list the crates that get included in the HTML, + // and ignore them when doing the JavaScript-based part of rendering. + // So C's HTML will have something like this: + // + // ```html + // <script type="text/javascript" src="/implementors/A/trait.Foo.js" + // data-ignore-extern-crates="A,B" async></script> + // ``` + // + // And, when the JS runs, anything in data-ignore-extern-crates is known + // to already be in the HTML, and will be ignored. + // + // [JSONP]: https://en.wikipedia.org/wiki/JSONP let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..") .take(cx.current.len()) .chain(std::iter::once("implementors")) .collect(); - if it.item_id.is_local() { - js_src_path.extend(cx.current.iter().copied()); + if let Some(did) = it.item_id.as_def_id() && + let get_extern = { || cache.external_paths.get(&did).map(|s| s.0.clone()) } && + let Some(fqp) = cache.exact_paths.get(&did).cloned().or_else(get_extern) { + js_src_path.extend(fqp[..fqp.len() - 1].iter().copied()); + js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap())); } else { - let (ref path, _) = cache.external_paths[&it.item_id.expect_def_id()]; - js_src_path.extend(path[..path.len() - 1].iter().copied()); + js_src_path.extend(cx.current.iter().copied()); + js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap())); } - js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap())); + let extern_crates = extern_crates + .into_iter() + .map(|cnum| cx.shared.tcx.crate_name(cnum).to_string()) + .collect::<Vec<_>>() + .join(","); write!( w, - "<script type=\"text/javascript\" src=\"{src}\" async></script>", + "<script type=\"text/javascript\" src=\"{src}\" data-ignore-extern-crates=\"{extern_crates}\" async></script>", src = js_src_path.finish(), ); } diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index b5502309560..1ae888d059d 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ExprKind, GenericParam, HirId, Mod, Node}; +use rustc_hir::{ExprKind, HirId, Mod, Node}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -100,8 +100,6 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { self.tcx.hir() } - fn visit_generic_param(&mut self, _: &'tcx GenericParam<'tcx>) {} - fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) { self.handle_path(path, None); intravisit::walk_path(self, path); diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 7c202e471ad..e8e5fa17993 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -501,10 +501,13 @@ pub(super) fn write_shared( // // FIXME: this is a vague explanation for why this can't be a `get`, in // theory it should be... - let &(ref remote_path, remote_item_type) = match cache.paths.get(&did) { - Some(p) => p, + let (remote_path, remote_item_type) = match cache.exact_paths.get(&did) { + Some(p) => match cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) { + Some((_, t)) => (p, t), + None => continue, + }, None => match cache.external_paths.get(&did) { - Some(p) => p, + Some((p, t)) => (p, t), None => continue, }, }; diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index c7af41ac969..5fcffe715b1 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -16,6 +16,13 @@ module.exports = { "semi": [ "error", "always" - ] + ], + "quotes": [ + "error", + "double" + ], + "no-var": ["error"], + "prefer-const": ["error"], + "prefer-arrow-callback": ["error"], } }; diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 81c12be8e83..eb3ce37e313 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1333,6 +1333,11 @@ pre.rust { border-top: 2px solid; } +#titles > button:first-child:last-child { + margin-right: 1px; + width: calc(100% - 1px); +} + #titles > button:not(:last-child) { margin-right: 1px; width: calc(33.3% - 1px); @@ -1401,6 +1406,18 @@ pre.rust { cursor: pointer; } +@keyframes rotating { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} +#settings-menu.rotate img { + animation: rotating 2s linear infinite; +} + #help-button { font-family: "Fira Sans", Arial, sans-serif; text-align: center; diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js index de881dbd081..defdc20132e 100644 --- a/src/librustdoc/html/static/js/externs.js +++ b/src/librustdoc/html/static/js/externs.js @@ -1,9 +1,6 @@ // This file contains type definitions that are processed by the Closure Compiler but are // not put into the JavaScript we include as part of the documentation. It is used for // type checking. See README.md in this directory for more info. -/* eslint-env es6 */ -/* eslint no-var: "error" */ -/* eslint prefer-const: "error" */ /* eslint-disable */ let searchState; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 9e5de9a843a..336223ad28f 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1,11 +1,10 @@ -/* eslint-env es6 */ -/* eslint no-var: "error" */ -/* eslint prefer-const: "error" */ // Local js definitions: /* global addClass, getSettingValue, hasClass, searchState */ /* global onEach, onEachLazy, removeClass */ /* global switchTheme, useSystemTheme */ +"use strict"; + if (!String.prototype.startsWith) { String.prototype.startsWith = function(searchString, position) { position = position || 0; @@ -57,11 +56,20 @@ function resourcePath(basename, extension) { return getVar("root-path") + basename + getVar("resource-suffix") + extension; } +function hideMain() { + addClass(document.getElementById(MAIN_ID), "hidden"); +} + +function showMain() { + removeClass(document.getElementById(MAIN_ID), "hidden"); +} + (function () { window.rootPath = getVar("root-path"); window.currentCrate = getVar("current-crate"); window.searchJS = resourcePath("search", ".js"); window.searchIndexJS = resourcePath("search-index", ".js"); + window.settingsJS = resourcePath("settings", ".js"); const sidebarVars = document.getElementById("sidebar-vars"); if (sidebarVars) { window.sidebarCurrent = { @@ -104,6 +112,9 @@ function getVirtualKey(ev) { const THEME_PICKER_ELEMENT_ID = "theme-picker"; const THEMES_ELEMENT_ID = "theme-choices"; const MAIN_ID = "main-content"; +const SETTINGS_BUTTON_ID = "settings-menu"; +const ALTERNATIVE_DISPLAY_ID = "alternative-display"; +const NOT_DISPLAYED_ID = "not-displayed"; function getThemesElement() { return document.getElementById(THEMES_ELEMENT_ID); @@ -113,6 +124,10 @@ function getThemePickerElement() { return document.getElementById(THEME_PICKER_ELEMENT_ID); } +function getSettingsButton() { + return document.getElementById(SETTINGS_BUTTON_ID); +} + // Returns the current URL without any query parameter or hash. function getNakedUrl() { return window.location.href.split("?")[0].split("#")[0]; @@ -136,6 +151,10 @@ function hideThemeButtonState() { themePicker.style.borderBottomLeftRadius = "3px"; } +window.hideSettings = () => { + // Does nothing by default. +}; + // Set up the theme picker list. (function () { if (!document.location.href.startsWith("file:///")) { @@ -170,10 +189,10 @@ function hideThemeButtonState() { themePicker.onclick = switchThemeButtonState; themePicker.onblur = handleThemeButtonsBlur; - availableThemes.forEach(function(item) { + availableThemes.forEach(item => { const but = document.createElement("button"); but.textContent = item; - but.onclick = function() { + but.onclick = () => { switchTheme(window.currentTheme, window.mainTheme, item, true); useSystemTheme(false); }; @@ -182,14 +201,121 @@ function hideThemeButtonState() { }); }()); +/** + * This function inserts `newNode` after `referenceNode`. It doesn't work if `referenceNode` + * doesn't have a parent node. + * + * @param {HTMLElement} newNode + * @param {HTMLElement} referenceNode + */ +function insertAfter(newNode, referenceNode) { + referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); +} + +/** + * This function creates a new `<section>` with the given `id` and `classes` if it doesn't already + * exist. + * + * More information about this in `switchDisplayedElement` documentation. + * + * @param {string} id + * @param {string} classes + */ +function getOrCreateSection(id, classes) { + let el = document.getElementById(id); + + if (!el) { + el = document.createElement("section"); + el.id = id; + el.className = classes; + insertAfter(el, document.getElementById(MAIN_ID)); + } + return el; +} + +/** + * Returns the `<section>` element which contains the displayed element. + * + * @return {HTMLElement} + */ +function getAlternativeDisplayElem() { + return getOrCreateSection(ALTERNATIVE_DISPLAY_ID, "content hidden"); +} + +/** + * Returns the `<section>` element which contains the not-displayed elements. + * + * @return {HTMLElement} + */ +function getNotDisplayedElem() { + return getOrCreateSection(NOT_DISPLAYED_ID, "hidden"); +} + +/** + * To nicely switch between displayed "extra" elements (such as search results or settings menu) + * and to alternate between the displayed and not displayed elements, we hold them in two different + * `<section>` elements. They work in pair: one holds the hidden elements while the other + * contains the displayed element (there can be only one at the same time!). So basically, we switch + * elements between the two `<section>` elements. + * + * @param {HTMLElement} elemToDisplay + */ +function switchDisplayedElement(elemToDisplay) { + const el = getAlternativeDisplayElem(); + + if (el.children.length > 0) { + getNotDisplayedElem().appendChild(el.firstElementChild); + } + if (elemToDisplay === null) { + addClass(el, "hidden"); + showMain(); + return; + } + el.appendChild(elemToDisplay); + hideMain(); + removeClass(el, "hidden"); +} + +function browserSupportsHistoryApi() { + return window.history && typeof window.history.pushState === "function"; +} + +// eslint-disable-next-line no-unused-vars +function loadCss(cssFileName) { + const link = document.createElement("link"); + link.href = resourcePath(cssFileName, ".css"); + link.type = "text/css"; + link.rel = "stylesheet"; + document.getElementsByTagName("head")[0].appendChild(link); +} + (function() { - "use strict"; + function loadScript(url) { + const script = document.createElement("script"); + script.src = url; + document.head.append(script); + } + + getSettingsButton().onclick = event => { + addClass(getSettingsButton(), "rotate"); + event.preventDefault(); + // Sending request for the CSS and the JS files at the same time so it will + // hopefully be loaded when the JS will generate the settings content. + loadCss("settings"); + loadScript(window.settingsJS); + }; window.searchState = { loadingText: "Loading search results...", input: document.getElementsByClassName("search-input")[0], - outputElement: function() { - return document.getElementById("search"); + outputElement: () => { + let el = document.getElementById("search"); + if (!el) { + el = document.createElement("section"); + el.id = "search"; + getNotDisplayedElem().appendChild(el); + } + return el; }, title: document.title, titleBeforeSearch: document.title, @@ -202,66 +328,53 @@ function hideThemeButtonState() { currentTab: 0, // tab and back preserves the element that was focused. focusedByTab: [null, null, null], - clearInputTimeout: function() { + clearInputTimeout: () => { if (searchState.timeout !== null) { clearTimeout(searchState.timeout); searchState.timeout = null; } }, + isDisplayed: () => searchState.outputElement().parentElement.id === ALTERNATIVE_DISPLAY_ID, // Sets the focus on the search bar at the top of the page - focus: function() { + focus: () => { searchState.input.focus(); }, // Removes the focus from the search bar. - defocus: function() { + defocus: () => { searchState.input.blur(); }, - showResults: function(search) { - if (search === null || typeof search === 'undefined') { + showResults: search => { + if (search === null || typeof search === "undefined") { search = searchState.outputElement(); } - addClass(main, "hidden"); - removeClass(search, "hidden"); + switchDisplayedElement(search); searchState.mouseMovedAfterSearch = false; document.title = searchState.title; }, - hideResults: function(search) { - if (search === null || typeof search === 'undefined') { - search = searchState.outputElement(); - } - addClass(search, "hidden"); - removeClass(main, "hidden"); + hideResults: () => { + switchDisplayedElement(null); document.title = searchState.titleBeforeSearch; // We also remove the query parameter from the URL. - if (searchState.browserSupportsHistoryApi()) { + if (browserSupportsHistoryApi()) { history.replaceState(null, window.currentCrate + " - Rust", getNakedUrl() + window.location.hash); } }, - getQueryStringParams: function() { + getQueryStringParams: () => { const params = {}; window.location.search.substring(1).split("&"). - map(function(s) { + map(s => { const pair = s.split("="); params[decodeURIComponent(pair[0])] = typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]); }); return params; }, - browserSupportsHistoryApi: function() { - return window.history && typeof window.history.pushState === "function"; - }, - setup: function() { + setup: () => { const search_input = searchState.input; if (!searchState.input) { return; } - function loadScript(url) { - const script = document.createElement('script'); - script.src = url; - document.head.append(script); - } - let searchLoaded = false; function loadSearch() { if (!searchLoaded) { @@ -271,13 +384,13 @@ function hideThemeButtonState() { } } - search_input.addEventListener("focus", function() { + search_input.addEventListener("focus", () => { search_input.origPlaceholder = search_input.placeholder; search_input.placeholder = "Type your search here."; loadSearch(); }); - if (search_input.value != '') { + if (search_input.value !== "") { loadSearch(); } @@ -303,23 +416,20 @@ function hideThemeButtonState() { } const toggleAllDocsId = "toggle-all-docs"; - const main = document.getElementById(MAIN_ID); let savedHash = ""; function handleHashes(ev) { - let elem; - const search = searchState.outputElement(); - if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) { + if (ev !== null && searchState.isDisplayed() && ev.newURL) { // This block occurs when clicking on an element in the navbar while // in a search. - searchState.hideResults(search); + switchDisplayedElement(null); const hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1); - if (searchState.browserSupportsHistoryApi()) { + if (browserSupportsHistoryApi()) { // `window.location.search`` contains all the query parameters, not just `search`. history.replaceState(null, "", getNakedUrl() + window.location.search + "#" + hash); } - elem = document.getElementById(hash); + const elem = document.getElementById(hash); if (elem) { elem.scrollIntoView(); } @@ -389,14 +499,17 @@ function hideThemeButtonState() { } function handleEscape(ev) { + searchState.clearInputTimeout(); const help = getHelpElement(false); - const search = searchState.outputElement(); if (help && !hasClass(help, "hidden")) { displayHelp(false, ev, help); - } else if (search && !hasClass(search, "hidden")) { - searchState.clearInputTimeout(); + } else { + switchDisplayedElement(null); + if (browserSupportsHistoryApi()) { + history.replaceState(null, window.currentCrate + " - Rust", + getNakedUrl() + window.location.hash); + } ev.preventDefault(); - searchState.hideResults(search); } searchState.defocus(); hideThemeButtonState(); @@ -505,7 +618,7 @@ function hideThemeButtonState() { document.addEventListener("keydown", handleShortcut); // delayed sidebar rendering. - window.initSidebarItems = function(items) { + window.initSidebarItems = items => { const sidebar = document.getElementsByClassName("sidebar-elems")[0]; let others; const current = window.sidebarCurrent; @@ -616,7 +729,7 @@ function hideThemeButtonState() { } }; - window.register_implementors = function(imp) { + window.register_implementors = imp => { const implementors = document.getElementById("implementors-list"); const synthetic_implementors = document.getElementById("synthetic-implementors-list"); const inlined_types = new Set(); @@ -627,12 +740,12 @@ function hideThemeButtonState() { // // By the way, this is only used by and useful for traits implemented automatically // (like "Send" and "Sync"). - onEachLazy(synthetic_implementors.getElementsByClassName("impl"), function(el) { + onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => { const aliases = el.getAttribute("data-aliases"); if (!aliases) { return; } - aliases.split(",").forEach(function(alias) { + aliases.split(",").forEach(alias => { inlined_types.add(alias); }); }); @@ -642,8 +755,14 @@ function hideThemeButtonState() { const traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent; const baseIdName = "impl-" + traitName + "-"; const libs = Object.getOwnPropertyNames(imp); + // We don't want to include impls from this JS file, when the HTML already has them. + // The current crate should always be ignored. Other crates that should also be + // ignored are included in the attribute `data-ignore-extern-crates`. + const ignoreExternCrates = document + .querySelector("script[data-ignore-extern-crates]") + .getAttribute("data-ignore-extern-crates"); for (const lib of libs) { - if (lib === window.currentCrate) { + if (lib === window.currentCrate || ignoreExternCrates.indexOf(lib) !== -1) { continue; } const structs = imp[lib]; @@ -666,7 +785,7 @@ function hideThemeButtonState() { addClass(code, "code-header"); addClass(code, "in-band"); - onEachLazy(code.getElementsByTagName("a"), function(elem) { + onEachLazy(code.getElementsByTagName("a"), elem => { const href = elem.getAttribute("href"); if (href && href.indexOf("http") !== 0) { @@ -711,7 +830,7 @@ function hideThemeButtonState() { let sectionIsCollapsed = false; if (hasClass(innerToggle, "will-expand")) { removeClass(innerToggle, "will-expand"); - onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function(e) { + onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { if (!hasClass(e, "type-contents-toggle")) { e.open = true; } @@ -719,7 +838,7 @@ function hideThemeButtonState() { innerToggle.title = "collapse all docs"; } else { addClass(innerToggle, "will-expand"); - onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function(e) { + onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { if (e.parentNode.id !== "implementations-list" || (!hasClass(e, "implementors-toggle") && !hasClass(e, "type-contents-toggle"))) @@ -733,10 +852,6 @@ function hideThemeButtonState() { innerToggle.children[0].innerText = labelForToggleButton(sectionIsCollapsed); } - function insertAfter(newNode, referenceNode) { - referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); - } - (function() { const toggles = document.getElementById(toggleAllDocsId); if (toggles) { @@ -750,7 +865,7 @@ function hideThemeButtonState() { function setImplementorsTogglesOpen(id, open) { const list = document.getElementById(id); if (list !== null) { - onEachLazy(list.getElementsByClassName("implementors-toggle"), function(e) { + onEachLazy(list.getElementsByClassName("implementors-toggle"), e => { e.open = open; }); } @@ -761,7 +876,7 @@ function hideThemeButtonState() { setImplementorsTogglesOpen("blanket-implementations-list", false); } - onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function (e) { + onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => { if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) { e.open = true; } @@ -779,9 +894,9 @@ function hideThemeButtonState() { (function() { // To avoid checking on "rustdoc-line-numbers" value on every loop... - let lineNumbersFunc = function() {}; + let lineNumbersFunc = () => {}; if (getSettingValue("line-numbers") === "true") { - lineNumbersFunc = function(x) { + lineNumbersFunc = x => { const count = x.textContent.split("\n").length; const elems = []; for (let i = 0; i < count; ++i) { @@ -793,7 +908,7 @@ function hideThemeButtonState() { x.parentNode.insertBefore(node, x); }; } - onEachLazy(document.getElementsByClassName("rust-example-rendered"), function(e) { + onEachLazy(document.getElementsByClassName("rust-example-rendered"), e => { if (hasClass(e, "compile_fail")) { e.addEventListener("mouseover", function() { this.parentElement.previousElementSibling.childNodes[0].style.color = "#f00"; @@ -824,43 +939,43 @@ function hideThemeButtonState() { elem.addEventListener("click", f); } } - handleClick("help-button", function(ev) { + handleClick("help-button", ev => { displayHelp(true, ev); }); - handleClick(MAIN_ID, function() { + handleClick(MAIN_ID, () => { hideSidebar(); }); - onEachLazy(document.getElementsByTagName("a"), function(el) { + onEachLazy(document.getElementsByTagName("a"), el => { // For clicks on internal links (<A> tags with a hash property), we expand the section we're // jumping to *before* jumping there. We can't do this in onHashChange, because it changes // the height of the document so we wind up scrolled to the wrong place. if (el.hash) { - el.addEventListener("click", function() { + el.addEventListener("click", () => { expandSection(el.hash.slice(1)); hideSidebar(); }); } }); - onEachLazy(document.querySelectorAll(".rustdoc-toggle > summary:not(.hideme)"), function(el) { - el.addEventListener("click", function(e) { - if (e.target.tagName != "SUMMARY" && e.target.tagName != "A") { + onEachLazy(document.querySelectorAll(".rustdoc-toggle > summary:not(.hideme)"), el => { + el.addEventListener("click", e => { + if (e.target.tagName !== "SUMMARY" && e.target.tagName !== "A") { e.preventDefault(); } }); }); - onEachLazy(document.getElementsByClassName("notable-traits"), function(e) { + onEachLazy(document.getElementsByClassName("notable-traits"), e => { e.onclick = function() { - this.getElementsByClassName('notable-traits-tooltiptext')[0] + this.getElementsByClassName("notable-traits-tooltiptext")[0] .classList.toggle("force-tooltip"); }; }); const sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0]; if (sidebar_menu_toggle) { - sidebar_menu_toggle.addEventListener("click", function() { + sidebar_menu_toggle.addEventListener("click", () => { const sidebar = document.getElementsByClassName("sidebar")[0]; if (!hasClass(sidebar, "shown")) { addClass(sidebar, "shown"); @@ -870,12 +985,12 @@ function hideThemeButtonState() { }); } - let buildHelperPopup = function() { + let buildHelperPopup = () => { const popup = document.createElement("aside"); addClass(popup, "hidden"); popup.id = "help"; - popup.addEventListener("click", function(ev) { + popup.addEventListener("click", ev => { if (ev.target === popup) { // Clicked the blurred zone outside the help popup; dismiss help. displayHelp(false, ev); @@ -898,14 +1013,10 @@ function hideThemeButtonState() { ["⏎", "Go to active search result"], ["+", "Expand all sections"], ["-", "Collapse all sections"], - ].map(function(x) { - return "<dt>" + - x[0].split(" ") - .map(function(y, index) { - return (index & 1) === 0 ? "<kbd>" + y + "</kbd>" : " " + y + " "; - }) - .join("") + "</dt><dd>" + x[1] + "</dd>"; - }).join(""); + ].map(x => "<dt>" + + x[0].split(" ") + .map((y, index) => (index & 1) === 0 ? "<kbd>" + y + "</kbd>" : " " + y + " ") + .join("") + "</dt><dd>" + x[1] + "</dd>").join(""); const div_shortcuts = document.createElement("div"); addClass(div_shortcuts, "shortcuts"); div_shortcuts.innerHTML = "<h2>Keyboard Shortcuts</h2><dl>" + shortcuts + "</dl></div>"; @@ -923,9 +1034,7 @@ function hideThemeButtonState() { "You can look for items with an exact name by putting double quotes around \ your request: <code>\"string\"</code>", "Look for items inside another one by searching for a path: <code>vec::Vec</code>", - ].map(function(x) { - return "<p>" + x + "</p>"; - }).join(""); + ].map(x => "<p>" + x + "</p>").join(""); const div_infos = document.createElement("div"); addClass(div_infos, "infos"); div_infos.innerHTML = "<h2>Search Tricks</h2>" + infos; @@ -945,7 +1054,7 @@ function hideThemeButtonState() { popup.appendChild(container); insertAfter(popup, document.querySelector("main")); // So that it's only built once and then it'll do nothing when called! - buildHelperPopup = function() {}; + buildHelperPopup = () => {}; }; onHashChange(null); @@ -956,43 +1065,43 @@ function hideThemeButtonState() { (function () { let reset_button_timeout = null; - window.copy_path = function(but) { + window.copy_path = but => { const parent = but.parentElement; const path = []; - onEach(parent.childNodes, function(child) { - if (child.tagName === 'A') { + onEach(parent.childNodes, child => { + if (child.tagName === "A") { path.push(child.textContent); } }); - const el = document.createElement('textarea'); - el.value = path.join('::'); - el.setAttribute('readonly', ''); + const el = document.createElement("textarea"); + el.value = path.join("::"); + el.setAttribute("readonly", ""); // To not make it appear on the screen. - el.style.position = 'absolute'; - el.style.left = '-9999px'; + el.style.position = "absolute"; + el.style.left = "-9999px"; document.body.appendChild(el); el.select(); - document.execCommand('copy'); + document.execCommand("copy"); document.body.removeChild(el); // There is always one children, but multiple childNodes. - but.children[0].style.display = 'none'; + but.children[0].style.display = "none"; let tmp; if (but.childNodes.length < 2) { - tmp = document.createTextNode('✓'); + tmp = document.createTextNode("✓"); but.appendChild(tmp); } else { - onEachLazy(but.childNodes, function(e) { + onEachLazy(but.childNodes, e => { if (e.nodeType === Node.TEXT_NODE) { tmp = e; return true; } }); - tmp.textContent = '✓'; + tmp.textContent = "✓"; } if (reset_button_timeout !== null) { @@ -1000,7 +1109,7 @@ function hideThemeButtonState() { } function reset_button() { - tmp.textContent = ''; + tmp.textContent = ""; reset_button_timeout = null; but.children[0].style.display = ""; } diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 865ed7190f3..408b7e19fea 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -1,15 +1,14 @@ -/* eslint-env es6 */ -/* eslint no-var: "error" */ -/* eslint prefer-const: "error" */ /* global addClass, hasClass, removeClass, onEachLazy */ +"use strict"; + (function () { // Number of lines shown when code viewer is not expanded const MAX_LINES = 10; // Scroll code block to the given code location function scrollToLoc(elt, loc) { - const lines = elt.querySelector('.line-numbers'); + const lines = elt.querySelector(".line-numbers"); let scrollOffset; // If the block is greater than the size of the viewer, @@ -33,16 +32,16 @@ function updateScrapedExample(example) { const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); let locIndex = 0; - const highlights = Array.prototype.slice.call(example.querySelectorAll('.highlight')); - const link = example.querySelector('.scraped-example-title a'); + const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight")); + const link = example.querySelector(".scraped-example-title a"); if (locs.length > 1) { // Toggle through list of examples in a given file - const onChangeLoc = function(changeIndex) { - removeClass(highlights[locIndex], 'focus'); + const onChangeLoc = changeIndex => { + removeClass(highlights[locIndex], "focus"); changeIndex(); scrollToLoc(example, locs[locIndex][0]); - addClass(highlights[locIndex], 'focus'); + addClass(highlights[locIndex], "focus"); const url = locs[locIndex][1]; const title = locs[locIndex][2]; @@ -51,24 +50,24 @@ link.innerHTML = title; }; - example.querySelector('.prev') - .addEventListener('click', function() { - onChangeLoc(function() { + example.querySelector(".prev") + .addEventListener("click", () => { + onChangeLoc(() => { locIndex = (locIndex - 1 + locs.length) % locs.length; }); }); - example.querySelector('.next') - .addEventListener('click', function() { - onChangeLoc(function() { + example.querySelector("next") + .addEventListener("click", () => { + onChangeLoc(() => { locIndex = (locIndex + 1) % locs.length; }); }); } - const expandButton = example.querySelector('.expand'); + const expandButton = example.querySelector(".expand"); if (expandButton) { - expandButton.addEventListener('click', function () { + expandButton.addEventListener("click", () => { if (hasClass(example, "expanded")) { removeClass(example, "expanded"); scrollToLoc(example, locs[0][0]); @@ -82,24 +81,24 @@ scrollToLoc(example, locs[0][0]); } - const firstExamples = document.querySelectorAll('.scraped-example-list > .scraped-example'); + const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example"); onEachLazy(firstExamples, updateScrapedExample); - onEachLazy(document.querySelectorAll('.more-examples-toggle'), function(toggle) { + onEachLazy(document.querySelectorAll(".more-examples-toggle"), toggle => { // Allow users to click the left border of the <details> section to close it, // since the section can be large and finding the [+] button is annoying. - onEachLazy(toggle.querySelectorAll('.toggle-line, .hide-more'), button => { - button.addEventListener('click', function() { + onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"), button => { + button.addEventListener("click", () => { toggle.open = false; }); }); - const moreExamples = toggle.querySelectorAll('.scraped-example'); - toggle.querySelector('summary').addEventListener('click', function() { + const moreExamples = toggle.querySelectorAll(".scraped-example"); + toggle.querySelector("summary").addEventListener("click", () => { // Wrapping in setTimeout ensures the update happens after the elements are actually // visible. This is necessary since updateScrapedExample calls scrollToLoc which // depends on offsetHeight, a property that requires an element to be visible to // compute correctly. - setTimeout(function() { onEachLazy(moreExamples, updateScrapedExample); }); + setTimeout(() => { onEachLazy(moreExamples, updateScrapedExample); }); }, {once: true}); }); })(); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index a6f7dd74af6..1e3894c1fcd 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1,8 +1,7 @@ -/* eslint-env es6 */ -/* eslint no-var: "error" */ -/* eslint prefer-const: "error" */ /* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */ -/* global onEachLazy, removeClass, searchState, hasClass */ +/* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi */ + +"use strict"; (function() { // This mapping table should match the discriminants of @@ -42,26 +41,33 @@ const TY_KEYWORD = itemTypes.indexOf("keyword"); // In the search display, allows to switch between tabs. function printTab(nb) { - if (nb === 0 || nb === 1 || nb === 2) { - searchState.currentTab = nb; - } - let nb_copy = nb; - onEachLazy(document.getElementById("titles").childNodes, function(elem) { - if (nb_copy === 0) { + let iter = 0; + let foundCurrentTab = false; + let foundCurrentResultSet = false; + onEachLazy(document.getElementById("titles").childNodes, elem => { + if (nb === iter) { addClass(elem, "selected"); + foundCurrentTab = true; } else { removeClass(elem, "selected"); } - nb_copy -= 1; + iter += 1; }); - onEachLazy(document.getElementById("results").childNodes, function(elem) { - if (nb === 0) { + iter = 0; + onEachLazy(document.getElementById("results").childNodes, elem => { + if (nb === iter) { addClass(elem, "active"); + foundCurrentResultSet = true; } else { removeClass(elem, "active"); } - nb -= 1; + iter += 1; }); + if (foundCurrentTab && foundCurrentResultSet) { + searchState.currentTab = nb; + } else if (nb != 0) { + printTab(0); + } } /** @@ -100,7 +106,7 @@ function levenshtein(s1, s2) { return s1_len + s2_len; } -window.initSearch = function(rawSearchIndex) { +window.initSearch = rawSearchIndex => { const MAX_LEV_DISTANCE = 3; const MAX_RESULTS = 200; const GENERICS_DATA = 2; @@ -198,7 +204,7 @@ window.initSearch = function(rawSearchIndex) { * @return {boolean} */ function isPathStart(parserState) { - return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) == '::'; + return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) == "::"; } /** @@ -209,7 +215,7 @@ window.initSearch = function(rawSearchIndex) { * @return {boolean} */ function isReturnArrow(parserState) { - return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) == '->'; + return parserState.userQuery.slice(parserState.pos, parserState.pos + 2) == "->"; } /** @@ -221,10 +227,10 @@ window.initSearch = function(rawSearchIndex) { */ function isIdentCharacter(c) { return ( - c === '_' || - (c >= '0' && c <= '9') || - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z')); + c === "_" || + (c >= "0" && c <= "9") || + (c >= "a" && c <= "z") || + (c >= "A" && c <= "Z")); } /** @@ -258,7 +264,7 @@ window.initSearch = function(rawSearchIndex) { * @return {QueryElement} - The newly created `QueryElement`. */ function createQueryElement(query, parserState, name, generics, isInGenerics) { - if (name === '*' || (name.length === 0 && generics.length === 0)) { + if (name === "*" || (name.length === 0 && generics.length === 0)) { return; } if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) { @@ -774,7 +780,7 @@ window.initSearch = function(rawSearchIndex) { return []; } - results.sort(function(aaa, bbb) { + results.sort((aaa, bbb) => { let a, b; // sort by exact match with regard to the last word (mismatch goes later) @@ -978,9 +984,8 @@ window.initSearch = function(rawSearchIndex) { if (elem.generics.length === 0) { const checkGeneric = (row.length > GENERICS_DATA && row[GENERICS_DATA].length > 0); - if (checkGeneric && row[GENERICS_DATA].findIndex(function(tmp_elem) { - return tmp_elem[NAME] === elem.name; - }) !== -1) { + if (checkGeneric && row[GENERICS_DATA] + .findIndex(tmp_elem => tmp_elem[NAME] === elem.name) !== -1) { return 0; } } @@ -1169,7 +1174,7 @@ window.initSearch = function(rawSearchIndex) { } } } else { - Object.keys(ALIASES).forEach(function(crate) { + Object.keys(ALIASES).forEach(crate => { if (ALIASES[crate][lowerQuery]) { const pushTo = crate === window.currentCrate ? crateAliases : aliases; const query_aliases = ALIASES[crate][lowerQuery]; @@ -1180,7 +1185,7 @@ window.initSearch = function(rawSearchIndex) { }); } - const sortFunc = function(aaa, bbb) { + const sortFunc = (aaa, bbb) => { if (aaa.path < bbb.path) { return 1; } else if (aaa.path === bbb.path) { @@ -1191,7 +1196,7 @@ window.initSearch = function(rawSearchIndex) { crateAliases.sort(sortFunc); aliases.sort(sortFunc); - const pushFunc = function(alias) { + const pushFunc = alias => { alias.alias = query; const res = buildHrefAndPath(alias); alias.displayPath = pathSplitter(res[0]); @@ -1407,18 +1412,12 @@ window.initSearch = function(rawSearchIndex) { for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) { row = searchIndex[i]; in_returned = checkReturned(row, elem, parsedQuery.typeFilter); - addIntoResults(results_returned, row.id, i, -1, in_returned); + addIntoResults(results_others, row.id, i, -1, in_returned); } } } else if (parsedQuery.foundElems > 0) { - let container = results_others; - // In the special case where only a "returned" information is available, we want to - // put the information into the "results_returned" dict. - if (parsedQuery.returned.length !== 0 && parsedQuery.elems.length === 0) { - container = results_returned; - } for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) { - handleArgs(searchIndex[i], i, container); + handleArgs(searchIndex[i], i, results_others); } } } @@ -1507,6 +1506,9 @@ window.initSearch = function(rawSearchIndex) { displayPath = path + "::"; href = window.rootPath + path.replace(/::/g, "/") + "/" + name + "/index.html"; + } else if (type === "import") { + displayPath = item.path + "::"; + href = window.rootPath + item.path.replace(/::/g, "/") + "/index.html#reexport." + name; } else if (type === "primitive" || type === "keyword") { displayPath = ""; href = window.rootPath + path.replace(/::/g, "/") + @@ -1579,7 +1581,7 @@ window.initSearch = function(rawSearchIndex) { if (array.length > 0) { output.className = "search-results " + extraClass; - array.forEach(function(item) { + array.forEach(item => { const name = item.name; const type = itemTypes[item.ty]; @@ -1706,11 +1708,12 @@ window.initSearch = function(rawSearchIndex) { let crates = ""; if (window.ALL_CRATES.length > 1) { - crates = ` in <select id="crate-search"><option value="All crates">All crates</option>`; + crates = " in <select id=\"crate-search\"><option value=\"All crates\">" + + "All crates</option>"; for (const c of window.ALL_CRATES) { crates += `<option value="${c}" ${c == filterCrates && "selected"}>${c}</option>`; } - crates += `</select>`; + crates += "</select>"; } let typeFilter = ""; @@ -1718,17 +1721,31 @@ window.initSearch = function(rawSearchIndex) { typeFilter = " (type: " + escape(itemTypes[results.query.typeFilter]) + ")"; } - let output = `<div id="search-settings">` + + let output = "<div id=\"search-settings\">" + `<h1 class="search-results-title">Results for ${escape(results.query.userQuery)}` + `${typeFilter}</h1> in ${crates} </div>`; if (results.query.error !== null) { output += `<h3>Query parser error: "${results.query.error}".</h3>`; + output += "<div id=\"titles\">" + + makeTabHeader(0, "In Names", ret_others[1]) + + "</div>"; + currentTab = 0; + } else if (results.query.foundElems <= 1 && results.query.returned.length === 0) { + output += "<div id=\"titles\">" + + makeTabHeader(0, "In Names", ret_others[1]) + + makeTabHeader(1, "In Parameters", ret_in_args[1]) + + makeTabHeader(2, "In Return Types", ret_returned[1]) + + "</div>"; + } else { + const signatureTabTitle = + results.query.elems.length === 0 ? "In Function Return Types" : + results.query.returned.length === 0 ? "In Function Parameters" : + "In Function Signatures"; + output += "<div id=\"titles\">" + + makeTabHeader(0, signatureTabTitle, ret_others[1]) + + "</div>"; + currentTab = 0; } - output += `<div id="titles">` + - makeTabHeader(0, "In Names", ret_others[1]) + - makeTabHeader(1, "In Parameters", ret_in_args[1]) + - makeTabHeader(2, "In Return Types", ret_returned[1]) + - "</div>"; const resultsElem = document.createElement("div"); resultsElem.id = "results"; @@ -1743,12 +1760,16 @@ window.initSearch = function(rawSearchIndex) { } search.appendChild(resultsElem); // Reset focused elements. - searchState.focusedByTab = [null, null, null]; searchState.showResults(search); const elems = document.getElementById("titles").childNodes; - elems[0].onclick = function() { printTab(0); }; - elems[1].onclick = function() { printTab(1); }; - elems[2].onclick = function() { printTab(2); }; + searchState.focusedByTab = []; + let i = 0; + for (const elem of elems) { + const j = i; + elem.onclick = () => { printTab(j); }; + searchState.focusedByTab.push(null); + i += 1; + } printTab(currentTab); } @@ -1786,8 +1807,9 @@ window.initSearch = function(rawSearchIndex) { // Because searching is incremental by character, only the most // recent search query is added to the browser history. - if (searchState.browserSupportsHistoryApi()) { + if (browserSupportsHistoryApi()) { const newURL = buildUrl(query.original, filterCrates); + if (!history.state && !params.search) { history.pushState(null, "", newURL); } else { @@ -1965,10 +1987,9 @@ window.initSearch = function(rawSearchIndex) { if (!searchState.input) { return; } - const search = searchState.outputElement(); - if (search_input.value !== "" && hasClass(search, "hidden")) { - searchState.showResults(search); - if (searchState.browserSupportsHistoryApi()) { + if (search_input.value !== "" && !searchState.isDisplayed()) { + searchState.showResults(); + if (browserSupportsHistoryApi()) { history.replaceState(null, "", buildUrl(search_input.value, getFilterCrates())); } @@ -1977,10 +1998,10 @@ window.initSearch = function(rawSearchIndex) { } function registerSearchEvents() { - const searchAfter500ms = function() { + const searchAfter500ms = () => { searchState.clearInputTimeout(); if (searchState.input.value.length === 0) { - if (searchState.browserSupportsHistoryApi()) { + if (browserSupportsHistoryApi()) { history.replaceState(null, window.currentCrate + " - Rust", getNakedUrl() + window.location.hash); } @@ -1992,7 +2013,7 @@ window.initSearch = function(rawSearchIndex) { searchState.input.onkeyup = searchAfter500ms; searchState.input.oninput = searchAfter500ms; document.getElementsByClassName("search-form")[0].onsubmit = onSearchSubmit; - searchState.input.onchange = function(e) { + searchState.input.onchange = e => { if (e.target !== document.activeElement) { // To prevent doing anything when it's from a blur event. return; @@ -2006,7 +2027,7 @@ window.initSearch = function(rawSearchIndex) { }; searchState.input.onpaste = searchState.input.onchange; - searchState.outputElement().addEventListener("keydown", function(e) { + searchState.outputElement().addEventListener("keydown", e => { // We only handle unmodified keystrokes here. We don't want to interfere with, // for instance, alt-left and alt-right for history navigation. if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { @@ -2041,28 +2062,28 @@ window.initSearch = function(rawSearchIndex) { } }); - searchState.input.addEventListener("keydown", function(e) { + searchState.input.addEventListener("keydown", e => { if (e.which === 40) { // down focusSearchResult(); e.preventDefault(); } }); - searchState.input.addEventListener("focus", function() { + searchState.input.addEventListener("focus", () => { putBackSearch(); }); - searchState.input.addEventListener("blur", function() { + searchState.input.addEventListener("blur", () => { searchState.input.placeholder = searchState.input.origPlaceholder; }); // Push and pop states are used to add search results to the browser // history. - if (searchState.browserSupportsHistoryApi()) { + if (browserSupportsHistoryApi()) { // Store the previous <title> so we can revert back to it later. const previousTitle = document.title; - window.addEventListener("popstate", function(e) { + window.addEventListener("popstate", e => { const params = searchState.getQueryStringParams(); // Revert to the previous title manually since the History // API ignores the title parameter. @@ -2098,7 +2119,7 @@ window.initSearch = function(rawSearchIndex) { // This was an interaction between the back-forward cache and our handlers // that try to sync state between the URL and the search input. To work around it, // do a small amount of re-init on page show. - window.onpageshow = function(){ + window.onpageshow = () => { const qSearch = searchState.getQueryStringParams().search; if (searchState.input.value === "" && qSearch) { searchState.input.value = qSearch; diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 7bc6f6cfe04..ad32a193893 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -1,11 +1,13 @@ -/* eslint-env es6 */ -/* eslint no-var: "error" */ -/* eslint prefer-const: "error" */ // Local js definitions: -/* global getSettingValue, getVirtualKey, onEachLazy, updateLocalStorage, updateSystemTheme */ -/* global addClass, removeClass */ +/* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme */ +/* global addClass, removeClass, onEach, onEachLazy, NOT_DISPLAYED_ID */ +/* global MAIN_ID, getVar, getSettingsButton, switchDisplayedElement, getNotDisplayedElem */ + +"use strict"; (function () { + const isSettingsPage = window.location.pathname.endsWith("/settings.html"); + function changeSetting(settingName, value) { updateLocalStorage(settingName, value); @@ -55,9 +57,9 @@ } } - function setEvents() { + function setEvents(settingsElement) { updateLightAndDark(); - onEachLazy(document.getElementsByClassName("slider"), function(elem) { + onEachLazy(settingsElement.getElementsByClassName("slider"), elem => { const toggle = elem.previousElementSibling; const settingId = toggle.id; const settingValue = getSettingValue(settingId); @@ -70,7 +72,7 @@ toggle.onkeyup = handleKey; toggle.onkeyrelease = handleKey; }); - onEachLazy(document.getElementsByClassName("select-wrapper"), function(elem) { + onEachLazy(settingsElement.getElementsByClassName("select-wrapper"), elem => { const select = elem.getElementsByTagName("select")[0]; const settingId = select.id; const settingValue = getSettingValue(settingId); @@ -81,20 +83,189 @@ changeSetting(this.id, this.value); }; }); - onEachLazy(document.querySelectorAll("input[type=\"radio\"]"), function(elem) { + onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => { const settingId = elem.name; const settingValue = getSettingValue(settingId); if (settingValue !== null && settingValue !== "null") { elem.checked = settingValue === elem.value; } - elem.addEventListener("change", function(ev) { + elem.addEventListener("change", ev => { changeSetting(ev.target.name, ev.target.value); }); }); - document.getElementById("back").addEventListener("click", function() { - history.back(); - }); } - window.addEventListener("DOMContentLoaded", setEvents); + /** + * This function builds the sections inside the "settings page". It takes a `settings` list + * as argument which describes each setting and how to render it. It returns a string + * representing the raw HTML. + * + * @param {Array<Object>} settings + * + * @return {string} + */ + function buildSettingsPageSections(settings) { + let output = ""; + + for (const setting of settings) { + output += "<div class=\"setting-line\">"; + const js_data_name = setting["js_name"]; + const setting_name = setting["name"]; + + if (setting["options"] !== undefined) { + // This is a select setting. + output += `<div class="radio-line" id="${js_data_name}">\ + <span class="setting-name">${setting_name}</span>\ + <div class="choices">`; + onEach(setting["options"], option => { + const checked = option === setting["default"] ? " checked" : ""; + + output += `<label for="${js_data_name}-${option}" class="choice">\ + <input type="radio" name="${js_data_name}" \ + id="${js_data_name}-${option}" value="${option}"${checked}>\ + ${option}\ + </label>`; + }); + output += "</div></div>"; + } else { + // This is a toggle. + const checked = setting["default"] === true ? " checked" : ""; + output += ` + <label class="toggle"> + <input type="checkbox" id="${js_data_name}"${checked}> + <span class="slider"></span> + </label> + <div>${setting_name}</div>`; + } + output += "</div>"; + } + return output; + } + + /** + * This function builds the "settings page" and returns the generated HTML element. + * + * @return {HTMLElement} + */ + function buildSettingsPage() { + const themes = getVar("themes").split(","); + const settings = [ + { + "name": "Use system theme", + "js_name": "use-system-theme", + "default": true, + }, + { + "name": "Theme", + "js_name": "theme", + "default": "light", + "options": themes, + }, + { + "name": "Preferred light theme", + "js_name": "preferred-light-theme", + "default": "light", + "options": themes, + }, + { + "name": "Preferred dark theme", + "js_name": "preferred-dark-theme", + "default": "dark", + "options": themes, + }, + { + "name": "Auto-hide item contents for large items", + "js_name": "auto-hide-large-items", + "default": true, + }, + { + "name": "Auto-hide item methods' documentation", + "js_name": "auto-hide-method-docs", + "default": false, + }, + { + "name": "Auto-hide trait implementation documentation", + "js_name": "auto-hide-trait-implementations", + "default": false, + }, + { + "name": "Directly go to item in search if there is only one result", + "js_name": "go-to-only-result", + "default": false, + }, + { + "name": "Show line numbers on code examples", + "js_name": "line-numbers", + "default": false, + }, + { + "name": "Disable keyboard shortcuts", + "js_name": "disable-shortcuts", + "default": false, + }, + ]; + + // Then we build the DOM. + const el = document.createElement("section"); + el.id = "settings"; + let innerHTML = ` + <div class="main-heading"> + <h1 class="fqn"> + <span class="in-band">Rustdoc settings</span> + </h1> + <span class="out-of-band">`; + + if (isSettingsPage) { + innerHTML += + "<a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">Back</a>"; + } else { + innerHTML += "<a id=\"back\" href=\"javascript:void(0)\" " + + "onclick=\"switchDisplayedElement(null);\">Back</a>"; + } + innerHTML += `</span> + </div> + <div class="settings">${buildSettingsPageSections(settings)}</div>`; + + el.innerHTML = innerHTML; + + if (isSettingsPage) { + document.getElementById(MAIN_ID).appendChild(el); + } else { + getNotDisplayedElem().appendChild(el); + } + return el; + } + + const settingsMenu = buildSettingsPage(); + + if (isSettingsPage) { + // We replace the existing "onclick" callback to do nothing if clicked. + getSettingsButton().onclick = function(event) { + event.preventDefault(); + }; + } else { + // We replace the existing "onclick" callback. + const settingsButton = getSettingsButton(); + settingsButton.onclick = function(event) { + event.preventDefault(); + if (settingsMenu.parentElement.id === NOT_DISPLAYED_ID) { + switchDisplayedElement(settingsMenu); + } else { + window.hideSettings(); + } + }; + window.hideSettings = function() { + switchDisplayedElement(null); + }; + } + + // We now wait a bit for the web browser to end re-computing the DOM... + setTimeout(() => { + setEvents(settingsMenu); + // The setting menu is already displayed if we're on the settings page. + if (!isSettingsPage) { + switchDisplayedElement(settingsMenu); + } + removeClass(getSettingsButton(), "rotate"); + }, 0); })(); diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index c48a847665e..92ecd200081 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -1,13 +1,12 @@ -/* eslint-env es6 */ -/* eslint no-var: "error" */ -/* eslint prefer-const: "error" */ - // From rust: /* global search, sourcesIndex */ // Local js definitions: -/* global addClass, getCurrentValue, hasClass, onEachLazy, removeClass, searchState */ +/* global addClass, getCurrentValue, hasClass, onEachLazy, removeClass, browserSupportsHistoryApi */ /* global updateLocalStorage */ + +"use strict"; + (function() { function getCurrentFilePath() { @@ -32,7 +31,7 @@ function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) { fullPath += elem["name"] + "/"; - name.onclick = function() { + name.onclick = () => { if (hasClass(this, "expand")) { removeClass(this, "expand"); } else { @@ -134,7 +133,7 @@ function createSourceSidebar() { title.className = "title"; title.innerText = "Files"; sidebar.appendChild(title); - Object.keys(sourcesIndex).forEach(function(key) { + Object.keys(sourcesIndex).forEach(key => { sourcesIndex[key].name = key; hasFoundFile = createDirEntry(sourcesIndex[key], sidebar, "", currentFile, hasFoundFile); @@ -175,8 +174,8 @@ function highlightSourceLines(match) { if (x) { x.scrollIntoView(); } - onEachLazy(document.getElementsByClassName("line-numbers"), function(e) { - onEachLazy(e.getElementsByTagName("span"), function(i_e) { + onEachLazy(document.getElementsByClassName("line-numbers"), e => { + onEachLazy(e.getElementsByTagName("span"), i_e => { removeClass(i_e, "line-highlighted"); }); }); @@ -189,13 +188,13 @@ function highlightSourceLines(match) { } } -const handleSourceHighlight = (function() { +const handleSourceHighlight = (function () { let prev_line_id = 0; - const set_fragment = function(name) { + const set_fragment = name => { const x = window.scrollX, y = window.scrollY; - if (searchState.browserSupportsHistoryApi()) { + if (browserSupportsHistoryApi()) { history.replaceState(null, null, "#" + name); highlightSourceLines(); } else { @@ -205,7 +204,7 @@ const handleSourceHighlight = (function() { window.scrollTo(x, y); }; - return function(ev) { + return ev => { let cur_line_id = parseInt(ev.target.id, 10); ev.preventDefault(); @@ -226,14 +225,14 @@ const handleSourceHighlight = (function() { }; }()); -window.addEventListener("hashchange", function() { +window.addEventListener("hashchange", () => { const match = window.location.hash.match(lineNumbersRegex); if (match) { return highlightSourceLines(match); } }); -onEachLazy(document.getElementsByClassName("line-numbers"), function(el) { +onEachLazy(document.getElementsByClassName("line-numbers"), el => { el.addEventListener("click", handleSourceHighlight); }); diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index ae670ed9894..21de7d77d64 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -1,6 +1,4 @@ -/* eslint-env es6 */ -/* eslint no-var: "error" */ -/* eslint prefer-const: "error" */ +"use strict"; const darkThemes = ["dark", "ayu"]; window.currentTheme = document.getElementById("themeStyle"); @@ -26,7 +24,7 @@ function getSettingValue(settingName) { if (settingsDataset !== null) { // See the comment for `default_settings.into_iter()` etc. in // `Options::from_matches` in `librustdoc/config.rs`. - const def = settingsDataset[settingName.replace(/-/g,'_')]; + const def = settingsDataset[settingName.replace(/-/g,"_")]; if (def !== undefined) { return def; } @@ -139,11 +137,11 @@ function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) { let found = false; if (savedHref.length === 0) { - onEachLazy(document.getElementsByTagName("link"), function(el) { + onEachLazy(document.getElementsByTagName("link"), el => { savedHref.push(el.href); }); } - onEach(savedHref, function(el) { + onEach(savedHref, el => { if (el === newHref) { found = true; return true; @@ -170,12 +168,12 @@ function useSystemTheme(value) { } } -const updateSystemTheme = (function() { +const updateSystemTheme = (function () { if (!window.matchMedia) { // fallback to the CSS computed value - return function() { + return () => { const cssTheme = getComputedStyle(document.documentElement) - .getPropertyValue('content'); + .getPropertyValue("content"); switchTheme( window.currentTheme, @@ -190,7 +188,7 @@ const updateSystemTheme = (function() { const mql = window.matchMedia("(prefers-color-scheme: dark)"); function handlePreferenceChange(mql) { - const use = function(theme) { + const use = theme => { switchTheme(window.currentTheme, window.mainTheme, theme, true); }; // maybe the user has disabled the setting in the meantime! @@ -214,7 +212,7 @@ const updateSystemTheme = (function() { mql.addListener(handlePreferenceChange); - return function() { + return () => { handlePreferenceChange(mql); }; })(); @@ -252,7 +250,7 @@ if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) { // For some reason, if we try to change the theme while the `pageshow` event is // running, it sometimes fails to take effect. The problem manifests on Chrome, // specifically when talking to a remote website with no caching. -window.addEventListener("pageshow", function(ev) { +window.addEventListener("pageshow", ev => { if (ev.persisted) { setTimeout(switchToSavedTheme, 0); } diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index 564731ab735..470cce93a50 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -135,7 +135,6 @@ </nav> {#- -#} </div> {#- -#} <section id="main-content" class="content">{{- content|safe -}}</section> {#- -#} - <section id="search" class="content hidden"></section> {#- -#} </div> {#- -#} </main> {#- -#} {{- layout.external_html.after_content|safe -}} diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index cd196a01847..dfc70237821 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -5,7 +5,6 @@ #![feature(rustc_private)] #![feature(array_methods)] #![feature(assert_matches)] -#![feature(bool_to_option)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(box_syntax)] @@ -96,7 +95,7 @@ use crate::passes::collect_intra_doc_links; /// /// Example: /// -/// ``` +/// ```ignore(cannot-test-this-because-non-exported-macro) /// let letters = map!{"a" => "b", "c" => "d"}; /// ``` /// diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs index 1d2ef832db7..07d05cab1d1 100644 --- a/src/librustdoc/passes/collect_intra_doc_links/early.rs +++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs @@ -293,10 +293,22 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { if let Res::Def(DefKind::Mod, ..) = child.res { self.resolve_doc_links_extern_inner(def_id); // Inner attribute scope } - // Traits are processed in `add_extern_traits_in_scope`. + // `DefKind::Trait`s are processed in `process_extern_impls`. if let Res::Def(DefKind::Mod | DefKind::Enum, ..) = child.res { self.process_module_children_or_reexports(def_id); } + if let Res::Def(DefKind::Struct | DefKind::Union | DefKind::Variant, _) = + child.res + { + let field_def_ids = Vec::from_iter( + self.resolver + .cstore() + .associated_item_def_ids_untracked(def_id, self.sess), + ); + for field_def_id in field_def_ids { + self.resolve_doc_links_extern_outer(field_def_id, scope_id); + } + } } } } diff --git a/src/test/assembly/asm/global_asm.rs b/src/test/assembly/asm/global_asm.rs index b76ce7ac387..4cf73b40faf 100644 --- a/src/test/assembly/asm/global_asm.rs +++ b/src/test/assembly/asm/global_asm.rs @@ -2,6 +2,7 @@ // only-linux // assembly-output: emit-asm // compile-flags: -C llvm-args=--x86-asm-syntax=intel +// compile-flags: -C symbol-mangling-version=v0 #![feature(asm_const, asm_sym)] #![crate_type = "rlib"] @@ -24,3 +25,7 @@ global_asm!("movl ${}, %ecx", const 5, options(att_syntax)); global_asm!("call {}", sym my_func); // CHECK: lea rax, [rip + MY_STATIC] global_asm!("lea rax, [rip + {}]", sym MY_STATIC); +// CHECK: call _RNvCsiubXh4Yz005_10global_asm6foobar +global_asm!("call {}", sym foobar); +// CHECK: _RNvCsiubXh4Yz005_10global_asm6foobar: +fn foobar() { loop {} } diff --git a/src/test/debuginfo/msvc-embedded-natvis.natvis b/src/test/debuginfo/msvc-embedded-natvis.natvis new file mode 100644 index 00000000000..201d014b520 --- /dev/null +++ b/src/test/debuginfo/msvc-embedded-natvis.natvis @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010"> + <Type Name="msvc_embedded_natvis::Point"> + <DisplayString>({x}, {y})</DisplayString> + <Expand> + <Item Name="[x]">x</Item> + <Item Name="[y]">y</Item> + </Expand> + </Type> + + <Type Name="msvc_embedded_natvis::Line"> + <DisplayString>({a}, {b})</DisplayString> + <Expand> + <Item Name="[a]">a</Item> + <Item Name="[b]">b</Item> + </Expand> + </Type> +</AutoVisualizer> diff --git a/src/test/debuginfo/msvc-embedded-natvis.rs b/src/test/debuginfo/msvc-embedded-natvis.rs new file mode 100644 index 00000000000..f714fb2ad17 --- /dev/null +++ b/src/test/debuginfo/msvc-embedded-natvis.rs @@ -0,0 +1,64 @@ +// only-cdb +// compile-flags:-g + +// === CDB TESTS ================================================================================== + +// cdb-command: g + +// cdb-command: .nvlist +// cdb-check: [...].exe (embedded NatVis "[...]msvc_embedded_natvis-0.natvis") + +// cdb-command: dx point_a +// cdb-check:point_a : (0, 0) [Type: msvc_embedded_natvis::Point] +// cdb-check: [<Raw View>] [Type: msvc_embedded_natvis::Point] +// cdb-check: [x] : 0 [Type: int] +// cdb-check: [y] : 0 [Type: int] + +// cdb-command: dx point_b +// cdb-check:point_b : (5, 8) [Type: msvc_embedded_natvis::Point] +// cdb-check: [<Raw View>] [Type: msvc_embedded_natvis::Point] +// cdb-check: [x] : 5 [Type: int] +// cdb-check: [y] : 8 [Type: int] + +// cdb-command: dx line +// cdb-check:line : ((0, 0), (5, 8)) [Type: msvc_embedded_natvis::Line] +// cdb-check: [<Raw View>] [Type: msvc_embedded_natvis::Line] +// cdb-check: [a] : (0, 0) [Type: msvc_embedded_natvis::Point] +// cdb-check: [b] : (5, 8) [Type: msvc_embedded_natvis::Point] + +#![feature(debugger_visualizer)] +#![debugger_visualizer(natvis_file = "msvc-embedded-natvis.natvis")] + +pub struct Point { + x: i32, + y: i32, +} + +impl Point { + pub fn new(x: i32, y: i32) -> Point { + Point { x: x, y: y } + } +} + +pub struct Line { + a: Point, + b: Point, +} + +impl Line { + pub fn new(a: Point, b: Point) -> Line { + Line { a: a, b: b } + } +} + +fn main() { + let point_a = Point::new(0, 0); + let point_b = Point::new(5, 8); + let line = Line::new(point_a, point_b); + + zzz(); // #break +} + +fn zzz() { + () +} diff --git a/src/test/incremental/issue-96319-coinductive-cycle.rs b/src/test/incremental/issue-96319-coinductive-cycle.rs new file mode 100644 index 00000000000..b5ff9112a20 --- /dev/null +++ b/src/test/incremental/issue-96319-coinductive-cycle.rs @@ -0,0 +1,34 @@ +// edition:2018 +// revisions: rpass1 rpass2 + +pub struct Stmt { + pub stmt_type: StmtKind, + #[cfg(rpass1)] pub stmt_tag: Option<LintTag>, + #[cfg(rpass2)] pub renamed_tag: Option<LintTag>, +} +pub struct LintTag; +pub enum StmtKind { + If(If), + Block(&'static str), + Return(Return), +} +pub struct If { + pub condition: Function, +} +pub struct Return { + pub value: Function, +} +pub struct Function { + pub parameters: Box<Stmt>, +} +pub fn start_late_pass(stmt_receiver: Box<Stmt>) { + spawn(async { stmt_receiver }); +} + +pub fn spawn<T>(_: T) +where + T: Send, +{ +} + +fn main() {} diff --git a/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff b/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff index 7dd420e41ce..bbde6ad4b63 100644 --- a/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff +++ b/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff @@ -77,7 +77,7 @@ _9 = const "hello, world!"; // scope 4 at $DIR/const_debuginfo.rs:14:13: 14:28 // mir::Constant // + span: $DIR/const_debuginfo.rs:14:13: 14:28 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [8191], len: Size { raw: 13 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 13 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } StorageLive(_10); // scope 5 at $DIR/const_debuginfo.rs:16:9: 16:10 Deinit(_10); // scope 5 at $DIR/const_debuginfo.rs:16:13: 16:34 (_10.0: bool) = const true; // scope 5 at $DIR/const_debuginfo.rs:16:13: 16:34 diff --git a/src/test/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.diff b/src/test/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.diff index 49f6c104157..cb4273ba6bd 100644 --- a/src/test/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.diff +++ b/src/test/mir-opt/const_prop/control_flow_simplification.hello.ConstProp.diff @@ -22,7 +22,7 @@ // + literal: Const { ty: fn(&str) -> ! {begin_panic::<&str>}, val: Value(Scalar(<ZST>)) } // mir::Constant // + span: $SRC_DIR/std/src/panic.rs:LL:COL - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } } bb2: { diff --git a/src/test/mir-opt/inline/inline_diverging.g.Inline.diff b/src/test/mir-opt/inline/inline_diverging.g.Inline.diff index 3b9d5e727b8..31719b435d6 100644 --- a/src/test/mir-opt/inline/inline_diverging.g.Inline.diff +++ b/src/test/mir-opt/inline/inline_diverging.g.Inline.diff @@ -43,7 +43,7 @@ + // + literal: Const { ty: fn(&str) -> ! {begin_panic::<&str>}, val: Value(Scalar(<ZST>)) } + // mir::Constant + // + span: $SRC_DIR/std/src/panic.rs:LL:COL -+ // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) } ++ // + literal: Const { ty: &str, val: Value(Slice(..)) } } } diff --git a/src/test/mir-opt/inline/inline_into_box_place.main.Inline.32bit.diff b/src/test/mir-opt/inline/inline_into_box_place.main.Inline.32bit.diff index 074ad067ff8..c19cbe3e5b0 100644 --- a/src/test/mir-opt/inline/inline_into_box_place.main.Inline.32bit.diff +++ b/src/test/mir-opt/inline/inline_into_box_place.main.Inline.32bit.diff @@ -16,6 +16,7 @@ scope 2 { } + scope 3 (inlined Vec::<u32>::new) { // at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ let mut _8: alloc::raw_vec::RawVec<u32>; // in scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + } bb0: { @@ -34,8 +35,8 @@ - (*_5) = Vec::<u32>::new() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 + StorageLive(_7); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 + _7 = &mut (*_5); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 -+ Deinit((*_7)); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL -+ ((*_7).0: alloc::raw_vec::RawVec<u32>) = const alloc::raw_vec::RawVec::<u32> { ptr: Unique::<u32> { pointer: NonNull::<u32> { pointer: {0x4 as *const u32} }, _marker: PhantomData::<u32> }, cap: 0_usize, alloc: std::alloc::Global }; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL ++ StorageLive(_8); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL ++ _8 = const alloc::raw_vec::RawVec::<u32> { ptr: Unique::<u32> { pointer: NonNull::<u32> { pointer: {0x4 as *const u32} }, _marker: PhantomData::<u32> }, cap: 0_usize, alloc: std::alloc::Global }; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL // mir::Constant - // + span: $DIR/inline-into-box-place.rs:8:33: 8:41 - // + user_ty: UserType(1) @@ -45,8 +46,11 @@ - bb2: { + // + span: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + // + user_ty: UserType(0) -+ // + literal: Const { ty: alloc::raw_vec::RawVec<u32>, val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ // + literal: Const { ty: alloc::raw_vec::RawVec<u32>, val: Value(ByRef(..)) } ++ Deinit((*_7)); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL ++ ((*_7).0: alloc::raw_vec::RawVec<u32>) = move _8; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + ((*_7).1: usize) = const 0_usize; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL ++ StorageDead(_8); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + StorageDead(_7); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 _1 = move _5; // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 StorageDead(_5); // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 diff --git a/src/test/mir-opt/inline/inline_into_box_place.main.Inline.64bit.diff b/src/test/mir-opt/inline/inline_into_box_place.main.Inline.64bit.diff index a055ae9864f..c19cbe3e5b0 100644 --- a/src/test/mir-opt/inline/inline_into_box_place.main.Inline.64bit.diff +++ b/src/test/mir-opt/inline/inline_into_box_place.main.Inline.64bit.diff @@ -16,6 +16,7 @@ scope 2 { } + scope 3 (inlined Vec::<u32>::new) { // at $DIR/inline-into-box-place.rs:8:33: 8:43 ++ let mut _8: alloc::raw_vec::RawVec<u32>; // in scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + } bb0: { @@ -34,8 +35,8 @@ - (*_5) = Vec::<u32>::new() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 + StorageLive(_7); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 + _7 = &mut (*_5); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 -+ Deinit((*_7)); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL -+ ((*_7).0: alloc::raw_vec::RawVec<u32>) = const alloc::raw_vec::RawVec::<u32> { ptr: Unique::<u32> { pointer: NonNull::<u32> { pointer: {0x4 as *const u32} }, _marker: PhantomData::<u32> }, cap: 0_usize, alloc: std::alloc::Global }; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL ++ StorageLive(_8); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL ++ _8 = const alloc::raw_vec::RawVec::<u32> { ptr: Unique::<u32> { pointer: NonNull::<u32> { pointer: {0x4 as *const u32} }, _marker: PhantomData::<u32> }, cap: 0_usize, alloc: std::alloc::Global }; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL // mir::Constant - // + span: $DIR/inline-into-box-place.rs:8:33: 8:41 - // + user_ty: UserType(1) @@ -45,8 +46,11 @@ - bb2: { + // + span: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + // + user_ty: UserType(0) -+ // + literal: Const { ty: alloc::raw_vec::RawVec<u32>, val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [65535], len: Size { raw: 16 } }, align: Align { pow2: 3 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) } ++ // + literal: Const { ty: alloc::raw_vec::RawVec<u32>, val: Value(ByRef(..)) } ++ Deinit((*_7)); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL ++ ((*_7).0: alloc::raw_vec::RawVec<u32>) = move _8; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + ((*_7).1: usize) = const 0_usize; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL ++ StorageDead(_8); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + StorageDead(_7); // scope 0 at $DIR/inline-into-box-place.rs:8:33: 8:43 _1 = move _5; // scope 0 at $DIR/inline-into-box-place.rs:8:29: 8:43 StorageDead(_5); // scope 0 at $DIR/inline-into-box-place.rs:8:42: 8:43 diff --git a/src/test/mir-opt/issue_76432.test.SimplifyComparisonIntegral.diff b/src/test/mir-opt/issue_76432.test.SimplifyComparisonIntegral.diff index c1a4fc301d7..f9e11439dd9 100644 --- a/src/test/mir-opt/issue_76432.test.SimplifyComparisonIntegral.diff +++ b/src/test/mir-opt/issue_76432.test.SimplifyComparisonIntegral.diff @@ -73,7 +73,7 @@ // + literal: Const { ty: fn(&'static str) -> ! {core::panicking::panic}, val: Value(Scalar(<ZST>)) } // mir::Constant // + span: $SRC_DIR/core/src/panic.rs:LL:COL - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [105, 110, 116, 101, 114, 110, 97, 108, 32, 101, 114, 114, 111, 114, 58, 32, 101, 110, 116, 101, 114, 101, 100, 32, 117, 110, 114, 101, 97, 99, 104, 97, 98, 108, 101, 32, 99, 111, 100, 101], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1099511627775], len: Size { raw: 40 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 40 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } } bb2: { diff --git a/src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir b/src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir index e2051c85af2..a6174174849 100644 --- a/src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir +++ b/src/test/mir-opt/issues/issue_59352.num_to_digit.PreCodegen.after.mir @@ -92,7 +92,7 @@ fn num_to_digit(_1: char) -> u32 { // + literal: Const { ty: fn(&'static str) -> ! {core::panicking::panic}, val: Value(Scalar(<ZST>)) } // mir::Constant // + span: $SRC_DIR/core/src/option.rs:LL:COL - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [99, 97, 108, 108, 101, 100, 32, 96, 79, 112, 116, 105, 111, 110, 58, 58, 117, 110, 119, 114, 97, 112, 40, 41, 96, 32, 111, 110, 32, 97, 32, 96, 78, 111, 110, 101, 96, 32, 118, 97, 108, 117, 101], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [8796093022207], len: Size { raw: 43 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 43 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } } bb7: { diff --git a/src/test/mir-opt/no_drop_for_inactive_variant.unwrap.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/no_drop_for_inactive_variant.unwrap.SimplifyCfg-elaborate-drops.after.mir index d562f04560c..2044d34a3db 100644 --- a/src/test/mir-opt/no_drop_for_inactive_variant.unwrap.SimplifyCfg-elaborate-drops.after.mir +++ b/src/test/mir-opt/no_drop_for_inactive_variant.unwrap.SimplifyCfg-elaborate-drops.after.mir @@ -26,7 +26,7 @@ fn unwrap(_1: Option<T>) -> T { // + literal: Const { ty: fn(&str) -> ! {begin_panic::<&str>}, val: Value(Scalar(<ZST>)) } // mir::Constant // + span: $SRC_DIR/std/src/panic.rs:LL:COL - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [101, 120, 112, 108, 105, 99, 105, 116, 32, 112, 97, 110, 105, 99], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [16383], len: Size { raw: 14 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 14 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } } bb2: { diff --git a/src/test/mir-opt/no_spurious_drop_after_call.main.ElaborateDrops.before.mir b/src/test/mir-opt/no_spurious_drop_after_call.main.ElaborateDrops.before.mir index 22bf1acc57d..bdab2d93222 100644 --- a/src/test/mir-opt/no_spurious_drop_after_call.main.ElaborateDrops.before.mir +++ b/src/test/mir-opt/no_spurious_drop_after_call.main.ElaborateDrops.before.mir @@ -15,7 +15,7 @@ fn main() -> () { _4 = const ""; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 // mir::Constant // + span: $DIR/no-spurious-drop-after-call.rs:9:20: 9:22 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [], len: Size { raw: 0 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 0 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _3 = &(*_4); // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34 _2 = <str as ToString>::to_string(move _3) -> bb1; // scope 0 at $DIR/no-spurious-drop-after-call.rs:9:20: 9:34 // mir::Constant diff --git a/src/test/mir-opt/storage_live_dead_in_statics.XXX.mir_map.0.mir b/src/test/mir-opt/storage_live_dead_in_statics.XXX.mir_map.0.mir index 62fbcaaa289..e0875ab0069 100644 --- a/src/test/mir-opt/storage_live_dead_in_statics.XXX.mir_map.0.mir +++ b/src/test/mir-opt/storage_live_dead_in_statics.XXX.mir_map.0.mir @@ -192,7 +192,7 @@ static XXX: &Foo = { _2 = Foo { tup: const "hi", data: move _3 }; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:29: 23:2 // mir::Constant // + span: $DIR/storage_live_dead_in_statics.rs:6:10: 6:14 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [104, 105], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 2 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } StorageDead(_3); // scope 0 at $DIR/storage_live_dead_in_statics.rs:23:1: 23:2 _1 = &_2; // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 _0 = &(*_1); // scope 0 at $DIR/storage_live_dead_in_statics.rs:5:28: 23:2 diff --git a/src/test/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir b/src/test/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir index 2b79a69b93b..16fd328b6f9 100644 --- a/src/test/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir +++ b/src/test/mir-opt/uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir @@ -22,7 +22,7 @@ fn main() -> () { _5 = const "C"; // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching.rs:23:21: 23:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _1 = &(*_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 StorageDead(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:23: 23:24 StorageDead(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:24:6: 24:7 @@ -40,7 +40,7 @@ fn main() -> () { _9 = const "E"; // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching.rs:28:21: 28:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _6 = &(*_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 StorageDead(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:23: 28:24 goto -> bb3; // scope 0 at $DIR/uninhabited_enum_branching.rs:28:23: 28:24 @@ -50,7 +50,7 @@ fn main() -> () { _6 = const "D"; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:21: 27:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching.rs:27:21: 27:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } goto -> bb3; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:21: 27:24 } diff --git a/src/test/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff b/src/test/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff index fe87bbd8c0b..c499e5c59db 100644 --- a/src/test/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff +++ b/src/test/mir-opt/uninhabited_enum_branching.main.UninhabitedEnumBranching.diff @@ -28,7 +28,7 @@ _5 = const "C"; // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching.rs:23:21: 23:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _1 = &(*_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:21: 23:24 StorageDead(_5); // scope 0 at $DIR/uninhabited_enum_branching.rs:23:23: 23:24 goto -> bb4; // scope 0 at $DIR/uninhabited_enum_branching.rs:23:23: 23:24 @@ -38,7 +38,7 @@ _1 = const "A(Empty)"; // scope 0 at $DIR/uninhabited_enum_branching.rs:21:24: 21:34 // mir::Constant // + span: $DIR/uninhabited_enum_branching.rs:21:24: 21:34 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [65, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } goto -> bb4; // scope 0 at $DIR/uninhabited_enum_branching.rs:21:24: 21:34 } @@ -47,7 +47,7 @@ _4 = const "B(Empty)"; // scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 // mir::Constant // + span: $DIR/uninhabited_enum_branching.rs:22:24: 22:34 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [66, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _1 = &(*_4); // scope 0 at $DIR/uninhabited_enum_branching.rs:22:24: 22:34 StorageDead(_4); // scope 0 at $DIR/uninhabited_enum_branching.rs:22:33: 22:34 goto -> bb4; // scope 0 at $DIR/uninhabited_enum_branching.rs:22:33: 22:34 @@ -69,7 +69,7 @@ _9 = const "E"; // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching.rs:28:21: 28:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [69], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _6 = &(*_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:21: 28:24 StorageDead(_9); // scope 0 at $DIR/uninhabited_enum_branching.rs:28:23: 28:24 goto -> bb7; // scope 0 at $DIR/uninhabited_enum_branching.rs:28:23: 28:24 @@ -79,7 +79,7 @@ _6 = const "D"; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:21: 27:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching.rs:27:21: 27:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } goto -> bb7; // scope 0 at $DIR/uninhabited_enum_branching.rs:27:21: 27:24 } diff --git a/src/test/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir b/src/test/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir index 27f9c8b7f8f..77951bc8d7b 100644 --- a/src/test/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir +++ b/src/test/mir-opt/uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir @@ -40,7 +40,7 @@ fn main() -> () { _8 = const "D"; // scope 1 at $DIR/uninhabited_enum_branching2.rs:25:21: 25:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching2.rs:25:21: 25:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _3 = &(*_8); // scope 1 at $DIR/uninhabited_enum_branching2.rs:25:21: 25:24 StorageDead(_8); // scope 1 at $DIR/uninhabited_enum_branching2.rs:25:23: 25:24 goto -> bb3; // scope 1 at $DIR/uninhabited_enum_branching2.rs:25:23: 25:24 @@ -51,7 +51,7 @@ fn main() -> () { _7 = const "C"; // scope 1 at $DIR/uninhabited_enum_branching2.rs:24:21: 24:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching2.rs:24:21: 24:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _3 = &(*_7); // scope 1 at $DIR/uninhabited_enum_branching2.rs:24:21: 24:24 StorageDead(_7); // scope 1 at $DIR/uninhabited_enum_branching2.rs:24:23: 24:24 goto -> bb3; // scope 1 at $DIR/uninhabited_enum_branching2.rs:24:23: 24:24 @@ -70,7 +70,7 @@ fn main() -> () { _13 = const "D"; // scope 1 at $DIR/uninhabited_enum_branching2.rs:32:21: 32:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching2.rs:32:21: 32:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _9 = &(*_13); // scope 1 at $DIR/uninhabited_enum_branching2.rs:32:21: 32:24 StorageDead(_13); // scope 1 at $DIR/uninhabited_enum_branching2.rs:32:23: 32:24 goto -> bb6; // scope 1 at $DIR/uninhabited_enum_branching2.rs:32:23: 32:24 @@ -81,7 +81,7 @@ fn main() -> () { _12 = const "C"; // scope 1 at $DIR/uninhabited_enum_branching2.rs:31:21: 31:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching2.rs:31:21: 31:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _9 = &(*_12); // scope 1 at $DIR/uninhabited_enum_branching2.rs:31:21: 31:24 StorageDead(_12); // scope 1 at $DIR/uninhabited_enum_branching2.rs:31:23: 31:24 goto -> bb6; // scope 1 at $DIR/uninhabited_enum_branching2.rs:31:23: 31:24 diff --git a/src/test/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff b/src/test/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff index 8622fccec88..1b06c730cda 100644 --- a/src/test/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff +++ b/src/test/mir-opt/uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff @@ -42,7 +42,7 @@ _8 = const "D"; // scope 1 at $DIR/uninhabited_enum_branching2.rs:25:21: 25:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching2.rs:25:21: 25:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _3 = &(*_8); // scope 1 at $DIR/uninhabited_enum_branching2.rs:25:21: 25:24 StorageDead(_8); // scope 1 at $DIR/uninhabited_enum_branching2.rs:25:23: 25:24 goto -> bb5; // scope 1 at $DIR/uninhabited_enum_branching2.rs:25:23: 25:24 @@ -52,7 +52,7 @@ _3 = const "A(Empty)"; // scope 1 at $DIR/uninhabited_enum_branching2.rs:22:24: 22:34 // mir::Constant // + span: $DIR/uninhabited_enum_branching2.rs:22:24: 22:34 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [65, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } goto -> bb5; // scope 1 at $DIR/uninhabited_enum_branching2.rs:22:24: 22:34 } @@ -61,7 +61,7 @@ _6 = const "B(Empty)"; // scope 1 at $DIR/uninhabited_enum_branching2.rs:23:24: 23:34 // mir::Constant // + span: $DIR/uninhabited_enum_branching2.rs:23:24: 23:34 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [66, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _3 = &(*_6); // scope 1 at $DIR/uninhabited_enum_branching2.rs:23:24: 23:34 StorageDead(_6); // scope 1 at $DIR/uninhabited_enum_branching2.rs:23:33: 23:34 goto -> bb5; // scope 1 at $DIR/uninhabited_enum_branching2.rs:23:33: 23:34 @@ -72,7 +72,7 @@ _7 = const "C"; // scope 1 at $DIR/uninhabited_enum_branching2.rs:24:21: 24:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching2.rs:24:21: 24:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _3 = &(*_7); // scope 1 at $DIR/uninhabited_enum_branching2.rs:24:21: 24:24 StorageDead(_7); // scope 1 at $DIR/uninhabited_enum_branching2.rs:24:23: 24:24 goto -> bb5; // scope 1 at $DIR/uninhabited_enum_branching2.rs:24:23: 24:24 @@ -92,7 +92,7 @@ _13 = const "D"; // scope 1 at $DIR/uninhabited_enum_branching2.rs:32:21: 32:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching2.rs:32:21: 32:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [68], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _9 = &(*_13); // scope 1 at $DIR/uninhabited_enum_branching2.rs:32:21: 32:24 StorageDead(_13); // scope 1 at $DIR/uninhabited_enum_branching2.rs:32:23: 32:24 goto -> bb10; // scope 1 at $DIR/uninhabited_enum_branching2.rs:32:23: 32:24 @@ -102,7 +102,7 @@ _9 = const "A(Empty)"; // scope 1 at $DIR/uninhabited_enum_branching2.rs:29:24: 29:34 // mir::Constant // + span: $DIR/uninhabited_enum_branching2.rs:29:24: 29:34 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [65, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } goto -> bb10; // scope 1 at $DIR/uninhabited_enum_branching2.rs:29:24: 29:34 } @@ -111,7 +111,7 @@ _11 = const "B(Empty)"; // scope 1 at $DIR/uninhabited_enum_branching2.rs:30:24: 30:34 // mir::Constant // + span: $DIR/uninhabited_enum_branching2.rs:30:24: 30:34 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [66, 40, 69, 109, 112, 116, 121, 41], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 8 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _9 = &(*_11); // scope 1 at $DIR/uninhabited_enum_branching2.rs:30:24: 30:34 StorageDead(_11); // scope 1 at $DIR/uninhabited_enum_branching2.rs:30:33: 30:34 goto -> bb10; // scope 1 at $DIR/uninhabited_enum_branching2.rs:30:33: 30:34 @@ -122,7 +122,7 @@ _12 = const "C"; // scope 1 at $DIR/uninhabited_enum_branching2.rs:31:21: 31:24 // mir::Constant // + span: $DIR/uninhabited_enum_branching2.rs:31:21: 31:24 - // + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [67], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1], len: Size { raw: 1 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 1 }) } + // + literal: Const { ty: &str, val: Value(Slice(..)) } _9 = &(*_12); // scope 1 at $DIR/uninhabited_enum_branching2.rs:31:21: 31:24 StorageDead(_12); // scope 1 at $DIR/uninhabited_enum_branching2.rs:31:23: 31:24 goto -> bb10; // scope 1 at $DIR/uninhabited_enum_branching2.rs:31:23: 31:24 diff --git a/src/test/rustdoc-gui/escape-key.goml b/src/test/rustdoc-gui/escape-key.goml index 6e305e81eee..8713bf65c84 100644 --- a/src/test/rustdoc-gui/escape-key.goml +++ b/src/test/rustdoc-gui/escape-key.goml @@ -4,17 +4,20 @@ goto: file://|DOC_PATH|/test_docs/index.html // First, we check that the search results are hidden when the Escape key is pressed. write: (".search-input", "test") wait-for: "#search h1" // The search element is empty before the first search -assert-attribute: ("#search", {"class": "content"}) +// Check that the currently displayed element is search. +wait-for: "#alternative-display #search" assert-attribute: ("#main-content", {"class": "content hidden"}) assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH) press-key: "Escape" -assert-attribute: ("#search", {"class": "content hidden"}) +// Checks that search is no longer in the displayed content. +wait-for: "#not-displayed #search" +assert-false: "#alternative-display #search" assert-attribute: ("#main-content", {"class": "content"}) assert-document-property: ({"URL": "index.html"}, [ENDS_WITH]) // Check that focusing the search input brings back the search results focus: ".search-input" -assert-attribute: ("#search", {"class": "content"}) +wait-for: "#alternative-display #search" assert-attribute: ("#main-content", {"class": "content hidden"}) assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH) @@ -24,8 +27,8 @@ click: "#help-button" assert-document-property: ({"URL": "index.html?search=test"}, [ENDS_WITH]) assert-attribute: ("#help", {"class": ""}) press-key: "Escape" +wait-for: "#alternative-display #search" assert-attribute: ("#help", {"class": "hidden"}) -assert-attribute: ("#search", {"class": "content"}) assert-attribute: ("#main-content", {"class": "content hidden"}) assert-document-property: ({"URL": "index.html?search=test"}, [ENDS_WITH]) @@ -37,5 +40,6 @@ assert-false: ".search-input:focus" assert: "#results a:focus" press-key: "Escape" assert-attribute: ("#help", {"class": "hidden"}) -assert-attribute: ("#search", {"class": "content hidden"}) +wait-for: "#not-displayed #search" +assert-false: "#alternative-display #search" assert-attribute: ("#main-content", {"class": "content"}) diff --git a/src/test/rustdoc-gui/implementors.goml b/src/test/rustdoc-gui/implementors.goml index 6460a917e92..f29613f78b1 100644 --- a/src/test/rustdoc-gui/implementors.goml +++ b/src/test/rustdoc-gui/implementors.goml @@ -18,3 +18,10 @@ assert: "#implementors-list .impl:nth-child(2) > .code-header.in-band" goto: file://|DOC_PATH|/test_docs/struct.HasEmptyTraits.html compare-elements-position-near-false: ("#impl-EmptyTrait1", "#impl-EmptyTrait2", {"y": 30}) compare-elements-position-near: ("#impl-EmptyTrait3 h3", "#impl-EmptyTrait3 .item-info", {"y": 30}) + +// Now check that re-exports work correctly. +// There should be exactly one impl shown on both of these pages. +goto: file://|DOC_PATH|/lib2/trait.TraitToReexport.html +assert-count: ("#implementors-list .impl", 1) +goto: file://|DOC_PATH|/implementors/trait.TraitToReexport.html +assert-count: ("#implementors-list .impl", 1) diff --git a/src/test/rustdoc-gui/search-reexport.goml b/src/test/rustdoc-gui/search-reexport.goml new file mode 100644 index 00000000000..557781d4810 --- /dev/null +++ b/src/test/rustdoc-gui/search-reexport.goml @@ -0,0 +1,29 @@ +// Checks that the reexports are present in the search index, can have +// doc aliases and are highligted when their ID is the hash of the page. +goto: file://|DOC_PATH|/test_docs/index.html +local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"} +reload: +// First we check that the reexport has the correct ID and no background color. +assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;") +assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"}) +write: (".search-input", "TheStdReexport") +wait-for: "//a[@class='result-import']" +assert-attribute: ( + "//a[@class='result-import']", + {"href": "../test_docs/index.html#reexport.TheStdReexport"}, +) +assert-text: ("//a[@class='result-import']", "test_docs::TheStdReexport") +click: "//a[@class='result-import']" +// We check that it has the background modified thanks to the focus. +wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgb(73, 74, 61)"}) + +// We now check that the alias is working as well on the reexport. +write: (".search-input", "AliasForTheStdReexport") +wait-for: "//a[@class='result-import']" +assert-text: ( + "//a[@class='result-import']", + "AliasForTheStdReexport - see test_docs::TheStdReexport", +) +// Same thing again, we click on it to ensure the background is once again set as expected. +click: "//a[@class='result-import']" +wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgb(73, 74, 61)"}) diff --git a/src/test/rustdoc-gui/search-tab-change-title-fn-sig.goml b/src/test/rustdoc-gui/search-tab-change-title-fn-sig.goml new file mode 100644 index 00000000000..763927f9d0f --- /dev/null +++ b/src/test/rustdoc-gui/search-tab-change-title-fn-sig.goml @@ -0,0 +1,64 @@ +// Checks that the search tab results work correctly with function signature syntax +// First, try a search-by-name +goto: file://|DOC_PATH|/test_docs/index.html +write: (".search-input", "Foo") +// Waiting for the search results to appear... +wait-for: "#titles" +assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) +assert-text: ("#titles > button:nth-of-type(1)", "In Names", STARTS_WITH) +assert: "input.search-input:focus" +// Use left-right keys +press-key: "ArrowDown" +assert: "#results > .search-results.active > a:nth-of-type(1):focus" +press-key: "ArrowRight" +wait-for-attribute: ("#titles > button:nth-of-type(2)", {"class": "selected"}) +press-key: "ArrowRight" +wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"}) +press-key: "ArrowRight" +wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) +press-key: "ArrowLeft" +wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"}) + +// Now try search-by-return +goto: file://|DOC_PATH|/test_docs/index.html +write: (".search-input", "-> String") +// Waiting for the search results to appear... +wait-for: "#titles" +assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) +assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH) +assert: "input.search-input:focus" +// Use left-right keys +press-key: "ArrowDown" +assert: "#results > .search-results.active > a:nth-of-type(1):focus" +press-key: "ArrowRight" +wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) +press-key: "ArrowRight" +wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) +press-key: "ArrowRight" +wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) +press-key: "ArrowLeft" +wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) + +// Try with a search-by-return with no results +goto: file://|DOC_PATH|/test_docs/index.html +write: (".search-input", "-> Something") +// Waiting for the search results to appear... +wait-for: "#titles" +assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) +assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH) + +// Try with a search-by-parameter +goto: file://|DOC_PATH|/test_docs/index.html +write: (".search-input", "usize pattern") +// Waiting for the search results to appear... +wait-for: "#titles" +assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) +assert-text: ("#titles > button:nth-of-type(1)", "In Function Parameters", STARTS_WITH) + +// Try with a search-by-parameter-and-return +goto: file://|DOC_PATH|/test_docs/index.html +write: (".search-input", "pattern -> str") +// Waiting for the search results to appear... +wait-for: "#titles" +assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) +assert-text: ("#titles > button:nth-of-type(1)", "In Function Signatures", STARTS_WITH) diff --git a/src/test/rustdoc-gui/search-tab-selection-if-current-is-empty.goml b/src/test/rustdoc-gui/search-tab-selection-if-current-is-empty.goml deleted file mode 100644 index 52b3ceae7b1..00000000000 --- a/src/test/rustdoc-gui/search-tab-selection-if-current-is-empty.goml +++ /dev/null @@ -1,23 +0,0 @@ -// Checks that the first non-empty search result tab is selected if the default/currently selected -// one is empty. -goto: file://|DOC_PATH|/test_docs/index.html -write: (".search-input", "Foo") -// Waiting for the search results to appear... -wait-for: "#titles" -assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) - -// To go back to the original "state" -goto: file://|DOC_PATH|/test_docs/index.html -write: (".search-input", "-> String") -// Waiting for the search results to appear... -wait-for: "#titles" -// With this search, only the last tab shouldn't be empty so it should be selected. -assert-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"}) - -// To go back to the original "state" -goto: file://|DOC_PATH|/test_docs/index.html -write: (".search-input", "-> Something") -// Waiting for the search results to appear... -wait-for: "#titles" -// With this search, all the tabs are empty so the first one should remain selected. -assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) diff --git a/src/test/rustdoc-gui/settings.goml b/src/test/rustdoc-gui/settings.goml new file mode 100644 index 00000000000..6c4611b1cb2 --- /dev/null +++ b/src/test/rustdoc-gui/settings.goml @@ -0,0 +1,67 @@ +// This test ensures that the settings menu display is working as expected. +goto: file://|DOC_PATH|/test_docs/index.html +// First, we check that the settings page doesn't exist. +assert-false: "#settings" +// We now click on the settings button. +click: "#settings-menu" +wait-for: "#settings" +assert: "#main-content.hidden" +assert-css: ("#settings", {"display": "block"}) +// Let's close it by clicking on the same button. +click: "#settings-menu" +assert-false: "#alternative-display #settings" +assert: "#not-displayed #settings" +assert: "#main-content:not(.hidden)" + +// Let's open and then close it again with the "close settings" button. +click: "#settings-menu" +wait-for: "#alternative-display #settings" +assert: "#main-content.hidden" +click: "#back" +wait-for: "#not-displayed #settings" +assert: "#main-content:not(.hidden)" + +// Let's check that pressing "ESCAPE" is closing it. +click: "#settings-menu" +wait-for: "#alternative-display #settings" +press-key: "Escape" +wait-for: "#not-displayed #settings" +assert: "#main-content:not(.hidden)" + +// Let's click on it when the search results are displayed. +focus: ".search-input" +write: "test" +wait-for: "#alternative-display #search" +click: "#settings-menu" +wait-for: "#alternative-display #settings" +assert: "#not-displayed #search" +assert: "#main-content.hidden" + +// Now let's check the content of the settings menu. +local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"} +reload: +click: "#settings-menu" +wait-for: "#settings" + +// We check that the "Use system theme" is disabled. +assert-property: ("#use-system-theme", {"checked": "false"}) +assert: "//*[@class='setting-line']/*[text()='Use system theme']" +// Meaning that only the "theme" menu is showing up. +assert: ".setting-line:not(.hidden) #theme" +assert: ".setting-line.hidden #preferred-dark-theme" +assert: ".setting-line.hidden #preferred-light-theme" + +// We check that the correct theme is selected. +assert-property: ("#theme .choices #theme-dark", {"checked": "true"}) + +// We now switch the display. +click: "#use-system-theme" +// Wait for the hidden element to show up. +wait-for: ".setting-line:not(.hidden) #preferred-dark-theme" +assert: ".setting-line:not(.hidden) #preferred-light-theme" +// Check that the theme picking is hidden. +assert: ".setting-line.hidden #theme" + +// We check their text as well. +assert-text: ("#preferred-dark-theme .setting-name", "Preferred dark theme") +assert-text: ("#preferred-light-theme .setting-name", "Preferred light theme") diff --git a/src/test/rustdoc-gui/sidebar.goml b/src/test/rustdoc-gui/sidebar.goml index 6b79b00d3f7..32fe3334f36 100644 --- a/src/test/rustdoc-gui/sidebar.goml +++ b/src/test/rustdoc-gui/sidebar.goml @@ -13,15 +13,16 @@ assert-css: ("#all-types", {"color": "rgb(53, 109, 164)"}) // We check that we have the crates list and that the "current" on is "test_docs". assert-text: (".sidebar-elems .crate > ul > li > a.current", "test_docs") // And we're also supposed to have the list of items in the current module. -assert-text: (".sidebar-elems section ul > li:nth-child(1)", "Modules") -assert-text: (".sidebar-elems section ul > li:nth-child(2)", "Macros") -assert-text: (".sidebar-elems section ul > li:nth-child(3)", "Structs") -assert-text: (".sidebar-elems section ul > li:nth-child(4)", "Enums") -assert-text: (".sidebar-elems section ul > li:nth-child(5)", "Traits") -assert-text: (".sidebar-elems section ul > li:nth-child(6)", "Functions") -assert-text: (".sidebar-elems section ul > li:nth-child(7)", "Type Definitions") -assert-text: (".sidebar-elems section ul > li:nth-child(8)", "Unions") -assert-text: (".sidebar-elems section ul > li:nth-child(9)", "Keywords") +assert-text: (".sidebar-elems section ul > li:nth-child(1)", "Re-exports") +assert-text: (".sidebar-elems section ul > li:nth-child(2)", "Modules") +assert-text: (".sidebar-elems section ul > li:nth-child(3)", "Macros") +assert-text: (".sidebar-elems section ul > li:nth-child(4)", "Structs") +assert-text: (".sidebar-elems section ul > li:nth-child(5)", "Enums") +assert-text: (".sidebar-elems section ul > li:nth-child(6)", "Traits") +assert-text: (".sidebar-elems section ul > li:nth-child(7)", "Functions") +assert-text: (".sidebar-elems section ul > li:nth-child(8)", "Type Definitions") +assert-text: (".sidebar-elems section ul > li:nth-child(9)", "Unions") +assert-text: (".sidebar-elems section ul > li:nth-child(10)", "Keywords") assert-text: ("#structs + .item-table .item-left > a", "Foo") click: "#structs + .item-table .item-left > a" diff --git a/src/test/rustdoc-gui/src/lib2/implementors/lib.rs b/src/test/rustdoc-gui/src/lib2/implementors/lib.rs index 6417a6ac5af..1620e842291 100644 --- a/src/test/rustdoc-gui/src/lib2/implementors/lib.rs +++ b/src/test/rustdoc-gui/src/lib2/implementors/lib.rs @@ -9,3 +9,12 @@ pub struct Struct; impl Whatever for Struct { type Foo = u8; } + +mod traits { + pub trait TraitToReexport { + fn method() {} + } +} + +#[doc(inline)] +pub use traits::TraitToReexport; diff --git a/src/test/rustdoc-gui/src/lib2/lib.rs b/src/test/rustdoc-gui/src/lib2/lib.rs index 83e86c43934..d06b46f952d 100644 --- a/src/test/rustdoc-gui/src/lib2/lib.rs +++ b/src/test/rustdoc-gui/src/lib2/lib.rs @@ -43,6 +43,13 @@ impl implementors::Whatever for Foo { type Foo = u32; } +#[doc(inline)] +pub use implementors::TraitToReexport; + +pub struct StructToImplOnReexport; + +impl TraitToReexport for StructToImplOnReexport {} + pub mod sub_mod { /// ```txt /// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa diff --git a/src/test/rustdoc-gui/src/test_docs/lib.rs b/src/test/rustdoc-gui/src/test_docs/lib.rs index 348b1a65c78..b6fe9eb2565 100644 --- a/src/test/rustdoc-gui/src/test_docs/lib.rs +++ b/src/test/rustdoc-gui/src/test_docs/lib.rs @@ -274,3 +274,6 @@ impl EmptyTrait3 for HasEmptyTraits {} mod macros; pub use macros::*; + +#[doc(alias = "AliasForTheStdReexport")] +pub use ::std as TheStdReexport; diff --git a/src/test/rustdoc-gui/theme-change.goml b/src/test/rustdoc-gui/theme-change.goml index 333391ba279..9706511ea19 100644 --- a/src/test/rustdoc-gui/theme-change.goml +++ b/src/test/rustdoc-gui/theme-change.goml @@ -9,6 +9,7 @@ click: "#theme-choices > button:last-child" wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" }) goto: file://|DOC_PATH|/settings.html +wait-for: "#settings" click: "#theme-light" wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" }) assert-local-storage: { "rustdoc-theme": "light" } diff --git a/src/test/rustdoc-gui/theme-in-history.goml b/src/test/rustdoc-gui/theme-in-history.goml index 3b66c85d8da..f576ced1c62 100644 --- a/src/test/rustdoc-gui/theme-in-history.goml +++ b/src/test/rustdoc-gui/theme-in-history.goml @@ -1,15 +1,19 @@ // Ensures that the theme is working when going back in history. goto: file://|DOC_PATH|/test_docs/index.html // Set the theme to dark. -local-storage: {"rustdoc-theme": "dark", "rustdoc-preferred-dark-theme": "dark", "rustdoc-use-system-theme": "false"} +local-storage: { + "rustdoc-theme": "dark", + "rustdoc-preferred-dark-theme": "dark", + "rustdoc-use-system-theme": "false", +} // We reload the page so the local storage settings are being used. reload: assert-css: ("body", { "background-color": "rgb(53, 53, 53)" }) assert-local-storage: { "rustdoc-theme": "dark" } // Now we go to the settings page. -click: "#settings-menu" -wait-for: ".settings" +goto: file://|DOC_PATH|/settings.html +wait-for: "#settings" // We change the theme to "light". click: "#theme-light" wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" }) @@ -18,7 +22,7 @@ assert-local-storage: { "rustdoc-theme": "light" } // We go back in history. history-go-back: // Confirm that we're not on the settings page. -assert-false: ".settings" +assert-false: "#settings" // Check that the current theme is still "light". assert-css: ("body", { "background-color": "rgb(255, 255, 255)" }) assert-local-storage: { "rustdoc-theme": "light" } diff --git a/src/test/rustdoc-ui/block-doc-comment.rs b/src/test/rustdoc-ui/block-doc-comment.rs index c60dfa3f951..ce529916e5e 100644 --- a/src/test/rustdoc-ui/block-doc-comment.rs +++ b/src/test/rustdoc-ui/block-doc-comment.rs @@ -1,5 +1,6 @@ // check-pass // compile-flags:--test +// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" // This test ensures that no code block is detected in the doc comments. diff --git a/src/test/rustdoc-ui/block-doc-comment.stdout b/src/test/rustdoc-ui/block-doc-comment.stdout index e5c27bebbdb..7326c0a25a0 100644 --- a/src/test/rustdoc-ui/block-doc-comment.stdout +++ b/src/test/rustdoc-ui/block-doc-comment.stdout @@ -1,5 +1,5 @@ running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/src/test/rustdoc-ui/intra-doc/assoc-field.rs b/src/test/rustdoc-ui/intra-doc/assoc-field.rs new file mode 100644 index 00000000000..e18404e4430 --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/assoc-field.rs @@ -0,0 +1,26 @@ +// Traits in scope are collected for doc links in field attributes. + +// check-pass +// aux-build: assoc-field-dep.rs + +extern crate assoc_field_dep; +pub use assoc_field_dep::*; + +#[derive(Clone)] +pub struct Struct; + +pub mod mod1 { + pub struct Fields { + /// [crate::Struct::clone] + pub field: u8, + } +} + +pub mod mod2 { + pub enum Fields { + V { + /// [crate::Struct::clone] + field: u8, + }, + } +} diff --git a/src/test/rustdoc-ui/intra-doc/auxiliary/assoc-field-dep.rs b/src/test/rustdoc-ui/intra-doc/auxiliary/assoc-field-dep.rs new file mode 100644 index 00000000000..cfb24fc2c66 --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/auxiliary/assoc-field-dep.rs @@ -0,0 +1,18 @@ +#[derive(Clone)] +pub struct Struct; + +pub mod dep_mod1 { + pub struct Fields { + /// [crate::Struct::clone] + pub field: u8, + } +} + +pub mod dep_mod2 { + pub enum Fields { + V { + /// [crate::Struct::clone] + field: u8, + }, + } +} diff --git a/src/test/rustdoc/check-source-code-urls-to-def.rs b/src/test/rustdoc/check-source-code-urls-to-def.rs index ca4179d403d..12c5df2871c 100644 --- a/src/test/rustdoc/check-source-code-urls-to-def.rs +++ b/src/test/rustdoc/check-source-code-urls-to-def.rs @@ -46,6 +46,24 @@ pub fn foo(a: u32, b: &str, c: String, d: Foo, e: bar::Bar, f: source_code::Sour // @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#14-16"]' 'Trait' pub fn foo2<T: bar::sub::Trait, V: Trait>(t: &T, v: &V, b: bool) {} +pub trait AnotherTrait {} +pub trait WhyNot {} + +// @has - '//a[@href="../../src/foo/check-source-code-urls-to-def.rs.html#49"]' 'AnotherTrait' +// @has - '//a[@href="../../src/foo/check-source-code-urls-to-def.rs.html#50"]' 'WhyNot' +pub fn foo3<T, V>(t: &T, v: &V) +where + T: AnotherTrait, + V: WhyNot +{} + +pub trait AnotherTrait2 {} + +// @has - '//a[@href="../../src/foo/check-source-code-urls-to-def.rs.html#60"]' 'AnotherTrait2' +pub fn foo4() { + let x: Vec<AnotherTrait2> = Vec::new(); +} + // @has - '//a[@href="../../foo/primitive.bool.html"]' 'bool' #[doc(primitive = "bool")] mod whatever {} diff --git a/src/test/rustdoc/hidden-impls.rs b/src/test/rustdoc/hidden-impls.rs index 935bfb26863..8f33a6604c2 100644 --- a/src/test/rustdoc/hidden-impls.rs +++ b/src/test/rustdoc/hidden-impls.rs @@ -12,6 +12,6 @@ pub mod __hidden { // @has foo/trait.Clone.html // @!has - 'Foo' -// @has implementors/foo/trait.Clone.js +// @has implementors/core/clone/trait.Clone.js // @!has - 'Foo' pub use std::clone::Clone; diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index efbf78ac87d..c63410fa35b 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -474,3 +474,11 @@ struct Subdiagnostic { #[subdiagnostic] note: Note, } + +#[derive(SessionDiagnostic)] +#[error(code = "E0123", slug = "foo")] +struct VecField { + #[primary_span] + #[label] + spans: Vec<Span>, +} diff --git a/src/test/ui/argument-suggestions/basic.rs b/src/test/ui/argument-suggestions/basic.rs index 765b2d5d68e..3e96322d67e 100644 --- a/src/test/ui/argument-suggestions/basic.rs +++ b/src/test/ui/argument-suggestions/basic.rs @@ -22,4 +22,7 @@ fn main() { missing(); //~ ERROR this function takes swapped("", 1); //~ ERROR arguments to this function are incorrect permuted(Y {}, Z {}, X {}); //~ ERROR arguments to this function are incorrect + + let closure = |x| x; + closure(); //~ ERROR this function takes } diff --git a/src/test/ui/argument-suggestions/basic.stderr b/src/test/ui/argument-suggestions/basic.stderr index 78f82b07619..8300a22c548 100644 --- a/src/test/ui/argument-suggestions/basic.stderr +++ b/src/test/ui/argument-suggestions/basic.stderr @@ -81,7 +81,23 @@ help: reorder these arguments LL | permuted(X {}, Y {}, Z {}); | ~~~~~~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 5 previous errors +error[E0057]: this function takes 1 argument but 0 arguments were supplied + --> $DIR/basic.rs:27:5 + | +LL | closure(); + | ^^^^^^^-- an argument is missing + | +note: closure defined here + --> $DIR/basic.rs:26:19 + | +LL | let closure = |x| x; + | ^^^ +help: provide the argument + | +LL | closure({_}); + | ~~~~~~~~~~~~ + +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0061, E0308. -For more information about an error, try `rustc --explain E0061`. +Some errors have detailed explanations: E0057, E0061, E0308. +For more information about an error, try `rustc --explain E0057`. diff --git a/src/test/ui/argument-suggestions/issue-96638.rs b/src/test/ui/argument-suggestions/issue-96638.rs new file mode 100644 index 00000000000..9c6e81ab8cc --- /dev/null +++ b/src/test/ui/argument-suggestions/issue-96638.rs @@ -0,0 +1,9 @@ +fn f(_: usize, _: &usize, _: usize) {} + +fn arg<T>() -> T { todo!() } + +fn main() { + let x = arg(); // `x` must be inferred + // The reference on `&x` is important to reproduce the ICE + f(&x, ""); //~ ERROR this function takes 3 arguments but 2 arguments were supplied +} diff --git a/src/test/ui/argument-suggestions/issue-96638.stderr b/src/test/ui/argument-suggestions/issue-96638.stderr new file mode 100644 index 00000000000..35190e2ca0d --- /dev/null +++ b/src/test/ui/argument-suggestions/issue-96638.stderr @@ -0,0 +1,19 @@ +error[E0061]: this function takes 3 arguments but 2 arguments were supplied + --> $DIR/issue-96638.rs:8:5 + | +LL | f(&x, ""); + | ^ -- an argument of type `usize` is missing + | +note: function defined here + --> $DIR/issue-96638.rs:1:4 + | +LL | fn f(_: usize, _: &usize, _: usize) {} + | ^ -------- --------- -------- +help: provide the argument + | +LL | f({usize}, &x, {usize}); + | ~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0061`. diff --git a/src/test/ui/associated-types/associated-types-issue-20346.stderr b/src/test/ui/associated-types/associated-types-issue-20346.stderr index 516057e53d2..a67cf99283c 100644 --- a/src/test/ui/associated-types/associated-types-issue-20346.stderr +++ b/src/test/ui/associated-types/associated-types-issue-20346.stderr @@ -12,8 +12,8 @@ note: expected this to be `Option<T>` | LL | type Item = T; | ^ - = note: expected enum `Option<T>` - found type `T` + = note: expected enum `Option<T>` + found type parameter `T` note: required by a bound in `is_iterator_of` --> $DIR/associated-types-issue-20346.rs:15:34 | diff --git a/src/test/ui/associated-types/higher-ranked-projection.bad.stderr b/src/test/ui/associated-types/higher-ranked-projection.bad.stderr index 1ac72e4b90c..65533f93c94 100644 --- a/src/test/ui/associated-types/higher-ranked-projection.bad.stderr +++ b/src/test/ui/associated-types/higher-ranked-projection.bad.stderr @@ -5,7 +5,7 @@ LL | foo(()); | ^^^ lifetime mismatch | = note: expected reference `&'a ()` - found type `&()` + found reference `&()` note: the lifetime requirement is introduced here --> $DIR/higher-ranked-projection.rs:15:33 | 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 746ced689d4..6f6e9b38e94 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 @@ -{"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":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}}]}]},"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}}],"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}],"spans":{"inner_span":{"lo":0,"hi":0},"inject_use_span":{"lo":0,"hi":0}},"id":0,"is_placeholder":false} +{"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},{"variant":"Ast","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":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":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}}],"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}],"spans":{"inner_span":{"lo":0,"hi":0},"inject_use_span":{"lo":0,"hi":0}},"id":0,"is_placeholder":false} diff --git a/src/test/ui/ast-json/ast-json-output.stdout b/src/test/ui/ast-json/ast-json-output.stdout index b0aaa663f38..5637ce596b3 100644 --- a/src/test/ui/ast-json/ast-json-output.stdout +++ b/src/test/ui/ast-json/ast-json-output.stdout @@ -1 +1 @@ -{"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":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":null}]},"span":{"lo":0,"hi":0}}]},"Alone"]]}}]}]},"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}}],"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":"rust_2015","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}],"spans":{"inner_span":{"lo":0,"hi":0},"inject_use_span":{"lo":0,"hi":0}},"id":0,"is_placeholder":false} +{"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},{"variant":"Ast","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":{"0":[[{"variant":"Token","fields":[{"kind":{"variant":"Literal","fields":[{"kind":"Str","symbol":"lib","suffix":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}}],"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":"rust_2015","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}],"spans":{"inner_span":{"lo":0,"hi":0},"inject_use_span":{"lo":0,"hi":0}},"id":0,"is_placeholder":false} diff --git a/src/test/ui/async-await/generator-desc.stderr b/src/test/ui/async-await/generator-desc.stderr index b205fd61915..3be8c552063 100644 --- a/src/test/ui/async-await/generator-desc.stderr +++ b/src/test/ui/async-await/generator-desc.stderr @@ -25,12 +25,17 @@ LL | fun(one(), two()); | arguments to this function are incorrect | note: while checking the return type of the `async fn` + --> $DIR/generator-desc.rs:5:16 + | +LL | async fn one() {} + | ^ checked the `Output` of this `async fn`, expected opaque type +note: while checking the return type of the `async fn` --> $DIR/generator-desc.rs:6:16 | LL | async fn two() {} | ^ checked the `Output` of this `async fn`, found opaque type - = note: expected type `impl Future<Output = ()>` (opaque type at <$DIR/generator-desc.rs:5:16>) - found opaque type `impl Future<Output = ()>` (opaque type at <$DIR/generator-desc.rs:6:16>) + = note: expected opaque type `impl Future<Output = ()>` (opaque type at <$DIR/generator-desc.rs:5:16>) + found opaque type `impl Future<Output = ()>` (opaque type at <$DIR/generator-desc.rs:6:16>) = help: consider `await`ing on both `Future`s = note: distinct uses of `impl Trait` result in different opaque types note: function defined here @@ -43,26 +48,26 @@ error[E0308]: mismatched types --> $DIR/generator-desc.rs:14:26 | LL | fun((async || {})(), (async || {})()); - | --- ^^^^^^^^^^^^^^^ expected `async` closure body, found a different `async` closure body - | | + | --- -- ^^^^^^^^^^^^^^^ expected `async` closure body, found a different `async` closure body + | | | + | | the expected `async` closure body | arguments to this function are incorrect | ::: $SRC_DIR/core/src/future/mod.rs:LL:COL | LL | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return> - | ------------------------------- the found opaque type + | ------------------------------- + | | + | the expected opaque type + | the found opaque type | - = note: expected type `impl Future<Output = ()>` (`async` closure body) - found opaque type `impl Future<Output = ()>` (`async` closure body) + = note: expected opaque type `impl Future<Output = ()>` (`async` closure body) + found opaque type `impl Future<Output = ()>` (`async` closure body) note: function defined here --> $DIR/generator-desc.rs:8:4 | LL | fn fun<F: Future<Output = ()>>(f1: F, f2: F) {} | ^^^ ----- ----- -help: consider `await`ing on the `Future` - | -LL | fun((async || {})(), (async || {})().await); - | ++++++ error: aborting due to 3 previous errors diff --git a/src/test/ui/async-await/no-async-const.stderr b/src/test/ui/async-await/no-async-const.stderr index ae13b90c3cf..a51dc88a4ed 100644 --- a/src/test/ui/async-await/no-async-const.stderr +++ b/src/test/ui/async-await/no-async-const.stderr @@ -7,7 +7,7 @@ LL | pub async const fn x() {} | | expected one of `extern`, `fn`, or `unsafe` | help: `const` must come before `async`: `const async` | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error diff --git a/src/test/ui/async-await/no-unsafe-async.stderr b/src/test/ui/async-await/no-unsafe-async.stderr index 0c362052501..f23d17d6bfa 100644 --- a/src/test/ui/async-await/no-unsafe-async.stderr +++ b/src/test/ui/async-await/no-unsafe-async.stderr @@ -12,7 +12,7 @@ LL | unsafe async fn g() {} LL | } | - the item list ends here | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: expected one of `extern` or `fn`, found keyword `async` --> $DIR/no-unsafe-async.rs:11:8 @@ -23,7 +23,7 @@ LL | unsafe async fn f() {} | | expected one of `extern` or `fn` | help: `async` must come before `unsafe`: `async unsafe` | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to 2 previous errors diff --git a/src/test/ui/async-await/suggest-missing-await.stderr b/src/test/ui/async-await/suggest-missing-await.stderr index ba9ed5cb65f..1196601ace0 100644 --- a/src/test/ui/async-await/suggest-missing-await.stderr +++ b/src/test/ui/async-await/suggest-missing-await.stderr @@ -60,8 +60,13 @@ LL | | LL | | }; | |_____- `if` and `else` have incompatible types | - = note: expected type `impl Future<Output = ()>` - found unit type `()` +note: while checking the return type of the `async fn` + --> $DIR/suggest-missing-await.rs:18:18 + | +LL | async fn dummy() {} + | ^ checked the `Output` of this `async fn`, expected opaque type + = note: expected opaque type `impl Future<Output = ()>` + found unit type `()` help: consider `await`ing on the `Future` | LL | dummy().await diff --git a/src/test/ui/attributes/issue-90873.rs b/src/test/ui/attributes/issue-90873.rs index 0f62d415308..1411f61744d 100644 --- a/src/test/ui/attributes/issue-90873.rs +++ b/src/test/ui/attributes/issue-90873.rs @@ -1,9 +1,9 @@ #![u=||{static d=||1;}] -//~^ unexpected token +//~^ unexpected expression //~| cannot find attribute `u` in this scope //~| missing type for `static` item #![a={impl std::ops::Neg for i8 {}}] -//~^ ERROR unexpected token +//~^ ERROR unexpected expression //~| ERROR cannot find attribute `a` in this scope //~| ERROR `main` function not found in crate `issue_90873` diff --git a/src/test/ui/attributes/issue-90873.stderr b/src/test/ui/attributes/issue-90873.stderr index fbdc05ef6f0..0852bb7ca8b 100644 --- a/src/test/ui/attributes/issue-90873.stderr +++ b/src/test/ui/attributes/issue-90873.stderr @@ -1,4 +1,4 @@ -error: unexpected token: `|| +error: unexpected expression: `|| { static d: _ = || 1; }` @@ -7,7 +7,7 @@ error: unexpected token: `|| LL | #![u=||{static d=||1;}] | ^^^^^^^^^^^^^^^^^ -error: unexpected token: `{ +error: unexpected expression: `{ impl std::ops::Neg for i8 {} }` --> $DIR/issue-90873.rs:6:6 diff --git a/src/test/ui/attributes/key-value-expansion-on-mac.rs b/src/test/ui/attributes/key-value-expansion-on-mac.rs index 95bc1c04961..c1d68d8cda9 100644 --- a/src/test/ui/attributes/key-value-expansion-on-mac.rs +++ b/src/test/ui/attributes/key-value-expansion-on-mac.rs @@ -7,8 +7,8 @@ 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)` +// the "unexpected expression" errors comes from the validation. +#[rustc_dummy = stringify!(b)] //~ ERROR unexpected expression: `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 index fa9ea543765..64ab892d997 100644 --- a/src/test/ui/attributes/key-value-expansion-on-mac.stderr +++ b/src/test/ui/attributes/key-value-expansion-on-mac.stderr @@ -1,4 +1,4 @@ -error: unexpected token: `stringify!(b)` +error: unexpected expression: `stringify!(b)` --> $DIR/key-value-expansion-on-mac.rs:11:17 | LL | #[rustc_dummy = stringify!(b)] diff --git a/src/test/ui/attributes/key-value-expansion.rs b/src/test/ui/attributes/key-value-expansion.rs index 08121413ee9..83d601e5e3a 100644 --- a/src/test/ui/attributes/key-value-expansion.rs +++ b/src/test/ui/attributes/key-value-expansion.rs @@ -18,13 +18,13 @@ macro_rules! bug { // Any expressions containing macro call `X` that's more complex than `X` itself. // Parentheses will work. -bug!((column!())); //~ ERROR unexpected token: `(7u32)` +bug!((column!())); //~ ERROR unexpected expression: `(7u32)` // Original test case. macro_rules! bug { () => { - bug!("bug" + stringify!(found)); //~ ERROR unexpected token: `"bug" + "found"` + bug!("bug" + stringify!(found)); //~ ERROR unexpected expression: `"bug" + "found"` }; ($test:expr) => { #[doc = $test] @@ -46,7 +46,7 @@ macro_rules! doc_comment { macro_rules! some_macro { ($t1: ty) => { doc_comment! {format!("{coor}", coor = stringify!($t1)).as_str()} - //~^ ERROR unexpected token: `{ + //~^ ERROR unexpected expression: `{ }; } diff --git a/src/test/ui/attributes/key-value-expansion.stderr b/src/test/ui/attributes/key-value-expansion.stderr index 1b7cb76b553..1b776322aaa 100644 --- a/src/test/ui/attributes/key-value-expansion.stderr +++ b/src/test/ui/attributes/key-value-expansion.stderr @@ -1,10 +1,10 @@ -error: unexpected token: `(7u32)` +error: unexpected expression: `(7u32)` --> $DIR/key-value-expansion.rs:21:6 | LL | bug!((column!())); | ^^^^^^^^^^^ -error: unexpected token: `"bug" + "found"` +error: unexpected expression: `"bug" + "found"` --> $DIR/key-value-expansion.rs:27:14 | LL | bug!("bug" + stringify!(found)); @@ -15,7 +15,7 @@ LL | bug!(); | = note: this error originates in the macro `bug` (in Nightly builds, run with -Z macro-backtrace for more info) -error: unexpected token: `{ +error: unexpected expression: `{ let res = ::alloc::fmt::format(::core::fmt::Arguments::new_v1(&[""], &[::core::fmt::ArgumentV1::new_display(&"u8")])); diff --git a/src/test/ui/borrowck/suggest-local-var-imm-and-mut.nll.stderr b/src/test/ui/borrowck/suggest-local-var-imm-and-mut.nll.stderr deleted file mode 100644 index 2ba0b6b28aa..00000000000 --- a/src/test/ui/borrowck/suggest-local-var-imm-and-mut.nll.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable - --> $DIR/suggest-local-var-imm-and-mut.rs:12:22 - | -LL | self.foo(self.bar()); - | ---------^^^^^^^^^^- - | | | | - | | | mutable borrow occurs here - | | immutable borrow later used by call - | immutable borrow occurs here - | -help: try adding a local storing this argument... - --> $DIR/suggest-local-var-imm-and-mut.rs:12:22 - | -LL | self.foo(self.bar()); - | ^^^^^^^^^^ -help: ...and then using that local as the argument to this call - --> $DIR/suggest-local-var-imm-and-mut.rs:12:13 - | -LL | self.foo(self.bar()); - | ^^^^^^^^^^^^^^^^^^^^ - -error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable - --> $DIR/suggest-local-var-imm-and-mut.rs:24:39 - | -LL | Self::foo(self, Self::bar(self)); - | --------- ---- ^^^^ mutable borrow occurs here - | | | - | | immutable borrow occurs here - | immutable borrow later used by call - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/two-phase-cannot-nest-mut-self-calls.stderr b/src/test/ui/borrowck/two-phase-cannot-nest-mut-self-calls.stderr index 85c7159952f..a89bb941532 100644 --- a/src/test/ui/borrowck/two-phase-cannot-nest-mut-self-calls.stderr +++ b/src/test/ui/borrowck/two-phase-cannot-nest-mut-self-calls.stderr @@ -13,23 +13,6 @@ LL | | LL | | 0 LL | | }); | |______- immutable borrow occurs here - | -help: try adding a local storing this argument... - --> $DIR/two-phase-cannot-nest-mut-self-calls.rs:16:9 - | -LL | vec.push(2); - | ^^^^^^^^^^^ -help: ...and then using that local as the argument to this call - --> $DIR/two-phase-cannot-nest-mut-self-calls.rs:14:5 - | -LL | / vec.get({ -LL | | -LL | | vec.push(2); -LL | | -LL | | -LL | | 0 -LL | | }); - | |______^ error: aborting due to previous error diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.base.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.base.stderr new file mode 100644 index 00000000000..cbbbde61917 --- /dev/null +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.base.stderr @@ -0,0 +1,25 @@ +error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable + --> $DIR/two-phase-reservation-sharing-interference-2.rs:19:5 + | +LL | let shared = &v; + | -- immutable borrow occurs here +LL | +LL | v.extend(shared); + | ^^------^^^^^^^^ + | | | + | | immutable borrow later used by call + | mutable borrow occurs here + +error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable + --> $DIR/two-phase-reservation-sharing-interference-2.rs:27:5 + | +LL | v.extend(&v); + | ^^------^--^ + | | | | + | | | immutable borrow occurs here + | | immutable borrow later used by call + | mutable borrow occurs here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2015.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2015.stderr index 6cff53399ca..69c3d7915e4 100644 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2015.stderr +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2015.stderr @@ -20,22 +20,6 @@ LL | v.extend(&v); | | immutable borrow later used by call | mutable borrow occurs here -warning: cannot borrow `v` as mutable because it is also borrowed as immutable - --> $DIR/two-phase-reservation-sharing-interference-2.rs:40:5 - | -LL | let shared = &v; - | -- immutable borrow occurs here -LL | -LL | v.push(shared.len()); - | ^^^^^^^------------^ - | | | - | | immutable borrow later used here - | mutable borrow occurs here - | - = note: `#[warn(mutable_borrow_reservation_conflict)]` on by default - = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future - = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159> - -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2018.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2018.stderr index 6cff53399ca..69c3d7915e4 100644 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2018.stderr +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.migrate2018.stderr @@ -20,22 +20,6 @@ LL | v.extend(&v); | | immutable borrow later used by call | mutable borrow occurs here -warning: cannot borrow `v` as mutable because it is also borrowed as immutable - --> $DIR/two-phase-reservation-sharing-interference-2.rs:40:5 - | -LL | let shared = &v; - | -- immutable borrow occurs here -LL | -LL | v.push(shared.len()); - | ^^^^^^^------------^ - | | | - | | immutable borrow later used here - | mutable borrow occurs here - | - = note: `#[warn(mutable_borrow_reservation_conflict)]` on by default - = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future - = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159> - -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll.stderr new file mode 100644 index 00000000000..cbbbde61917 --- /dev/null +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll.stderr @@ -0,0 +1,25 @@ +error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable + --> $DIR/two-phase-reservation-sharing-interference-2.rs:19:5 + | +LL | let shared = &v; + | -- immutable borrow occurs here +LL | +LL | v.extend(shared); + | ^^------^^^^^^^^ + | | | + | | immutable borrow later used by call + | mutable borrow occurs here + +error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable + --> $DIR/two-phase-reservation-sharing-interference-2.rs:27:5 + | +LL | v.extend(&v); + | ^^------^--^ + | | | | + | | | immutable borrow occurs here + | | immutable borrow later used by call + | mutable borrow occurs here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll2015.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll2015.stderr index 0ae6fe78c6a..69c3d7915e4 100644 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll2015.stderr +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll2015.stderr @@ -5,9 +5,9 @@ LL | let shared = &v; | -- immutable borrow occurs here LL | LL | v.extend(shared); - | ^^^^^^^^^------^ - | | | - | | immutable borrow later used here + | ^^------^^^^^^^^ + | | | + | | immutable borrow later used by call | mutable borrow occurs here error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable @@ -20,18 +20,6 @@ LL | v.extend(&v); | | immutable borrow later used by call | mutable borrow occurs here -error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable - --> $DIR/two-phase-reservation-sharing-interference-2.rs:40:5 - | -LL | let shared = &v; - | -- immutable borrow occurs here -LL | -LL | v.push(shared.len()); - | ^^^^^^^------------^ - | | | - | | immutable borrow later used here - | mutable borrow occurs here - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll2018.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll2018.stderr index 0ae6fe78c6a..69c3d7915e4 100644 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll2018.stderr +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.nll2018.stderr @@ -5,9 +5,9 @@ LL | let shared = &v; | -- immutable borrow occurs here LL | LL | v.extend(shared); - | ^^^^^^^^^------^ - | | | - | | immutable borrow later used here + | ^^------^^^^^^^^ + | | | + | | immutable borrow later used by call | mutable borrow occurs here error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable @@ -20,18 +20,6 @@ LL | v.extend(&v); | | immutable borrow later used by call | mutable borrow occurs here -error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable - --> $DIR/two-phase-reservation-sharing-interference-2.rs:40:5 - | -LL | let shared = &v; - | -- immutable borrow occurs here -LL | -LL | v.push(shared.len()); - | ^^^^^^^------------^ - | | | - | | immutable borrow later used here - | mutable borrow occurs here - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.rs b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.rs index 14f687c2378..3e125869ef1 100644 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.rs +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-2.rs @@ -1,50 +1,39 @@ -// Test for #56254, we previously allowed the last example on the 2018 -// edition. Make sure that we now emit a warning in that case and an error for -// everyone else. +// Test for #56254. The last example originally failed with the ast checker, was +// accidentally allowed under migrate/nll, then linted against in migrate mode +// but disallowed under NLL. Now, we accept it everywhere. //ignore-compare-mode-nll //ignore-compare-mode-polonius -//revisions: migrate2015 migrate2018 nll2015 nll2018 +//revisions: base nll //[migrate2018] edition:2018 //[nll2018] edition:2018 -#![cfg_attr(any(nll2015, nll2018), feature(nll))] +#![cfg_attr(nll, feature(nll))] fn double_conflicts() { let mut v = vec![0, 1, 2]; let shared = &v; v.extend(shared); - //[migrate2015]~^ ERROR cannot borrow `v` as mutable - //[nll2015]~^^ ERROR cannot borrow `v` as mutable - //[migrate2018]~^^^ ERROR cannot borrow `v` as mutable - //[nll2018]~^^^^ ERROR cannot borrow `v` as mutable + //[base]~^ ERROR cannot borrow `v` as mutable + //[nll]~^^ ERROR cannot borrow `v` as mutable } fn activation_conflict() { let mut v = vec![0, 1, 2]; v.extend(&v); - //[migrate2015]~^ ERROR cannot borrow `v` as mutable - //[nll2015]~^^ ERROR cannot borrow `v` as mutable - //[migrate2018]~^^^ ERROR cannot borrow `v` as mutable - //[nll2018]~^^^^ ERROR cannot borrow `v` as mutable + //[base]~^ ERROR cannot borrow `v` as mutable + //[nll]~^^ ERROR cannot borrow `v` as mutable } -fn reservation_conflict() { +fn reservation_allowed() { let mut v = vec![0, 1, 2]; let shared = &v; v.push(shared.len()); - //[nll2015]~^ ERROR cannot borrow `v` as mutable - //[nll2018]~^^ ERROR cannot borrow `v` as mutable - //[migrate2015]~^^^ WARNING cannot borrow `v` as mutable - //[migrate2015]~| WARNING may become a hard error in the future - - //[migrate2018]~^^^^^^ WARNING cannot borrow `v` as mutable - //[migrate2018]~| WARNING may become a hard error in the future } fn main() {} diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.nll.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.nll.stderr deleted file mode 100644 index 52e8de3c4ac..00000000000 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.nll.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable - --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:13:9 - | -LL | let shared = &v; - | -- immutable borrow occurs here -LL | -LL | v.push(shared.len()); - | ^^^^^^^------------^ - | | | - | | immutable borrow later used here - | mutable borrow occurs here - -error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable - --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:24:9 - | -LL | let shared = &v; - | -- immutable borrow occurs here -LL | -LL | v.push(shared.len()); - | ^^^^^^^------------^ - | | | - | | immutable borrow later used here - | mutable borrow occurs here - -error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable - --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:37:9 - | -LL | let shared = &v; - | -- immutable borrow occurs here -LL | -LL | v.push(shared.len()); - | ^^^^^^^------------^ - | | | - | | immutable borrow later used here - | mutable borrow occurs here - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.rs b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.rs deleted file mode 100644 index 0e1d77ace3f..00000000000 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Check that the future-compat-lint for the reservation conflict is -// handled like any other lint. - -// edition:2018 - -mod future_compat_allow { - #![allow(mutable_borrow_reservation_conflict)] - - fn reservation_conflict() { - let mut v = vec![0, 1, 2]; - let shared = &v; - - v.push(shared.len()); - } -} - -mod future_compat_warn { - #![warn(mutable_borrow_reservation_conflict)] - - fn reservation_conflict() { - let mut v = vec![0, 1, 2]; - let shared = &v; - - v.push(shared.len()); - //~^ WARNING cannot borrow `v` as mutable - //~| WARNING may become a hard error in the future - } -} - -mod future_compat_deny { - #![deny(mutable_borrow_reservation_conflict)] - - fn reservation_conflict() { - let mut v = vec![0, 1, 2]; - let shared = &v; - - v.push(shared.len()); - //~^ ERROR cannot borrow `v` as mutable - //~| WARNING may become a hard error in the future - } -} - -fn main() {} diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.stderr b/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.stderr deleted file mode 100644 index aab21c9e78b..00000000000 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference-future-compat-lint.stderr +++ /dev/null @@ -1,42 +0,0 @@ -warning: cannot borrow `v` as mutable because it is also borrowed as immutable - --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:24:9 - | -LL | let shared = &v; - | -- immutable borrow occurs here -LL | -LL | v.push(shared.len()); - | ^^^^^^^------------^ - | | | - | | immutable borrow later used here - | mutable borrow occurs here - | -note: the lint level is defined here - --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:18:13 - | -LL | #![warn(mutable_borrow_reservation_conflict)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future - = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159> - -error: cannot borrow `v` as mutable because it is also borrowed as immutable - --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:37:9 - | -LL | let shared = &v; - | -- immutable borrow occurs here -LL | -LL | v.push(shared.len()); - | ^^^^^^^------------^ - | | | - | | immutable borrow later used here - | mutable borrow occurs here - | -note: the lint level is defined here - --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:31:13 - | -LL | #![deny(mutable_borrow_reservation_conflict)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future - = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159> - -error: aborting due to previous error; 1 warning emitted - diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr index ee4907bb755..a8367766ae1 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr @@ -29,8 +29,8 @@ error: changes to closure capture in Rust 2021 will affect which traits the clos LL | thread::spawn(move || unsafe { | ^^^^^^^^^^^^^^ | | - | in Rust 2018, this closure implements `Sync` as `fptr` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr` is not fully captured and `fptr.0.0` does not implement `Sync` | in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr` is not fully captured and `fptr.0.0` does not implement `Send` + | in Rust 2018, this closure implements `Sync` as `fptr` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr` is not fully captured and `fptr.0.0` does not implement `Sync` ... LL | *fptr.0.0 = 20; | --------- in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0` diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr index 6594ec31653..2648b00435b 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr @@ -4,8 +4,8 @@ error: changes to closure capture in Rust 2021 will affect which traits the clos LL | let result = panic::catch_unwind(move || { | ^^^^^^^ | | - | in Rust 2018, this closure implements `UnwindSafe` as `f` implements `UnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe` because `f` is not fully captured and `f.0` does not implement `UnwindSafe` | in Rust 2018, this closure implements `RefUnwindSafe` as `f` implements `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `RefUnwindSafe` because `f` is not fully captured and `f.0` does not implement `RefUnwindSafe` + | in Rust 2018, this closure implements `UnwindSafe` as `f` implements `UnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe` because `f` is not fully captured and `f.0` does not implement `UnwindSafe` ... LL | f.0() | --- in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0` diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr index 0008f1b2c07..483eae6bb4b 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr @@ -94,8 +94,8 @@ error: changes to closure capture in Rust 2021 will affect which traits the clos LL | thread::spawn(move || unsafe { | ^^^^^^^^^^^^^^ | | - | in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Sync` | in Rust 2018, this closure implements `Send` as `fptr1` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Send` + | in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Sync` | in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr2` is not fully captured and `fptr2.0` does not implement `Send` ... LL | *fptr1.0.0 = 20; diff --git a/src/test/ui/closures/closure_cap_coerce_many_fail.stderr b/src/test/ui/closures/closure_cap_coerce_many_fail.stderr index bd2e31648cc..e25b33bbcdb 100644 --- a/src/test/ui/closures/closure_cap_coerce_many_fail.stderr +++ b/src/test/ui/closures/closure_cap_coerce_many_fail.stderr @@ -11,8 +11,8 @@ LL | | _ => unimplemented!(), LL | | }; | |_____- `match` arms have incompatible types | - = note: expected type `fn(i32, i32) -> i32 {add}` - found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:9:16: 9:43]` + = note: expected fn item `fn(i32, i32) -> i32 {add}` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:9:16: 9:43]` error[E0308]: `match` arms have incompatible types --> $DIR/closure_cap_coerce_many_fail.rs:18:16 @@ -20,15 +20,18 @@ error[E0308]: `match` arms have incompatible types LL | let _ = match "+" { | _____________- LL | | "+" => |a, b| (a + b) as i32, - | | --------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:37]` + | | --------------------- + | | | + | | the expected closure + | | this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:37]` LL | | "-" => |a, b| (a - b + cap) as i32, | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure LL | | _ => unimplemented!(), LL | | }; | |_____- `match` arms have incompatible types | - = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:37]` - found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:18:16: 18:43]` + = note: expected closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:37]` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:18:16: 18:43]` = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object @@ -38,15 +41,18 @@ error[E0308]: `match` arms have incompatible types LL | let _ = match "+" { | _____________- LL | | "+" => |a, b| (a + b + cap) as i32, - | | --------------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43]` + | | --------------------------- + | | | + | | the expected closure + | | this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43]` LL | | "-" => |a, b| (a - b) as i32, | | ^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure LL | | _ => unimplemented!(), LL | | }; | |_____- `match` arms have incompatible types | - = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43]` - found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:27:16: 27:37]` + = note: expected closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43]` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:27:16: 27:37]` = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object @@ -56,15 +62,18 @@ error[E0308]: `match` arms have incompatible types LL | let _ = match "+" { | _____________- LL | | "+" => |a, b| (a + b + cap) as i32, - | | --------------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43]` + | | --------------------------- + | | | + | | the expected closure + | | this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43]` LL | | "-" => |a, b| (a - b + cap) as i32, | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure LL | | _ => unimplemented!(), LL | | }; | |_____- `match` arms have incompatible types | - = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43]` - found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:35:16: 35:43]` + = note: expected closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43]` + found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:35:16: 35:43]` = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object diff --git a/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.stderr b/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.stderr index 4f266b166d6..36551e5afc6 100644 --- a/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.stderr +++ b/src/test/ui/coercion/coerce-reborrow-multi-arg-fail.stderr @@ -6,8 +6,8 @@ LL | test(&mut 7, &7); | | | arguments to this function are incorrect | - = note: expected type `&mut {integer}` - found reference `&{integer}` + = note: expected mutable reference `&mut {integer}` + found reference `&{integer}` note: function defined here --> $DIR/coerce-reborrow-multi-arg-fail.rs:1:4 | diff --git a/src/test/ui/const-generics/const-arg-in-const-arg.full.stderr b/src/test/ui/const-generics/const-arg-in-const-arg.full.stderr new file mode 100644 index 00000000000..dbbdb2a0ce3 --- /dev/null +++ b/src/test/ui/const-generics/const-arg-in-const-arg.full.stderr @@ -0,0 +1,162 @@ +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/const-arg-in-const-arg.rs:18:23 + | +LL | let _: [u8; faz::<'a>(&())]; + | ^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/const-arg-in-const-arg.rs:8:14 + | +LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } + | ^^ + +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/const-arg-in-const-arg.rs:21:23 + | +LL | let _: [u8; faz::<'b>(&())]; + | ^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/const-arg-in-const-arg.rs:8:14 + | +LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } + | ^^ + +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/const-arg-in-const-arg.rs:30:23 + | +LL | let _ = [0; faz::<'a>(&())]; + | ^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/const-arg-in-const-arg.rs:8:14 + | +LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } + | ^^ + +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/const-arg-in-const-arg.rs:33:23 + | +LL | let _ = [0; faz::<'b>(&())]; + | ^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/const-arg-in-const-arg.rs:8:14 + | +LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } + | ^^ + +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/const-arg-in-const-arg.rs:41:24 + | +LL | let _: Foo<{ faz::<'a>(&()) }>; + | ^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/const-arg-in-const-arg.rs:8:14 + | +LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } + | ^^ + +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/const-arg-in-const-arg.rs:44:24 + | +LL | let _: Foo<{ faz::<'b>(&()) }>; + | ^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/const-arg-in-const-arg.rs:8:14 + | +LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } + | ^^ + +error: unconstrained generic constant + --> $DIR/const-arg-in-const-arg.rs:13:12 + | +LL | let _: [u8; foo::<T>()]; + | ^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); foo::<T>()]:` + +error: unconstrained generic constant + --> $DIR/const-arg-in-const-arg.rs:15:12 + | +LL | let _: [u8; bar::<N>()]; + | ^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); bar::<N>()]:` + +error: unconstrained generic constant + --> $DIR/const-arg-in-const-arg.rs:36:12 + | +LL | let _: Foo<{ foo::<T>() }>; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { foo::<T>() }]:` + +error: unconstrained generic constant + --> $DIR/const-arg-in-const-arg.rs:38:12 + | +LL | let _: Foo<{ bar::<N>() }>; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { bar::<N>() }]:` + +error: unconstrained generic constant + --> $DIR/const-arg-in-const-arg.rs:25:17 + | +LL | let _ = [0; foo::<T>()]; + | ^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); foo::<T>()]:` + +error: unconstrained generic constant + --> $DIR/const-arg-in-const-arg.rs:27:17 + | +LL | let _ = [0; bar::<N>()]; + | ^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); bar::<N>()]:` + +error: unconstrained generic constant + --> $DIR/const-arg-in-const-arg.rs:47:19 + | +LL | let _ = Foo::<{ foo::<T>() }>; + | ^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { foo::<T>() }]:` + +error: unconstrained generic constant + --> $DIR/const-arg-in-const-arg.rs:49:19 + | +LL | let _ = Foo::<{ bar::<N>() }>; + | ^^^^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { bar::<N>() }]:` + +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/const-arg-in-const-arg.rs:52:27 + | +LL | let _ = Foo::<{ faz::<'a>(&()) }>; + | ^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/const-arg-in-const-arg.rs:8:14 + | +LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } + | ^^ + +error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/const-arg-in-const-arg.rs:55:27 + | +LL | let _ = Foo::<{ faz::<'b>(&()) }>; + | ^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/const-arg-in-const-arg.rs:8:14 + | +LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } + | ^^ + +error: aborting due to 16 previous errors + diff --git a/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr b/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr index 88d9ed46e1a..6ca9a2a4859 100644 --- a/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr +++ b/src/test/ui/const-generics/const-arg-in-const-arg.min.stderr @@ -8,7 +8,7 @@ LL | let _: [u8; foo::<T>()]; = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:14:23 + --> $DIR/const-arg-in-const-arg.rs:15:23 | LL | let _: [u8; bar::<N>()]; | ^ cannot perform const operation using `N` @@ -17,7 +17,7 @@ LL | let _: [u8; bar::<N>()]; = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:16:23 + --> $DIR/const-arg-in-const-arg.rs:18:23 | LL | let _: [u8; faz::<'a>(&())]; | ^^ @@ -26,7 +26,7 @@ LL | let _: [u8; faz::<'a>(&())]; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:18:23 + --> $DIR/const-arg-in-const-arg.rs:20:23 | LL | let _: [u8; baz::<'a>(&())]; | ^^ @@ -35,7 +35,7 @@ LL | let _: [u8; baz::<'a>(&())]; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:19:23 + --> $DIR/const-arg-in-const-arg.rs:21:23 | LL | let _: [u8; faz::<'b>(&())]; | ^^ @@ -44,7 +44,7 @@ LL | let _: [u8; faz::<'b>(&())]; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:21:23 + --> $DIR/const-arg-in-const-arg.rs:23:23 | LL | let _: [u8; baz::<'b>(&())]; | ^^ @@ -53,7 +53,7 @@ LL | let _: [u8; baz::<'b>(&())]; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:24:23 + --> $DIR/const-arg-in-const-arg.rs:27:23 | LL | let _ = [0; bar::<N>()]; | ^ cannot perform const operation using `N` @@ -62,7 +62,7 @@ LL | let _ = [0; bar::<N>()]; = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:26:23 + --> $DIR/const-arg-in-const-arg.rs:30:23 | LL | let _ = [0; faz::<'a>(&())]; | ^^ @@ -71,7 +71,7 @@ LL | let _ = [0; faz::<'a>(&())]; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:28:23 + --> $DIR/const-arg-in-const-arg.rs:32:23 | LL | let _ = [0; baz::<'a>(&())]; | ^^ @@ -80,7 +80,7 @@ LL | let _ = [0; baz::<'a>(&())]; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:29:23 + --> $DIR/const-arg-in-const-arg.rs:33:23 | LL | let _ = [0; faz::<'b>(&())]; | ^^ @@ -89,7 +89,7 @@ LL | let _ = [0; faz::<'b>(&())]; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:31:23 + --> $DIR/const-arg-in-const-arg.rs:35:23 | LL | let _ = [0; baz::<'b>(&())]; | ^^ @@ -98,7 +98,7 @@ LL | let _ = [0; baz::<'b>(&())]; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:32:24 + --> $DIR/const-arg-in-const-arg.rs:36:24 | LL | let _: Foo<{ foo::<T>() }>; | ^ cannot perform const operation using `T` @@ -107,7 +107,7 @@ LL | let _: Foo<{ foo::<T>() }>; = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:33:24 + --> $DIR/const-arg-in-const-arg.rs:38:24 | LL | let _: Foo<{ bar::<N>() }>; | ^ cannot perform const operation using `N` @@ -116,7 +116,7 @@ LL | let _: Foo<{ bar::<N>() }>; = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:35:24 + --> $DIR/const-arg-in-const-arg.rs:41:24 | LL | let _: Foo<{ faz::<'a>(&()) }>; | ^^ @@ -125,7 +125,7 @@ LL | let _: Foo<{ faz::<'a>(&()) }>; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:37:24 + --> $DIR/const-arg-in-const-arg.rs:43:24 | LL | let _: Foo<{ baz::<'a>(&()) }>; | ^^ @@ -134,7 +134,7 @@ LL | let _: Foo<{ baz::<'a>(&()) }>; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:38:24 + --> $DIR/const-arg-in-const-arg.rs:44:24 | LL | let _: Foo<{ faz::<'b>(&()) }>; | ^^ @@ -143,7 +143,7 @@ LL | let _: Foo<{ faz::<'b>(&()) }>; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:40:24 + --> $DIR/const-arg-in-const-arg.rs:46:24 | LL | let _: Foo<{ baz::<'b>(&()) }>; | ^^ @@ -152,7 +152,7 @@ LL | let _: Foo<{ baz::<'b>(&()) }>; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:41:27 + --> $DIR/const-arg-in-const-arg.rs:47:27 | LL | let _ = Foo::<{ foo::<T>() }>; | ^ cannot perform const operation using `T` @@ -161,7 +161,7 @@ LL | let _ = Foo::<{ foo::<T>() }>; = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/const-arg-in-const-arg.rs:42:27 + --> $DIR/const-arg-in-const-arg.rs:49:27 | LL | let _ = Foo::<{ bar::<N>() }>; | ^ cannot perform const operation using `N` @@ -170,7 +170,7 @@ LL | let _ = Foo::<{ bar::<N>() }>; = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:44:27 + --> $DIR/const-arg-in-const-arg.rs:52:27 | LL | let _ = Foo::<{ faz::<'a>(&()) }>; | ^^ @@ -179,7 +179,7 @@ LL | let _ = Foo::<{ faz::<'a>(&()) }>; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:46:27 + --> $DIR/const-arg-in-const-arg.rs:54:27 | LL | let _ = Foo::<{ baz::<'a>(&()) }>; | ^^ @@ -188,7 +188,7 @@ LL | let _ = Foo::<{ baz::<'a>(&()) }>; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:47:27 + --> $DIR/const-arg-in-const-arg.rs:55:27 | LL | let _ = Foo::<{ faz::<'b>(&()) }>; | ^^ @@ -197,7 +197,7 @@ LL | let _ = Foo::<{ faz::<'b>(&()) }>; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0658]: a non-static lifetime is not allowed in a `const` - --> $DIR/const-arg-in-const-arg.rs:49:27 + --> $DIR/const-arg-in-const-arg.rs:57:27 | LL | let _ = Foo::<{ baz::<'b>(&()) }>; | ^^ @@ -206,7 +206,7 @@ LL | let _ = Foo::<{ baz::<'b>(&()) }>; = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable error[E0747]: unresolved item provided when a constant was expected - --> $DIR/const-arg-in-const-arg.rs:14:23 + --> $DIR/const-arg-in-const-arg.rs:15:23 | LL | let _: [u8; bar::<N>()]; | ^ @@ -217,7 +217,7 @@ LL | let _: [u8; bar::<{ N }>()]; | + + error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:16:23 + --> $DIR/const-arg-in-const-arg.rs:18:23 | LL | let _: [u8; faz::<'a>(&())]; | ^^ @@ -229,7 +229,7 @@ LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:19:23 + --> $DIR/const-arg-in-const-arg.rs:21:23 | LL | let _: [u8; faz::<'b>(&())]; | ^^ @@ -241,7 +241,7 @@ LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ error[E0747]: unresolved item provided when a constant was expected - --> $DIR/const-arg-in-const-arg.rs:24:23 + --> $DIR/const-arg-in-const-arg.rs:27:23 | LL | let _ = [0; bar::<N>()]; | ^ @@ -252,7 +252,7 @@ LL | let _ = [0; bar::<{ N }>()]; | + + error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:26:23 + --> $DIR/const-arg-in-const-arg.rs:30:23 | LL | let _ = [0; faz::<'a>(&())]; | ^^ @@ -264,7 +264,7 @@ LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:29:23 + --> $DIR/const-arg-in-const-arg.rs:33:23 | LL | let _ = [0; faz::<'b>(&())]; | ^^ @@ -276,7 +276,7 @@ LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ error[E0747]: unresolved item provided when a constant was expected - --> $DIR/const-arg-in-const-arg.rs:33:24 + --> $DIR/const-arg-in-const-arg.rs:38:24 | LL | let _: Foo<{ bar::<N>() }>; | ^ @@ -287,7 +287,7 @@ LL | let _: Foo<{ bar::<{ N }>() }>; | + + error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:35:24 + --> $DIR/const-arg-in-const-arg.rs:41:24 | LL | let _: Foo<{ faz::<'a>(&()) }>; | ^^ @@ -299,7 +299,7 @@ LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:38:24 + --> $DIR/const-arg-in-const-arg.rs:44:24 | LL | let _: Foo<{ faz::<'b>(&()) }>; | ^^ @@ -311,7 +311,7 @@ LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ error: constant expression depends on a generic parameter - --> $DIR/const-arg-in-const-arg.rs:23:17 + --> $DIR/const-arg-in-const-arg.rs:25:17 | LL | let _ = [0; foo::<T>()]; | ^^^^^^^^^^ @@ -319,7 +319,7 @@ LL | let _ = [0; foo::<T>()]; = note: this may fail depending on what value the parameter takes error[E0747]: unresolved item provided when a constant was expected - --> $DIR/const-arg-in-const-arg.rs:42:27 + --> $DIR/const-arg-in-const-arg.rs:49:27 | LL | let _ = Foo::<{ bar::<N>() }>; | ^ @@ -330,7 +330,7 @@ LL | let _ = Foo::<{ bar::<{ N }>() }>; | + + error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:44:27 + --> $DIR/const-arg-in-const-arg.rs:52:27 | LL | let _ = Foo::<{ faz::<'a>(&()) }>; | ^^ @@ -342,7 +342,7 @@ LL | const fn faz<'a>(_: &'a ()) -> usize { 13 } | ^^ error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present - --> $DIR/const-arg-in-const-arg.rs:47:27 + --> $DIR/const-arg-in-const-arg.rs:55:27 | LL | let _ = Foo::<{ faz::<'b>(&()) }>; | ^^ diff --git a/src/test/ui/const-generics/const-arg-in-const-arg.rs b/src/test/ui/const-generics/const-arg-in-const-arg.rs index b9daadb5474..44a4f560a24 100644 --- a/src/test/ui/const-generics/const-arg-in-const-arg.rs +++ b/src/test/ui/const-generics/const-arg-in-const-arg.rs @@ -1,7 +1,7 @@ -// revisions: min -// FIXME(generic_const_exprs): This test currently causes an ICE because -// we don't yet correctly deal with lifetimes, reenable this test once -// this is fixed. +// revisions: full min + +#![cfg_attr(full, feature(generic_const_exprs))] +#![cfg_attr(full, allow(incomplete_features))] const fn foo<T>() -> usize { std::mem::size_of::<T>() } const fn bar<const N: usize>() -> usize { N } @@ -10,43 +10,51 @@ const fn baz<'a>(_: &'a ()) -> usize where &'a (): Sized { 13 } struct Foo<const N: usize>; fn test<'a, 'b, T, const N: usize>() where &'b (): Sized { - let _: [u8; foo::<T>()]; //~ ERROR generic parameters may not - let _: [u8; bar::<N>()]; //~ ERROR generic parameters may not - //~^ ERROR unresolved item provided when a constant was expected - let _: [u8; faz::<'a>(&())]; //~ ERROR a non-static lifetime + let _: [u8; foo::<T>()]; //[min]~ ERROR generic parameters may not + //[full]~^ ERROR unconstrained generic constant + let _: [u8; bar::<N>()]; //[min]~ ERROR generic parameters may not + //[min]~^ ERROR unresolved item provided when a constant was expected + //[full]~^^ ERROR unconstrained generic constant + let _: [u8; faz::<'a>(&())]; //[min]~ ERROR a non-static lifetime //~^ ERROR cannot specify lifetime arguments - let _: [u8; baz::<'a>(&())]; //~ ERROR a non-static lifetime - let _: [u8; faz::<'b>(&())]; //~ ERROR a non-static lifetime + let _: [u8; baz::<'a>(&())]; //[min]~ ERROR a non-static lifetime + let _: [u8; faz::<'b>(&())]; //[min]~ ERROR a non-static lifetime //~^ ERROR cannot specify lifetime arguments - let _: [u8; baz::<'b>(&())]; //~ ERROR a non-static lifetime + let _: [u8; baz::<'b>(&())]; //[min]~ ERROR a non-static lifetime - let _ = [0; foo::<T>()]; //~ ERROR constant expression depends on a generic parameter - let _ = [0; bar::<N>()]; //~ ERROR generic parameters may not - //~^ ERROR unresolved item provided when a constant was expected - let _ = [0; faz::<'a>(&())]; //~ ERROR a non-static lifetime + let _ = [0; foo::<T>()]; //[min]~ ERROR constant expression depends on a generic parameter + //[full]~^ ERROR unconstrained generic constant + let _ = [0; bar::<N>()]; //[min]~ ERROR generic parameters may not + //[min]~^ ERROR unresolved item provided when a constant was expected + //[full]~^^ ERROR unconstrained generic constant + let _ = [0; faz::<'a>(&())]; //[min]~ ERROR a non-static lifetime //~^ ERROR cannot specify lifetime arguments - let _ = [0; baz::<'a>(&())]; //~ ERROR a non-static lifetime - let _ = [0; faz::<'b>(&())]; //~ ERROR a non-static lifetime + let _ = [0; baz::<'a>(&())]; //[min]~ ERROR a non-static lifetime + let _ = [0; faz::<'b>(&())]; //[min]~ ERROR a non-static lifetime //~^ ERROR cannot specify lifetime arguments - let _ = [0; baz::<'b>(&())]; //~ ERROR a non-static lifetime - let _: Foo<{ foo::<T>() }>; //~ ERROR generic parameters may not - let _: Foo<{ bar::<N>() }>; //~ ERROR generic parameters may not - //~^ ERROR unresolved item provided when a constant was expected - let _: Foo<{ faz::<'a>(&()) }>; //~ ERROR a non-static lifetime + let _ = [0; baz::<'b>(&())]; //[min]~ ERROR a non-static lifetime + let _: Foo<{ foo::<T>() }>; //[min]~ ERROR generic parameters may not + //[full]~^ ERROR unconstrained generic constant + let _: Foo<{ bar::<N>() }>; //[min]~ ERROR generic parameters may not + //[min]~^ ERROR unresolved item provided when a constant was expected + //[full]~^^ ERROR unconstrained generic constant + let _: Foo<{ faz::<'a>(&()) }>; //[min]~ ERROR a non-static lifetime //~^ ERROR cannot specify lifetime arguments - let _: Foo<{ baz::<'a>(&()) }>; //~ ERROR a non-static lifetime - let _: Foo<{ faz::<'b>(&()) }>; //~ ERROR a non-static lifetime + let _: Foo<{ baz::<'a>(&()) }>; //[min]~ ERROR a non-static lifetime + let _: Foo<{ faz::<'b>(&()) }>; //[min]~ ERROR a non-static lifetime //~^ ERROR cannot specify lifetime arguments - let _: Foo<{ baz::<'b>(&()) }>; //~ ERROR a non-static lifetime - let _ = Foo::<{ foo::<T>() }>; //~ ERROR generic parameters may not - let _ = Foo::<{ bar::<N>() }>; //~ ERROR generic parameters may not - //~^ ERROR unresolved item provided when a constant was expected - let _ = Foo::<{ faz::<'a>(&()) }>; //~ ERROR a non-static lifetime + let _: Foo<{ baz::<'b>(&()) }>; //[min]~ ERROR a non-static lifetime + let _ = Foo::<{ foo::<T>() }>; //[min]~ ERROR generic parameters may not + //[full]~^ ERROR unconstrained generic constant + let _ = Foo::<{ bar::<N>() }>; //[min]~ ERROR generic parameters may not + //[min]~^ ERROR unresolved item provided when a constant was expected + //[full]~^^ ERROR unconstrained generic constant + let _ = Foo::<{ faz::<'a>(&()) }>; //[min]~ ERROR a non-static lifetime //~^ ERROR cannot specify lifetime arguments - let _ = Foo::<{ baz::<'a>(&()) }>; //~ ERROR a non-static lifetime - let _ = Foo::<{ faz::<'b>(&()) }>; //~ ERROR a non-static lifetime + let _ = Foo::<{ baz::<'a>(&()) }>; //[min]~ ERROR a non-static lifetime + let _ = Foo::<{ faz::<'b>(&()) }>; //[min]~ ERROR a non-static lifetime //~^ ERROR cannot specify lifetime arguments - let _ = Foo::<{ baz::<'b>(&()) }>; //~ ERROR a non-static lifetime + let _ = Foo::<{ baz::<'b>(&()) }>; //[min]~ ERROR a non-static lifetime } fn main() {} diff --git a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.min.stderr b/src/test/ui/const-generics/const-argument-cross-crate-mismatch.min.stderr deleted file mode 100644 index e9854f0b9b5..00000000000 --- a/src/test/ui/const-generics/const-argument-cross-crate-mismatch.min.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error[E0308]: arguments to this function are incorrect - --> $DIR/const-argument-cross-crate-mismatch.rs:7:41 - | -LL | let _ = const_generic_lib::function(const_generic_lib::Struct([0u8, 1u8])); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^----------^ - | | - | expected `[u8; 3]`, found `[u8; 2]` - | -help: provide an argument of the correct type - | -LL | let _ = const_generic_lib::function(({[u8; 3]})); - | ^^^^^^^^^^^ - -error[E0308]: arguments to this function are incorrect - --> $DIR/const-argument-cross-crate-mismatch.rs:9:39 - | -LL | let _: const_generic_lib::Alias = const_generic_lib::Struct([0u8, 1u8, 2u8]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^---------------^ - | | - | expected `[u8; 2]`, found `[u8; 3]` - | -help: provide an argument of the correct type - | -LL | let _: const_generic_lib::Alias = ({[u8; 2]}); - | ^^^^^^^^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/const-argument-non-static-lifetime.min.stderr b/src/test/ui/const-generics/const-argument-non-static-lifetime.min.stderr new file mode 100644 index 00000000000..f1f3a9c3de0 --- /dev/null +++ b/src/test/ui/const-generics/const-argument-non-static-lifetime.min.stderr @@ -0,0 +1,12 @@ +error[E0658]: a non-static lifetime is not allowed in a `const` + --> $DIR/const-argument-non-static-lifetime.rs:15:17 + | +LL | let _: &'a (); + | ^^ + | + = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information + = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/const-generics/const-argument-non-static-lifetime.rs b/src/test/ui/const-generics/const-argument-non-static-lifetime.rs index 2aca7bdeb9a..36a569784ad 100644 --- a/src/test/ui/const-generics/const-argument-non-static-lifetime.rs +++ b/src/test/ui/const-generics/const-argument-non-static-lifetime.rs @@ -1,6 +1,8 @@ -// run-pass -// revisions: full -// FIXME(#75323) Omitted min revision for now due to ICE. +// [full] check-pass +// revisions: full min + +// regression test for #78180 +// compile-flags: -Zsave-analysis #![cfg_attr(full, feature(generic_const_exprs))] #![cfg_attr(full, allow(incomplete_features))] @@ -10,7 +12,7 @@ fn test<const N: usize>() {} fn wow<'a>() -> &'a () { test::<{ - let _: &'a (); + let _: &'a (); //[min]~ ERROR a non-static lifetime 3 }>(); &() diff --git a/src/test/ui/const-generics/issues/issue-775377.rs b/src/test/ui/const-generics/issues/issue-775377.rs new file mode 100644 index 00000000000..3cb8d3846ab --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-775377.rs @@ -0,0 +1,11 @@ +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +trait MyTrait<T> {} + +fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> { + //~^ ERROR overly complex generic constant + todo!() +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-775377.stderr b/src/test/ui/const-generics/issues/issue-775377.stderr new file mode 100644 index 00000000000..83946df4203 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-775377.stderr @@ -0,0 +1,11 @@ +error: overly complex generic constant + --> $DIR/issue-775377.rs:6:46 + | +LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constant + | + = help: consider moving this anonymous constant into a `const` function + = note: this operation may be supported in the future + +error: aborting due to previous error + diff --git a/src/test/ui/const-generics/issues/issue-83993.rs b/src/test/ui/const-generics/issues/issue-83993.rs new file mode 100644 index 00000000000..f2f05d9526b --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-83993.rs @@ -0,0 +1,14 @@ +// check-pass + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +fn bug<'a>() +where + for<'b> [(); { + let x: &'b (); + 0 + }]: +{} + +fn main() {} diff --git a/src/test/ui/consts/issue-90762.rs b/src/test/ui/consts/issue-90762.rs new file mode 100644 index 00000000000..78d387386f8 --- /dev/null +++ b/src/test/ui/consts/issue-90762.rs @@ -0,0 +1,31 @@ +// run-pass +#![allow(unreachable_code)] + +use std::sync::atomic::{AtomicBool, Ordering, AtomicUsize}; + +struct Print(usize); + +impl Drop for Print { + fn drop(&mut self) { + println!("{}", self.0); + FOO[self.0].store(true, Ordering::Relaxed); + assert_eq!(BAR.fetch_sub(1, Ordering::Relaxed), self.0); + } +} + +const A: Print = Print(0); +const B: Print = Print(1); + +static FOO: [AtomicBool; 3] = + [AtomicBool::new(false), AtomicBool::new(false), AtomicBool::new(false)]; +static BAR: AtomicUsize = AtomicUsize::new(2); + +fn main() { + loop { + std::mem::forget(({ A }, B, Print(2), break)); + } + for (i, b) in FOO.iter().enumerate() { + assert!(b.load(Ordering::Relaxed), "{} not set", i); + } + assert_eq!(BAR.fetch_add(1, Ordering::Relaxed), usize::max_value()); +} diff --git a/src/test/ui/consts/issue-90878-2.rs b/src/test/ui/consts/issue-90878-2.rs index ac5640646a8..e5bcecce6ee 100644 --- a/src/test/ui/consts/issue-90878-2.rs +++ b/src/test/ui/consts/issue-90878-2.rs @@ -1,4 +1,4 @@ - #![l=|x|[b;x ]] //~ ERROR unexpected token: `|x| [b; x]` + #![l=|x|[b;x ]] //~ ERROR unexpected expression: `|x| [b; x]` //~^ ERROR cannot find attribute `l` in this scope // notice the space at the start, diff --git a/src/test/ui/consts/issue-90878-2.stderr b/src/test/ui/consts/issue-90878-2.stderr index 4ccce36eedf..71b8d21fb4d 100644 --- a/src/test/ui/consts/issue-90878-2.stderr +++ b/src/test/ui/consts/issue-90878-2.stderr @@ -1,4 +1,4 @@ -error: unexpected token: `|x| [b; x]` +error: unexpected expression: `|x| [b; x]` --> $DIR/issue-90878-2.rs:1:7 | LL | #![l=|x|[b;x ]] diff --git a/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits.rs b/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits.rs index 0331e75b2fe..590475fa03a 100644 --- a/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits.rs +++ b/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits.rs @@ -29,7 +29,7 @@ mod x { mod y { use {Foo, Bar}; - #[rustc_then_this_would_need(typeck)] //~ ERROR no path + #[rustc_then_this_would_need(typeck)] //~ ERROR OK pub fn call_bar() { char::bar('a'); } diff --git a/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits.stderr b/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits.stderr index 08f382cc024..4e10437362c 100644 --- a/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits.stderr +++ b/src/test/ui/dep-graph/dep-graph-trait-impl-two-traits.stderr @@ -1,4 +1,4 @@ -error: no path from `x::<impl Foo for char>` to `typeck` +error: OK --> $DIR/dep-graph-trait-impl-two-traits.rs:32:5 | LL | #[rustc_then_this_would_need(typeck)] diff --git a/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr index 3d472bf6309..950e0223e22 100644 --- a/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr +++ b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr @@ -6,8 +6,8 @@ LL | (x, y) = &(1, 2); | | | expected reference, found tuple | - = note: expected type `&({integer}, {integer})` - found tuple `(_, _)` + = note: expected reference `&({integer}, {integer})` + found tuple `(_, _)` error: aborting due to previous error diff --git a/src/test/ui/destructuring-assignment/struct-or-enum-variant-path.rs b/src/test/ui/destructuring-assignment/struct-or-enum-variant-path.rs new file mode 100644 index 00000000000..8da7f90c524 --- /dev/null +++ b/src/test/ui/destructuring-assignment/struct-or-enum-variant-path.rs @@ -0,0 +1,34 @@ +// check-pass + +struct S; + +enum E { + V, +} + +type A = E; + +fn main() { + let mut a; + + (S, a) = (S, ()); + + (E::V, a) = (E::V, ()); + + (<E>::V, a) = (E::V, ()); + (A::V, a) = (E::V, ()); +} + +impl S { + fn check() { + let a; + (Self, a) = (S, ()); + } +} + +impl E { + fn check() { + let a; + (Self::V, a) = (E::V, ()); + } +} diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr index 55b08b74af0..a3004cbbe10 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr @@ -14,8 +14,8 @@ LL | (a, a, b) = (1, 2); | | | expected a tuple with 2 elements, found one with 3 elements | - = note: expected type `({integer}, {integer})` - found tuple `(_, _, _)` + = note: expected tuple `({integer}, {integer})` + found tuple `(_, _, _)` error[E0070]: invalid left-hand side of assignment --> $DIR/tuple_destructure_fail.rs:7:13 @@ -33,8 +33,8 @@ LL | (_,) = (1, 2); | | | expected a tuple with 2 elements, found one with 1 element | - = note: expected type `({integer}, {integer})` - found tuple `(_,)` + = note: expected tuple `({integer}, {integer})` + found tuple `(_,)` error: aborting due to 4 previous errors diff --git a/src/test/ui/error-codes/E0057.stderr b/src/test/ui/error-codes/E0057.stderr index a151b20f865..4b4d30a8387 100644 --- a/src/test/ui/error-codes/E0057.stderr +++ b/src/test/ui/error-codes/E0057.stderr @@ -4,6 +4,11 @@ error[E0057]: this function takes 1 argument but 0 arguments were supplied LL | let a = f(); | ^-- an argument is missing | +note: closure defined here + --> $DIR/E0057.rs:2:13 + | +LL | let f = |x| x * 3; + | ^^^ help: provide the argument | LL | let a = f({_}); @@ -15,6 +20,11 @@ error[E0057]: this function takes 1 argument but 2 arguments were supplied LL | let c = f(2, 3); | ^ - argument unexpected | +note: closure defined here + --> $DIR/E0057.rs:2:13 + | +LL | let f = |x| x * 3; + | ^^^ help: remove the extra argument | LL | let c = f(2); diff --git a/src/test/ui/error-codes/E0502.nll.stderr b/src/test/ui/error-codes/E0502.nll.stderr deleted file mode 100644 index a3c7ef76189..00000000000 --- a/src/test/ui/error-codes/E0502.nll.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0502]: cannot borrow `*a` as mutable because it is also borrowed as immutable - --> $DIR/E0502.rs:4:9 - | -LL | let ref y = a; - | ----- immutable borrow occurs here -LL | bar(a); - | ^ mutable borrow occurs here -LL | y.use_ref(); - | ----------- immutable borrow later used here - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/estr-subtyping.rs b/src/test/ui/estr-subtyping.rs deleted file mode 100644 index 9c5825fff85..00000000000 --- a/src/test/ui/estr-subtyping.rs +++ /dev/null @@ -1,15 +0,0 @@ -fn wants_uniq(x: String) { } -fn wants_slice(x: &str) { } - -fn has_uniq(x: String) { - wants_uniq(x); - wants_slice(&*x); -} - -fn has_slice(x: &str) { - wants_uniq(x); //~ ERROR mismatched types - wants_slice(x); -} - -fn main() { -} diff --git a/src/test/ui/estr-subtyping.stderr b/src/test/ui/estr-subtyping.stderr deleted file mode 100644 index adebb7d1e63..00000000000 --- a/src/test/ui/estr-subtyping.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/estr-subtyping.rs:10:15 - | -LL | wants_uniq(x); - | ---------- ^- help: try using a conversion method: `.to_string()` - | | | - | | expected struct `String`, found `&str` - | arguments to this function are incorrect - | -note: function defined here - --> $DIR/estr-subtyping.rs:1:4 - | -LL | fn wants_uniq(x: String) { } - | ^^^^^^^^^^ --------- - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/feature-gates/feature-gate-debugger-visualizer.rs b/src/test/ui/feature-gates/feature-gate-debugger-visualizer.rs new file mode 100644 index 00000000000..3f9eb27a0d6 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-debugger-visualizer.rs @@ -0,0 +1,3 @@ +#![debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR the `#[debugger_visualizer]` attribute is an experimental feature + +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-debugger-visualizer.stderr b/src/test/ui/feature-gates/feature-gate-debugger-visualizer.stderr new file mode 100644 index 00000000000..721b2b185da --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-debugger-visualizer.stderr @@ -0,0 +1,12 @@ +error[E0658]: the `#[debugger_visualizer]` attribute is an experimental feature + --> $DIR/feature-gate-debugger-visualizer.rs:1:1 + | +LL | #![debugger_visualizer(natvis_file = "../foo.natvis")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #95939 <https://github.com/rust-lang/rust/issues/95939> for more information + = help: add `#![feature(debugger_visualizer)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/fn/fn-item-type.rs b/src/test/ui/fn/fn-item-type.rs index 16a0c10ea3a..1831e6cbf10 100644 --- a/src/test/ui/fn/fn-item-type.rs +++ b/src/test/ui/fn/fn-item-type.rs @@ -12,7 +12,7 @@ impl<T> Foo for T { /* `foo` is still default here */ } fn main() { eq(foo::<u8>, bar::<u8>); //~^ ERROR mismatched types - //~| expected type `fn(_) -> _ {foo::<u8>}` + //~| expected fn item `fn(_) -> _ {foo::<u8>}` //~| found fn item `fn(_) -> _ {bar::<u8>}` //~| expected fn item, found a different fn item //~| different `fn` items always have unique types, even if their signatures are the same @@ -28,7 +28,6 @@ fn main() { eq(bar::<String>, bar::<Vec<u8>>); //~^ ERROR mismatched types - //~| expected type `fn(_) -> _ {bar::<String>}` //~| found fn item `fn(_) -> _ {bar::<Vec<u8>>}` //~| expected struct `String`, found struct `Vec` //~| different `fn` items always have unique types, even if their signatures are the same @@ -45,7 +44,6 @@ fn main() { eq(foo::<u8>, bar::<u8> as fn(isize) -> isize); //~^ ERROR mismatched types - //~| expected type `fn(_) -> _ {foo::<u8>}` //~| found fn pointer `fn(_) -> _` //~| expected fn item, found fn pointer //~| change the expected type to be function pointer diff --git a/src/test/ui/fn/fn-item-type.stderr b/src/test/ui/fn/fn-item-type.stderr index 1fb120eb7a7..ecc6485d6d2 100644 --- a/src/test/ui/fn/fn-item-type.stderr +++ b/src/test/ui/fn/fn-item-type.stderr @@ -6,8 +6,8 @@ LL | eq(foo::<u8>, bar::<u8>); | | | arguments to this function are incorrect | - = note: expected type `fn(_) -> _ {foo::<u8>}` - found fn item `fn(_) -> _ {bar::<u8>}` + = note: expected fn item `fn(_) -> _ {foo::<u8>}` + found fn item `fn(_) -> _ {bar::<u8>}` = note: different `fn` items always have unique types, even if their signatures are the same = help: change the expected type to be function pointer `fn(isize) -> isize` = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize` @@ -25,8 +25,8 @@ LL | eq(foo::<u8>, foo::<i8>); | | | arguments to this function are incorrect | - = note: expected type `fn(_) -> _ {foo::<u8>}` - found fn item `fn(_) -> _ {foo::<i8>}` + = note: expected fn item `fn(_) -> _ {foo::<u8>}` + found fn item `fn(_) -> _ {foo::<i8>}` = note: different `fn` items always have unique types, even if their signatures are the same = help: change the expected type to be function pointer `fn(isize) -> isize` = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize` @@ -44,8 +44,8 @@ LL | eq(bar::<String>, bar::<Vec<u8>>); | | | arguments to this function are incorrect | - = note: expected type `fn(_) -> _ {bar::<String>}` - found fn item `fn(_) -> _ {bar::<Vec<u8>>}` + = note: expected fn item `fn(_) -> _ {bar::<String>}` + found fn item `fn(_) -> _ {bar::<Vec<u8>>}` = note: different `fn` items always have unique types, even if their signatures are the same = help: change the expected type to be function pointer `fn(isize) -> isize` = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `bar::<String> as fn(isize) -> isize` @@ -56,15 +56,15 @@ LL | fn eq<T>(x: T, y: T) { } | ^^ ---- ---- error[E0308]: mismatched types - --> $DIR/fn-item-type.rs:39:26 + --> $DIR/fn-item-type.rs:38:26 | LL | eq(<u8 as Foo>::foo, <u16 as Foo>::foo); | -- ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16` | | | arguments to this function are incorrect | - = note: expected type `fn() {<u8 as Foo>::foo}` - found fn item `fn() {<u16 as Foo>::foo}` + = note: expected fn item `fn() {<u8 as Foo>::foo}` + found fn item `fn() {<u16 as Foo>::foo}` = note: different `fn` items always have unique types, even if their signatures are the same = help: change the expected type to be function pointer `fn()` = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `<u8 as Foo>::foo as fn()` @@ -75,14 +75,14 @@ LL | fn eq<T>(x: T, y: T) { } | ^^ ---- ---- error[E0308]: mismatched types - --> $DIR/fn-item-type.rs:46:19 + --> $DIR/fn-item-type.rs:45:19 | LL | eq(foo::<u8>, bar::<u8> as fn(isize) -> isize); | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer | | | arguments to this function are incorrect | - = note: expected type `fn(_) -> _ {foo::<u8>}` + = note: expected fn item `fn(_) -> _ {foo::<u8>}` found fn pointer `fn(_) -> _` = help: change the expected type to be function pointer `fn(isize) -> isize` = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize` diff --git a/src/test/ui/fn/keyword-order.rs b/src/test/ui/fn/keyword-order.rs new file mode 100644 index 00000000000..8a21db67333 --- /dev/null +++ b/src/test/ui/fn/keyword-order.rs @@ -0,0 +1,6 @@ +// edition:2018 + +default pub const async unsafe extern fn err() {} //~ ERROR `default` is not followed by an item +//~^ ERROR expected item, found keyword `pub` + +pub default const async unsafe extern fn ok() {} diff --git a/src/test/ui/fn/keyword-order.stderr b/src/test/ui/fn/keyword-order.stderr new file mode 100644 index 00000000000..d3b140c8528 --- /dev/null +++ b/src/test/ui/fn/keyword-order.stderr @@ -0,0 +1,16 @@ +error: `default` is not followed by an item + --> $DIR/keyword-order.rs:3:1 + | +LL | default pub const async unsafe extern fn err() {} + | ^^^^^^^ the `default` qualifier + | + = note: only `fn`, `const`, `type`, or `impl` items may be prefixed by `default` + +error: expected item, found keyword `pub` + --> $DIR/keyword-order.rs:3:9 + | +LL | default pub const async unsafe extern fn err() {} + | ^^^ expected item + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/for-loop-while/label_break_value.rs b/src/test/ui/for-loop-while/label_break_value.rs index 18930ac811e..5776c0b1e0c 100644 --- a/src/test/ui/for-loop-while/label_break_value.rs +++ b/src/test/ui/for-loop-while/label_break_value.rs @@ -95,6 +95,50 @@ fn label_break_mixed(v: u32) -> u32 { r } +fn label_break_match(c: u8, xe: u8, ye: i8) { + let mut x = 0; + let y = 'a: { + match c { + 0 => break 'a 0, + v if { if v % 2 == 0 { break 'a 1; }; v % 3 == 0 } => { x += 1; }, + v if { 'b: { break 'b v == 5; } } => { x = 41; }, + _ => 'b: { //~ WARNING `'b` shadows a label + break 'b (); + }, + } + x += 1; + -1 + }; + + assert_eq!(x, xe); + assert_eq!(y, ye); +} + +#[allow(unused_labels)] +fn label_break_macro() { + macro_rules! mac1 { + ($target:lifetime, $val:expr) => { + break $target $val; + }; + } + let x: u8 = 'a: { + 'b: { + mac1!('b, 1); + }; + 0 + }; + assert_eq!(x, 0); + let x: u8 = 'a: { //~ WARNING `'a` shadows a label + 'b: { //~ WARNING `'b` shadows a label + if true { + mac1!('a, 1); + } + }; + 0 + }; + assert_eq!(x, 1); +} + pub fn main() { assert_eq!(label_break(true, false), 1); assert_eq!(label_break(false, true), 2); @@ -112,5 +156,12 @@ pub fn main() { assert_eq!(label_break_mixed(5), 5); assert_eq!(label_break_mixed(6), 6); - // FIXME: ensure that labeled blocks work if produced by macros and in match arms + label_break_match(0, 0, 0); + label_break_match(1, 1, -1); + label_break_match(2, 0, 1); + label_break_match(3, 2, -1); + label_break_match(5, 42, -1); + label_break_match(7, 1, -1); + + label_break_macro(); } diff --git a/src/test/ui/for-loop-while/label_break_value.stderr b/src/test/ui/for-loop-while/label_break_value.stderr new file mode 100644 index 00000000000..b1eb3204fd5 --- /dev/null +++ b/src/test/ui/for-loop-while/label_break_value.stderr @@ -0,0 +1,28 @@ +warning: label name `'b` shadows a label name that is already in scope + --> $DIR/label_break_value.rs:105:18 + | +LL | v if { 'b: { break 'b v == 5; } } => { x = 41; }, + | -- first declared here +LL | _ => 'b: { + | ^^ label `'b` already in scope + +warning: label name `'a` shadows a label name that is already in scope + --> $DIR/label_break_value.rs:131:17 + | +LL | let x: u8 = 'a: { + | -- first declared here +... +LL | let x: u8 = 'a: { + | ^^ label `'a` already in scope + +warning: label name `'b` shadows a label name that is already in scope + --> $DIR/label_break_value.rs:132:9 + | +LL | 'b: { + | -- first declared here +... +LL | 'b: { + | ^^ label `'b` already in scope + +warning: 3 warnings emitted + diff --git a/src/test/ui/for-loop-while/label_break_value_invalid.rs b/src/test/ui/for-loop-while/label_break_value_invalid.rs new file mode 100644 index 00000000000..e603c8463b5 --- /dev/null +++ b/src/test/ui/for-loop-while/label_break_value_invalid.rs @@ -0,0 +1,39 @@ +#![crate_type = "lib"] +#![feature(label_break_value)] + +fn lbv_macro_test_hygiene_respected() { + macro_rules! mac2 { + ($val:expr) => { + break 'a $val; //~ ERROR undeclared label `'a` [E0426] + }; + } + let x: u8 = 'a: { + 'b: { + if true { + mac2!(2); + } + }; + 0 + }; + assert_eq!(x, 2); + + macro_rules! mac3 { + ($val:expr) => { + 'a: { + //~^ WARNING `'a` shadows a label + //~| WARNING `'a` shadows a label + //~| WARNING `'a` shadows a label + $val + } + }; + } + let x: u8 = mac3!('b: { //~ WARNING `'b` shadows a label + if true { + break 'a 3; //~ ERROR undeclared label `'a` [E0426] + } + 0 + }); + assert_eq!(x, 3); + let x: u8 = mac3!(break 'a 4); //~ ERROR undeclared label `'a` [E0426] + assert_eq!(x, 4); +} diff --git a/src/test/ui/for-loop-while/label_break_value_invalid.stderr b/src/test/ui/for-loop-while/label_break_value_invalid.stderr new file mode 100644 index 00000000000..549b394e14b --- /dev/null +++ b/src/test/ui/for-loop-while/label_break_value_invalid.stderr @@ -0,0 +1,89 @@ +error[E0426]: use of undeclared label `'a` + --> $DIR/label_break_value_invalid.rs:7:19 + | +LL | break 'a $val; + | ^^ undeclared label `'a` +... +LL | mac2!(2); + | -------- in this macro invocation + | + = note: this error originates in the macro `mac2` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0426]: use of undeclared label `'a` + --> $DIR/label_break_value_invalid.rs:32:19 + | +LL | let x: u8 = mac3!('b: { + | -- a label with a similar name is reachable +LL | if true { +LL | break 'a 3; + | ^^ + | | + | undeclared label `'a` + | help: try using similarly named label: `'b` + +error[E0426]: use of undeclared label `'a` + --> $DIR/label_break_value_invalid.rs:37:29 + | +LL | let x: u8 = mac3!(break 'a 4); + | ^^ undeclared label `'a` + +warning: label name `'a` shadows a label name that is already in scope + --> $DIR/label_break_value_invalid.rs:22:13 + | +LL | let x: u8 = 'a: { + | -- first declared here +... +LL | 'a: { + | ^^ label `'a` already in scope +... +LL | let x: u8 = mac3!('b: { + | _________________- +LL | | if true { +LL | | break 'a 3; +LL | | } +LL | | 0 +LL | | }); + | |______- in this macro invocation + | + = note: this warning originates in the macro `mac3` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: label name `'b` shadows a label name that is already in scope + --> $DIR/label_break_value_invalid.rs:30:23 + | +LL | 'b: { + | -- first declared here +... +LL | let x: u8 = mac3!('b: { + | ^^ label `'b` already in scope + +warning: label name `'a` shadows a label name that is already in scope + --> $DIR/label_break_value_invalid.rs:22:13 + | +LL | let x: u8 = 'a: { + | -- first declared here +... +LL | 'a: { + | ^^ label `'a` already in scope +... +LL | let x: u8 = mac3!(break 'a 4); + | ----------------- in this macro invocation + | + = note: this warning originates in the macro `mac3` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: label name `'a` shadows a label name that is already in scope + --> $DIR/label_break_value_invalid.rs:22:13 + | +LL | 'a: { + | ^^ + | | + | first declared here + | label `'a` already in scope +... +LL | let x: u8 = mac3!(break 'a 4); + | ----------------- in this macro invocation + | + = note: this warning originates in the macro `mac3` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors; 4 warnings emitted + +For more information about this error, try `rustc --explain E0426`. diff --git a/src/test/ui/generator/type-mismatch-signature-deduction.stderr b/src/test/ui/generator/type-mismatch-signature-deduction.stderr index f05faedf21b..d78a5929a89 100644 --- a/src/test/ui/generator/type-mismatch-signature-deduction.stderr +++ b/src/test/ui/generator/type-mismatch-signature-deduction.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | 5 | ^ expected enum `Result`, found integer | - = note: expected type `Result<{integer}, _>` + = note: expected enum `Result<{integer}, _>` found type `{integer}` note: return type inferred to be `Result<{integer}, _>` here --> $DIR/type-mismatch-signature-deduction.rs:9:20 diff --git a/src/test/ui/generic-associated-types/bugs/issue-89352.base.stderr b/src/test/ui/generic-associated-types/bugs/issue-89352.base.stderr new file mode 100644 index 00000000000..81125a7d6f3 --- /dev/null +++ b/src/test/ui/generic-associated-types/bugs/issue-89352.base.stderr @@ -0,0 +1,22 @@ +error[E0308]: mismatched types + --> $DIR/issue-89352.rs:36:13 + | +LL | let a = A::reborrow::<'ai, 's>(self.a.clone()); + | ^ lifetime mismatch + | + = note: expected type `<<A as GenAssoc<T>>::Iter<'s> as Sized>` + found type `<<A as GenAssoc<T>>::Iter<'ai> as Sized>` +note: the lifetime `'s` as defined here... + --> $DIR/issue-89352.rs:35:13 + | +LL | fn iter<'s>(&'s self) -> Self::Iter<'s> { + | ^^ +note: ...does not necessarily outlive the lifetime `'ai` as defined here + --> $DIR/issue-89352.rs:30:6 + | +LL | impl<'ai, T: 'ai, A: GenAssoc<T>> GenAssoc<T> for Wrapper<'ai, T, A> + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/generic-associated-types/issue-89352.rs b/src/test/ui/generic-associated-types/bugs/issue-89352.rs index d9c656d5f58..a9f0dd0a0b4 100644 --- a/src/test/ui/generic-associated-types/issue-89352.rs +++ b/src/test/ui/generic-associated-types/bugs/issue-89352.rs @@ -1,4 +1,16 @@ -// check-pass +// revisions: base nll +// ignore-compare-mode-nll +//[nll] compile-flags: -Z borrowck=mir + +//[base] check-fail +//[nll] check-pass +// known-bug + +// This should pass, but we end up with `A::Iter<'ai>: Sized` for some specific +// `'ai`. We also know that `for<'at> A::Iter<'at>: Sized` from the definition, +// but we prefer param env candidates. We changed this to preference in #92191, +// but this led to unintended consequences (#93262). Suprisingly, this passes +// under NLL. So only a bug in migrate mode. #![feature(generic_associated_types)] diff --git a/src/test/ui/generic-associated-types/issue-93262.rs b/src/test/ui/generic-associated-types/issue-93262.rs new file mode 100644 index 00000000000..adc6aa8fa1a --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-93262.rs @@ -0,0 +1,21 @@ +// check-pass + +#![feature(generic_associated_types)] + +pub trait Trait { + type Assoc<'a> where Self: 'a; +} + +pub trait Foo<T: Trait> +where + for<'a> T::Assoc<'a>: Clone +{} + +pub struct Type; + +impl<T: Trait> Foo<T> for Type +where + for<'a> T::Assoc<'a>: Clone +{} + +fn main() {} diff --git a/src/test/ui/hrtb/issue-94034.rs b/src/test/ui/hrtb/issue-95034.rs index 5239e5db11c..aee6fe61ba8 100644 --- a/src/test/ui/hrtb/issue-94034.rs +++ b/src/test/ui/hrtb/issue-95034.rs @@ -17,6 +17,8 @@ // This should not ICE. +// Refer to the issue for more minimized versions. + use std::{ future::Future, marker::PhantomData, diff --git a/src/test/ui/hrtb/issue-94034.stderr b/src/test/ui/hrtb/issue-95034.stderr index 1d8329142fc..1d8329142fc 100644 --- a/src/test/ui/hrtb/issue-94034.stderr +++ b/src/test/ui/hrtb/issue-95034.stderr diff --git a/src/test/ui/impl-trait/issues/issue-92305.rs b/src/test/ui/impl-trait/issues/issue-92305.rs new file mode 100644 index 00000000000..1518c116b31 --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-92305.rs @@ -0,0 +1,15 @@ +// edition:2021 + +use std::iter; + +fn f<T>(data: &[T]) -> impl Iterator<Item = Vec> { + //~^ ERROR: missing generics for struct `Vec` [E0107] + iter::empty() //~ ERROR: type annotations needed [E0282] +} + +fn g<T>(data: &[T], target: T) -> impl Iterator<Item = Vec<T>> { + //~^ ERROR: type annotations needed [E0282] + f(data).filter(|x| x == target) +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issues/issue-92305.stderr b/src/test/ui/impl-trait/issues/issue-92305.stderr new file mode 100644 index 00000000000..c4fc49225e9 --- /dev/null +++ b/src/test/ui/impl-trait/issues/issue-92305.stderr @@ -0,0 +1,32 @@ +error[E0107]: missing generics for struct `Vec` + --> $DIR/issue-92305.rs:5:45 + | +LL | fn f<T>(data: &[T]) -> impl Iterator<Item = Vec> { + | ^^^ expected at least 1 generic argument + | +note: struct defined here, with at least 1 generic parameter: `T` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + | +LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> { + | ^^^ - +help: add missing generic argument + | +LL | fn f<T>(data: &[T]) -> impl Iterator<Item = Vec<T>> { + | ~~~~~~ + +error[E0282]: type annotations needed + --> $DIR/issue-92305.rs:7:5 + | +LL | iter::empty() + | ^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the function `empty` + +error[E0282]: type annotations needed + --> $DIR/issue-92305.rs:10:35 + | +LL | fn g<T>(data: &[T], target: T) -> impl Iterator<Item = Vec<T>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0107, E0282. +For more information about an error, try `rustc --explain E0107`. diff --git a/src/test/ui/incoherent-inherent-impls/auxiliary/extern-crate.rs b/src/test/ui/incoherent-inherent-impls/auxiliary/extern-crate.rs new file mode 100644 index 00000000000..22f0d912c8f --- /dev/null +++ b/src/test/ui/incoherent-inherent-impls/auxiliary/extern-crate.rs @@ -0,0 +1,9 @@ +#![feature(rustc_attrs)] + +#[rustc_has_incoherent_inherent_impls] +pub struct StructWithAttr; +pub struct StructNoAttr; + +#[rustc_has_incoherent_inherent_impls] +pub enum EnumWithAttr {} +pub enum EnumNoAttr {} diff --git a/src/test/ui/incoherent-inherent-impls/needs-has-incoherent-impls.rs b/src/test/ui/incoherent-inherent-impls/needs-has-incoherent-impls.rs new file mode 100644 index 00000000000..0f7282bec5c --- /dev/null +++ b/src/test/ui/incoherent-inherent-impls/needs-has-incoherent-impls.rs @@ -0,0 +1,40 @@ +// aux-build:extern-crate.rs +#![feature(rustc_attrs)] +extern crate extern_crate; + +impl extern_crate::StructWithAttr { + //~^ ERROR cannot define inherent `impl` for a type outside of the crate + fn foo() {} +} +impl extern_crate::StructWithAttr { + #[rustc_allow_incoherent_impl] + fn bar() {} +} +impl extern_crate::StructNoAttr { + //~^ ERROR cannot define inherent `impl` for a type outside of the crate + fn foo() {} +} +impl extern_crate::StructNoAttr { + //~^ ERROR cannot define inherent `impl` for a type outside of the crate + #[rustc_allow_incoherent_impl] + fn bar() {} +} +impl extern_crate::EnumWithAttr { + //~^ ERROR cannot define inherent `impl` for a type outside of the crate + fn foo() {} +} +impl extern_crate::EnumWithAttr { + #[rustc_allow_incoherent_impl] + fn bar() {} +} +impl extern_crate::EnumNoAttr { + //~^ ERROR cannot define inherent `impl` for a type outside of the crate + fn foo() {} +} +impl extern_crate::EnumNoAttr { + //~^ ERROR cannot define inherent `impl` for a type outside of the crate + #[rustc_allow_incoherent_impl] + fn bar() {} +} + +fn main() {} diff --git a/src/test/ui/incoherent-inherent-impls/needs-has-incoherent-impls.stderr b/src/test/ui/incoherent-inherent-impls/needs-has-incoherent-impls.stderr new file mode 100644 index 00000000000..8f70825115d --- /dev/null +++ b/src/test/ui/incoherent-inherent-impls/needs-has-incoherent-impls.stderr @@ -0,0 +1,115 @@ +error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/needs-has-incoherent-impls.rs:5:1 + | +LL | / impl extern_crate::StructWithAttr { +LL | | +LL | | fn foo() {} +LL | | } + | |_^ + | + = help: consider moving this inherent impl into the crate defining the type if possible +help: alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items + --> $DIR/needs-has-incoherent-impls.rs:7:5 + | +LL | fn foo() {} + | ^^^^^^^^^^^ + +error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/needs-has-incoherent-impls.rs:13:1 + | +LL | / impl extern_crate::StructNoAttr { +LL | | +LL | | fn foo() {} +LL | | } + | |_^ + | + = help: consider moving this inherent impl into the crate defining the type if possible +help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items + --> $DIR/needs-has-incoherent-impls.rs:13:1 + | +LL | / impl extern_crate::StructNoAttr { +LL | | +LL | | fn foo() {} +LL | | } + | |_^ + +error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/needs-has-incoherent-impls.rs:17:1 + | +LL | / impl extern_crate::StructNoAttr { +LL | | +LL | | #[rustc_allow_incoherent_impl] +LL | | fn bar() {} +LL | | } + | |_^ + | + = help: consider moving this inherent impl into the crate defining the type if possible +help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items + --> $DIR/needs-has-incoherent-impls.rs:17:1 + | +LL | / impl extern_crate::StructNoAttr { +LL | | +LL | | #[rustc_allow_incoherent_impl] +LL | | fn bar() {} +LL | | } + | |_^ + +error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/needs-has-incoherent-impls.rs:22:1 + | +LL | / impl extern_crate::EnumWithAttr { +LL | | +LL | | fn foo() {} +LL | | } + | |_^ + | + = help: consider moving this inherent impl into the crate defining the type if possible +help: alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items + --> $DIR/needs-has-incoherent-impls.rs:24:5 + | +LL | fn foo() {} + | ^^^^^^^^^^^ + +error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/needs-has-incoherent-impls.rs:30:1 + | +LL | / impl extern_crate::EnumNoAttr { +LL | | +LL | | fn foo() {} +LL | | } + | |_^ + | + = help: consider moving this inherent impl into the crate defining the type if possible +help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items + --> $DIR/needs-has-incoherent-impls.rs:30:1 + | +LL | / impl extern_crate::EnumNoAttr { +LL | | +LL | | fn foo() {} +LL | | } + | |_^ + +error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/needs-has-incoherent-impls.rs:34:1 + | +LL | / impl extern_crate::EnumNoAttr { +LL | | +LL | | #[rustc_allow_incoherent_impl] +LL | | fn bar() {} +LL | | } + | |_^ + | + = help: consider moving this inherent impl into the crate defining the type if possible +help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items + --> $DIR/needs-has-incoherent-impls.rs:34:1 + | +LL | / impl extern_crate::EnumNoAttr { +LL | | +LL | | #[rustc_allow_incoherent_impl] +LL | | fn bar() {} +LL | | } + | |_^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0390`. diff --git a/src/test/ui/incoherent-inherent-impls/no-attr-empty-impl.rs b/src/test/ui/incoherent-inherent-impls/no-attr-empty-impl.rs new file mode 100644 index 00000000000..62c249e5882 --- /dev/null +++ b/src/test/ui/incoherent-inherent-impls/no-attr-empty-impl.rs @@ -0,0 +1,18 @@ +// aux-build:extern-crate.rs +extern crate extern_crate; + +impl extern_crate::StructWithAttr {} +//~^ ERROR cannot define inherent `impl` for a type outside of the crate + +impl extern_crate::StructNoAttr {} +//~^ ERROR cannot define inherent `impl` for a type outside of the crate + +impl extern_crate::EnumWithAttr {} +//~^ ERROR cannot define inherent `impl` for a type outside of the crate + +impl extern_crate::EnumNoAttr {} +//~^ ERROR cannot define inherent `impl` for a type outside of the crate + +impl f32 {} //~ ERROR cannot define inherent `impl` for primitive types + +fn main() {} diff --git a/src/test/ui/incoherent-inherent-impls/no-attr-empty-impl.stderr b/src/test/ui/incoherent-inherent-impls/no-attr-empty-impl.stderr new file mode 100644 index 00000000000..b3f8b51d0ea --- /dev/null +++ b/src/test/ui/incoherent-inherent-impls/no-attr-empty-impl.stderr @@ -0,0 +1,44 @@ +error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/no-attr-empty-impl.rs:4:1 + | +LL | impl extern_crate::StructWithAttr {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate. + | + = note: define and implement a trait or new type instead + +error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/no-attr-empty-impl.rs:7:1 + | +LL | impl extern_crate::StructNoAttr {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate. + | + = note: define and implement a trait or new type instead + +error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/no-attr-empty-impl.rs:10:1 + | +LL | impl extern_crate::EnumWithAttr {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate. + | + = note: define and implement a trait or new type instead + +error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/no-attr-empty-impl.rs:13:1 + | +LL | impl extern_crate::EnumNoAttr {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate. + | + = note: define and implement a trait or new type instead + +error[E0390]: cannot define inherent `impl` for primitive types + --> $DIR/no-attr-empty-impl.rs:16:6 + | +LL | impl f32 {} + | ^^^ + | + = help: consider using an extension trait instead + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0116, E0390. +For more information about an error, try `rustc --explain E0116`. diff --git a/src/test/ui/indexing-requires-a-uint.stderr b/src/test/ui/indexing-requires-a-uint.stderr index a9adff4fade..0a24855a6a7 100644 --- a/src/test/ui/indexing-requires-a-uint.stderr +++ b/src/test/ui/indexing-requires-a-uint.stderr @@ -21,6 +21,10 @@ note: function defined here | LL | fn bar<T>(_: T) {} | ^^^ ---- +help: you can convert a `usize` to an `isize` and panic if the converted value doesn't fit + | +LL | bar::<isize>(i.try_into().unwrap()); // i should not be re-coerced back to an isize + | ++++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/src/test/ui/inline-const/const-expr-generic-err.rs b/src/test/ui/inline-const/const-expr-generic-err.rs new file mode 100644 index 00000000000..4e8879af54a --- /dev/null +++ b/src/test/ui/inline-const/const-expr-generic-err.rs @@ -0,0 +1,15 @@ +// build-fail +#![feature(inline_const)] + +fn foo<T>() { + const { assert!(std::mem::size_of::<T>() == 0); } //~ ERROR E0080 +} + +fn bar<const N: usize>() -> usize { + const { N - 1 } //~ ERROR E0080 +} + +fn main() { + foo::<i32>(); + bar::<0>(); +} diff --git a/src/test/ui/inline-const/const-expr-generic-err.stderr b/src/test/ui/inline-const/const-expr-generic-err.stderr new file mode 100644 index 00000000000..db0d85a2d4e --- /dev/null +++ b/src/test/ui/inline-const/const-expr-generic-err.stderr @@ -0,0 +1,29 @@ +error[E0080]: evaluation of `foo::<i32>::{constant#0}` failed + --> $DIR/const-expr-generic-err.rs:5:13 + | +LL | const { assert!(std::mem::size_of::<T>() == 0); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: std::mem::size_of::<T>() == 0', $DIR/const-expr-generic-err.rs:5:13 + | + = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn foo::<i32>` + --> $DIR/const-expr-generic-err.rs:13:5 + | +LL | foo::<i32>(); + | ^^^^^^^^^^^^ + +error[E0080]: evaluation of `bar::<0_usize>::{constant#0}` failed + --> $DIR/const-expr-generic-err.rs:9:13 + | +LL | const { N - 1 } + | ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow + +note: the above error was encountered while instantiating `fn bar::<0_usize>` + --> $DIR/const-expr-generic-err.rs:14:5 + | +LL | bar::<0>(); + | ^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/inline-const/const-expr-generic-err2.rs b/src/test/ui/inline-const/const-expr-generic-err2.rs new file mode 100644 index 00000000000..e097cbe9dd6 --- /dev/null +++ b/src/test/ui/inline-const/const-expr-generic-err2.rs @@ -0,0 +1,10 @@ +#![feature(inline_const)] + +fn foo<T>() { + let _ = [0u8; const { std::mem::size_of::<T>() }]; + //~^ ERROR: constant expression depends on a generic parameter +} + +fn main() { + foo::<i32>(); +} diff --git a/src/test/ui/inline-const/const-expr-generic-err2.stderr b/src/test/ui/inline-const/const-expr-generic-err2.stderr new file mode 100644 index 00000000000..00b716cd259 --- /dev/null +++ b/src/test/ui/inline-const/const-expr-generic-err2.stderr @@ -0,0 +1,10 @@ +error: constant expression depends on a generic parameter + --> $DIR/const-expr-generic-err2.rs:4:19 + | +LL | let _ = [0u8; const { std::mem::size_of::<T>() }]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to previous error + diff --git a/src/test/ui/inline-const/const-expr-generic.rs b/src/test/ui/inline-const/const-expr-generic.rs new file mode 100644 index 00000000000..3207bfa0e89 --- /dev/null +++ b/src/test/ui/inline-const/const-expr-generic.rs @@ -0,0 +1,15 @@ +// check-pass +#![feature(inline_const)] + +fn foo<T>() -> usize { + const { std::mem::size_of::<T>() } +} + +fn bar<const N: usize>() -> usize { + const { N + 1 } +} + +fn main() { + foo::<i32>(); + bar::<1>(); +} diff --git a/src/test/ui/inline-const/const-match-pat-generic.rs b/src/test/ui/inline-const/const-match-pat-generic.rs index be7e1d8d449..e1946467583 100644 --- a/src/test/ui/inline-const/const-match-pat-generic.rs +++ b/src/test/ui/inline-const/const-match-pat-generic.rs @@ -1,6 +1,5 @@ #![allow(incomplete_features)] #![feature(inline_const_pat)] -#![feature(generic_const_exprs)] // rust-lang/rust#82518: ICE with inline-const in match referencing const-generic parameter @@ -16,7 +15,7 @@ const fn f(x: usize) -> usize { x + 1 } -fn bar<const V: usize>() where [(); f(V)]: { +fn bar<const V: usize>() { match 0 { const { f(V) } => {}, //~^ ERROR constant pattern depends on a generic parameter diff --git a/src/test/ui/inline-const/const-match-pat-generic.stderr b/src/test/ui/inline-const/const-match-pat-generic.stderr index 5fe5a7a6dad..ade200d99ba 100644 --- a/src/test/ui/inline-const/const-match-pat-generic.stderr +++ b/src/test/ui/inline-const/const-match-pat-generic.stderr @@ -1,17 +1,17 @@ error[E0158]: const parameters cannot be referenced in patterns - --> $DIR/const-match-pat-generic.rs:9:9 + --> $DIR/const-match-pat-generic.rs:8:9 | LL | const { V } => {}, | ^^^^^^^^^^^ error: constant pattern depends on a generic parameter - --> $DIR/const-match-pat-generic.rs:21:9 + --> $DIR/const-match-pat-generic.rs:20:9 | LL | const { f(V) } => {}, | ^^^^^^^^^^^^^^ error: constant pattern depends on a generic parameter - --> $DIR/const-match-pat-generic.rs:21:9 + --> $DIR/const-match-pat-generic.rs:20:9 | LL | const { f(V) } => {}, | ^^^^^^^^^^^^^^ diff --git a/src/test/ui/invalid/invalid-debugger-visualizer-option.rs b/src/test/ui/invalid/invalid-debugger-visualizer-option.rs new file mode 100644 index 00000000000..5332298f0ef --- /dev/null +++ b/src/test/ui/invalid/invalid-debugger-visualizer-option.rs @@ -0,0 +1,4 @@ +#![feature(debugger_visualizer)] +#![debugger_visualizer(random_file = "../foo.random")] //~ ERROR invalid argument + +fn main() {} diff --git a/src/test/ui/invalid/invalid-debugger-visualizer-option.stderr b/src/test/ui/invalid/invalid-debugger-visualizer-option.stderr new file mode 100644 index 00000000000..24ad9361fe3 --- /dev/null +++ b/src/test/ui/invalid/invalid-debugger-visualizer-option.stderr @@ -0,0 +1,10 @@ +error: invalid argument + --> $DIR/invalid-debugger-visualizer-option.rs:2:1 + | +LL | #![debugger_visualizer(random_file = "../foo.random")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected: `natvis_file = "..."` + +error: aborting due to previous error + diff --git a/src/test/ui/invalid/invalid-debugger-visualizer-target.rs b/src/test/ui/invalid/invalid-debugger-visualizer-target.rs new file mode 100644 index 00000000000..7668d092e61 --- /dev/null +++ b/src/test/ui/invalid/invalid-debugger-visualizer-target.rs @@ -0,0 +1,5 @@ +#![feature(debugger_visualizer)] + +#[debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR attribute should be applied to a module + +fn main() {} diff --git a/src/test/ui/invalid/invalid-debugger-visualizer-target.stderr b/src/test/ui/invalid/invalid-debugger-visualizer-target.stderr new file mode 100644 index 00000000000..3555bbb169b --- /dev/null +++ b/src/test/ui/invalid/invalid-debugger-visualizer-target.stderr @@ -0,0 +1,8 @@ +error: attribute should be applied to a module + --> $DIR/invalid-debugger-visualizer-target.rs:3:1 + | +LL | #[debugger_visualizer(natvis_file = "../foo.natvis")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/issues/issue-13359.stderr b/src/test/ui/issues/issue-13359.stderr index db6283ea11f..fef63680a86 100644 --- a/src/test/ui/issues/issue-13359.stderr +++ b/src/test/ui/issues/issue-13359.stderr @@ -11,6 +11,10 @@ note: function defined here | LL | fn foo(_s: i16) { } | ^^^ ------- +help: you can convert an `isize` to an `i16` and panic if the converted value doesn't fit + | +LL | foo((1*(1 as isize)).try_into().unwrap()); + | + +++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/issue-13359.rs:10:9 @@ -25,6 +29,10 @@ note: function defined here | LL | fn bar(_s: u32) { } | ^^^ ------- +help: you can convert a `usize` to a `u32` and panic if the converted value doesn't fit + | +LL | bar((1*(1 as usize)).try_into().unwrap()); + | + +++++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-61623.rs b/src/test/ui/issues/issue-61623.rs index e5b8747bd80..82df50d9dc3 100644 --- a/src/test/ui/issues/issue-61623.rs +++ b/src/test/ui/issues/issue-61623.rs @@ -5,7 +5,6 @@ fn f2<P>(_: P, _: ()) {} fn f3<'a>(x: &'a ((), &'a mut ())) { f2(|| x.0, f1(x.1)) //~^ ERROR cannot borrow `*x.1` as mutable, as it is behind a `&` reference -//~| ERROR cannot borrow `*x.1` as mutable because it is also borrowed as immutable } fn main() {} diff --git a/src/test/ui/issues/issue-61623.stderr b/src/test/ui/issues/issue-61623.stderr index 901c7598176..f6546054233 100644 --- a/src/test/ui/issues/issue-61623.stderr +++ b/src/test/ui/issues/issue-61623.stderr @@ -6,17 +6,6 @@ LL | fn f3<'a>(x: &'a ((), &'a mut ())) { LL | f2(|| x.0, f1(x.1)) | ^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable -error[E0502]: cannot borrow `*x.1` as mutable because it is also borrowed as immutable - --> $DIR/issue-61623.rs:6:19 - | -LL | f2(|| x.0, f1(x.1)) - | -- -- --- ^^^ mutable borrow occurs here - | | | | - | | | first borrow occurs due to use of `x` in closure - | | immutable borrow occurs here - | immutable borrow later used by call - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0502, E0596. -For more information about an error, try `rustc --explain E0502`. +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/layout/debug.rs b/src/test/ui/layout/debug.rs index 299151df664..a282e71235c 100644 --- a/src/test/ui/layout/debug.rs +++ b/src/test/ui/layout/debug.rs @@ -1,4 +1,4 @@ -// normalize-stderr-test "pref: Align \{\n *pow2: [1-3],\n *\}" -> "pref: $$PREF_ALIGN" +// normalize-stderr-test "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN" #![feature(never_type, rustc_attrs, type_alias_impl_trait)] #![crate_type = "lib"] diff --git a/src/test/ui/layout/debug.stderr b/src/test/ui/layout/debug.stderr index 25f7febfef9..56a1337e6a5 100644 --- a/src/test/ui/layout/debug.stderr +++ b/src/test/ui/layout/debug.stderr @@ -1,9 +1,7 @@ error: layout_of(E) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -33,27 +31,17 @@ error: layout_of(E) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 4, - }, + size: Size(4 bytes), }, Layout { fields: Arbitrary { offsets: [ - Size { - raw: 4, - }, - Size { - raw: 4, - }, - Size { - raw: 8, - }, + Size(4 bytes), + Size(4 bytes), + Size(8 bytes), ], memory_index: [ 0, @@ -67,14 +55,10 @@ error: layout_of(E) = Layout { abi: Uninhabited, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, + abi: Align(4 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 12, - }, + size: Size(12 bytes), }, ], }, @@ -83,9 +67,7 @@ error: layout_of(E) = Layout { }, largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I32, false, @@ -94,14 +76,10 @@ error: layout_of(E) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, + abi: Align(4 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 12, - }, + size: Size(12 bytes), } --> $DIR/debug.rs:6:1 | @@ -111,15 +89,9 @@ LL | enum E { Foo, Bar(!, i32, i32) } error: layout_of(S) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, - Size { - raw: 0, - }, - Size { - raw: 4, - }, + Size(0 bytes), + Size(0 bytes), + Size(4 bytes), ], memory_index: [ 1, @@ -148,14 +120,10 @@ error: layout_of(S) = Layout { ), largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, + abi: Align(4 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 8, - }, + size: Size(8 bytes), } --> $DIR/debug.rs:9:1 | @@ -174,14 +142,10 @@ error: layout_of(U) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, + abi: Align(4 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 8, - }, + size: Size(8 bytes), } --> $DIR/debug.rs:12:1 | @@ -191,9 +155,7 @@ LL | union U { f1: (i32, i32), f3: i32 } error: layout_of(std::result::Result<i32, i32>) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -213,9 +175,7 @@ error: layout_of(std::result::Result<i32, i32>) = Layout { Layout { fields: Arbitrary { offsets: [ - Size { - raw: 4, - }, + Size(4 bytes), ], memory_index: [ 0, @@ -229,21 +189,15 @@ error: layout_of(std::result::Result<i32, i32>) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, + abi: Align(4 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 8, - }, + size: Size(8 bytes), }, Layout { fields: Arbitrary { offsets: [ - Size { - raw: 4, - }, + Size(4 bytes), ], memory_index: [ 0, @@ -257,14 +211,10 @@ error: layout_of(std::result::Result<i32, i32>) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, + abi: Align(4 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 8, - }, + size: Size(8 bytes), }, ], }, @@ -286,9 +236,7 @@ error: layout_of(std::result::Result<i32, i32>) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I32, false, @@ -297,14 +245,10 @@ error: layout_of(std::result::Result<i32, i32>) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, + abi: Align(4 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 8, - }, + size: Size(8 bytes), } --> $DIR/debug.rs:15:1 | @@ -327,14 +271,10 @@ error: layout_of(i32) = Layout { ), largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, + abi: Align(4 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 4, - }, + size: Size(4 bytes), } --> $DIR/debug.rs:18:1 | diff --git a/src/test/ui/layout/hexagon-enum.stderr b/src/test/ui/layout/hexagon-enum.stderr index 4db8162b16b..ba919df771f 100644 --- a/src/test/ui/layout/hexagon-enum.stderr +++ b/src/test/ui/layout/hexagon-enum.stderr @@ -1,9 +1,7 @@ error: layout_of(A) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -33,16 +31,10 @@ error: layout_of(A) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, - pref: Align { - pow2: 0, - }, - }, - size: Size { - raw: 1, + abi: Align(1 bytes), + pref: Align(1 bytes), }, + size: Size(1 bytes), }, ], }, @@ -57,9 +49,7 @@ error: layout_of(A) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I8, false, @@ -68,16 +58,10 @@ error: layout_of(A) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, - pref: Align { - pow2: 0, - }, - }, - size: Size { - raw: 1, + abi: Align(1 bytes), + pref: Align(1 bytes), }, + size: Size(1 bytes), } --> $DIR/hexagon-enum.rs:16:1 | @@ -87,9 +71,7 @@ LL | enum A { Apple } error: layout_of(B) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -119,16 +101,10 @@ error: layout_of(B) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, - pref: Align { - pow2: 0, - }, - }, - size: Size { - raw: 1, + abi: Align(1 bytes), + pref: Align(1 bytes), }, + size: Size(1 bytes), }, ], }, @@ -143,9 +119,7 @@ error: layout_of(B) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I8, false, @@ -154,16 +128,10 @@ error: layout_of(B) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, - pref: Align { - pow2: 0, - }, - }, - size: Size { - raw: 1, + abi: Align(1 bytes), + pref: Align(1 bytes), }, + size: Size(1 bytes), } --> $DIR/hexagon-enum.rs:20:1 | @@ -173,9 +141,7 @@ LL | enum B { Banana = 255, } error: layout_of(C) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -205,16 +171,10 @@ error: layout_of(C) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 1, - }, - pref: Align { - pow2: 1, - }, - }, - size: Size { - raw: 2, + abi: Align(2 bytes), + pref: Align(2 bytes), }, + size: Size(2 bytes), }, ], }, @@ -229,9 +189,7 @@ error: layout_of(C) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I16, false, @@ -240,16 +198,10 @@ error: layout_of(C) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 1, - }, - pref: Align { - pow2: 1, - }, - }, - size: Size { - raw: 2, + abi: Align(2 bytes), + pref: Align(2 bytes), }, + size: Size(2 bytes), } --> $DIR/hexagon-enum.rs:24:1 | @@ -259,9 +211,7 @@ LL | enum C { Chaenomeles = 256, } error: layout_of(P) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -291,16 +241,10 @@ error: layout_of(P) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 4, + abi: Align(4 bytes), + pref: Align(4 bytes), }, + size: Size(4 bytes), }, ], }, @@ -315,9 +259,7 @@ error: layout_of(P) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I32, false, @@ -326,16 +268,10 @@ error: layout_of(P) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 4, + abi: Align(4 bytes), + pref: Align(4 bytes), }, + size: Size(4 bytes), } --> $DIR/hexagon-enum.rs:28:1 | @@ -345,9 +281,7 @@ LL | enum P { Peach = 0x1000_0000isize, } error: layout_of(T) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -377,16 +311,10 @@ error: layout_of(T) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 4, + abi: Align(4 bytes), + pref: Align(4 bytes), }, + size: Size(4 bytes), }, ], }, @@ -401,9 +329,7 @@ error: layout_of(T) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I32, true, @@ -412,16 +338,10 @@ error: layout_of(T) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 4, + abi: Align(4 bytes), + pref: Align(4 bytes), }, + size: Size(4 bytes), } --> $DIR/hexagon-enum.rs:34:1 | diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr index cd3fb5ca5ea..6c97a09b0c6 100644 --- a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr +++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr @@ -1,10 +1,10 @@ -error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) --> $DIR/homogeneous-aggr-zero-sized-c-struct.rs:22:1 | LL | pub type TestMiddle = Middle; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) --> $DIR/homogeneous-aggr-zero-sized-c-struct.rs:33:1 | LL | pub type TestFinal = Final; diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs index ec2c9b70224..a473c5c97c0 100644 --- a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs +++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs @@ -17,8 +17,7 @@ pub struct WithPhantomData { pub _unit: std::marker::PhantomData<()>, } -pub struct EmptyRustStruct { -} +pub struct EmptyRustStruct {} #[repr(C)] pub struct WithEmptyRustStruct { @@ -52,22 +51,22 @@ pub struct WithEmptyRustEnum { #[rustc_layout(homogeneous_aggregate)] pub type Test1 = BaseCase; -//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) #[rustc_layout(homogeneous_aggregate)] pub type Test2 = WithPhantomData; -//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) #[rustc_layout(homogeneous_aggregate)] pub type Test3 = WithEmptyRustStruct; -//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) #[rustc_layout(homogeneous_aggregate)] pub type Test4 = WithTransitivelyEmptyRustStruct; -//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) #[rustc_layout(homogeneous_aggregate)] pub type Test5 = WithEmptyRustEnum; -//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) +//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) -fn main() { } +fn main() {} diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr index ec2b08bf02d..322948ff783 100644 --- a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr +++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr @@ -1,29 +1,29 @@ -error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) - --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:54:1 +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) + --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:53:1 | LL | pub type Test1 = BaseCase; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) - --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:58:1 +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) + --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:57:1 | LL | pub type Test2 = WithPhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) - --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:62:1 +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) + --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:61:1 | LL | pub type Test3 = WithEmptyRustStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) - --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:66:1 +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) + --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:65:1 | LL | pub type Test4 = WithTransitivelyEmptyRustStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) - --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:70:1 +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) + --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:69:1 | LL | pub type Test5 = WithEmptyRustEnum; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs b/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs index 89387e01ba5..af5f5885d67 100644 --- a/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs +++ b/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs @@ -1,4 +1,4 @@ -// normalize-stderr-test "pref: Align \{\n *pow2: [1-3],\n *\}" -> "pref: $$PREF_ALIGN" +// normalize-stderr-test "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN" #![crate_type = "lib"] #![feature(rustc_attrs)] diff --git a/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr b/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr index 46187aae304..1a724e6f59b 100644 --- a/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr +++ b/src/test/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr @@ -1,9 +1,7 @@ error: layout_of(MissingPayloadField) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -23,9 +21,7 @@ error: layout_of(MissingPayloadField) = Layout { Layout { fields: Arbitrary { offsets: [ - Size { - raw: 1, - }, + Size(1 bytes), ], memory_index: [ 0, @@ -39,14 +35,10 @@ error: layout_of(MissingPayloadField) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 2, - }, + size: Size(2 bytes), }, Layout { fields: Arbitrary { @@ -61,14 +53,10 @@ error: layout_of(MissingPayloadField) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 1, - }, + size: Size(1 bytes), }, ], }, @@ -89,9 +77,7 @@ error: layout_of(MissingPayloadField) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I8, false, @@ -100,14 +86,10 @@ error: layout_of(MissingPayloadField) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 2, - }, + size: Size(2 bytes), } --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:16:1 | @@ -120,9 +102,7 @@ LL | | } error: layout_of(CommonPayloadField) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -142,9 +122,7 @@ error: layout_of(CommonPayloadField) = Layout { Layout { fields: Arbitrary { offsets: [ - Size { - raw: 1, - }, + Size(1 bytes), ], memory_index: [ 0, @@ -158,21 +136,15 @@ error: layout_of(CommonPayloadField) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 2, - }, + size: Size(2 bytes), }, Layout { fields: Arbitrary { offsets: [ - Size { - raw: 1, - }, + Size(1 bytes), ], memory_index: [ 0, @@ -186,14 +158,10 @@ error: layout_of(CommonPayloadField) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 2, - }, + size: Size(2 bytes), }, ], }, @@ -215,9 +183,7 @@ error: layout_of(CommonPayloadField) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I8, false, @@ -226,14 +192,10 @@ error: layout_of(CommonPayloadField) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 2, - }, + size: Size(2 bytes), } --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:25:1 | @@ -246,9 +208,7 @@ LL | | } error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -268,9 +228,7 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { Layout { fields: Arbitrary { offsets: [ - Size { - raw: 1, - }, + Size(1 bytes), ], memory_index: [ 0, @@ -284,21 +242,15 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 2, - }, + size: Size(2 bytes), }, Layout { fields: Arbitrary { offsets: [ - Size { - raw: 1, - }, + Size(1 bytes), ], memory_index: [ 0, @@ -312,14 +264,10 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 2, - }, + size: Size(2 bytes), }, ], }, @@ -340,9 +288,7 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I8, false, @@ -351,14 +297,10 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 2, - }, + size: Size(2 bytes), } --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:33:1 | @@ -371,9 +313,7 @@ LL | | } error: layout_of(NicheFirst) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -397,12 +337,8 @@ error: layout_of(NicheFirst) = Layout { Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, - Size { - raw: 1, - }, + Size(0 bytes), + Size(1 bytes), ], memory_index: [ 0, @@ -430,9 +366,7 @@ error: layout_of(NicheFirst) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I8, false, @@ -441,14 +375,10 @@ error: layout_of(NicheFirst) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 2, - }, + size: Size(2 bytes), }, Layout { fields: Arbitrary { @@ -463,14 +393,10 @@ error: layout_of(NicheFirst) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 0, - }, + size: Size(0 bytes), }, Layout { fields: Arbitrary { @@ -485,14 +411,10 @@ error: layout_of(NicheFirst) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 0, - }, + size: Size(0 bytes), }, ], }, @@ -513,9 +435,7 @@ error: layout_of(NicheFirst) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I8, false, @@ -524,14 +444,10 @@ error: layout_of(NicheFirst) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 2, - }, + size: Size(2 bytes), } --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:41:1 | @@ -545,9 +461,7 @@ LL | | } error: layout_of(NicheSecond) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 1, - }, + Size(1 bytes), ], memory_index: [ 0, @@ -571,12 +485,8 @@ error: layout_of(NicheSecond) = Layout { Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, - Size { - raw: 1, - }, + Size(0 bytes), + Size(1 bytes), ], memory_index: [ 0, @@ -604,9 +514,7 @@ error: layout_of(NicheSecond) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 1, - }, + offset: Size(1 bytes), value: Int( I8, false, @@ -615,14 +523,10 @@ error: layout_of(NicheSecond) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 2, - }, + size: Size(2 bytes), }, Layout { fields: Arbitrary { @@ -637,14 +541,10 @@ error: layout_of(NicheSecond) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 0, - }, + size: Size(0 bytes), }, Layout { fields: Arbitrary { @@ -659,14 +559,10 @@ error: layout_of(NicheSecond) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 0, - }, + size: Size(0 bytes), }, ], }, @@ -687,9 +583,7 @@ error: layout_of(NicheSecond) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 1, - }, + offset: Size(1 bytes), value: Int( I8, false, @@ -698,14 +592,10 @@ error: layout_of(NicheSecond) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, + abi: Align(1 bytes), pref: $PREF_ALIGN, }, - size: Size { - raw: 2, - }, + size: Size(2 bytes), } --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:50:1 | diff --git a/src/test/ui/layout/thumb-enum.stderr b/src/test/ui/layout/thumb-enum.stderr index 9d1f234f31a..9db9ad5a784 100644 --- a/src/test/ui/layout/thumb-enum.stderr +++ b/src/test/ui/layout/thumb-enum.stderr @@ -1,9 +1,7 @@ error: layout_of(A) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -33,16 +31,10 @@ error: layout_of(A) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 1, + abi: Align(1 bytes), + pref: Align(4 bytes), }, + size: Size(1 bytes), }, ], }, @@ -57,9 +49,7 @@ error: layout_of(A) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I8, false, @@ -68,16 +58,10 @@ error: layout_of(A) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 1, + abi: Align(1 bytes), + pref: Align(4 bytes), }, + size: Size(1 bytes), } --> $DIR/thumb-enum.rs:16:1 | @@ -87,9 +71,7 @@ LL | enum A { Apple } error: layout_of(B) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -119,16 +101,10 @@ error: layout_of(B) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 1, + abi: Align(1 bytes), + pref: Align(4 bytes), }, + size: Size(1 bytes), }, ], }, @@ -143,9 +119,7 @@ error: layout_of(B) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I8, false, @@ -154,16 +128,10 @@ error: layout_of(B) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 0, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 1, + abi: Align(1 bytes), + pref: Align(4 bytes), }, + size: Size(1 bytes), } --> $DIR/thumb-enum.rs:20:1 | @@ -173,9 +141,7 @@ LL | enum B { Banana = 255, } error: layout_of(C) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -205,16 +171,10 @@ error: layout_of(C) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 1, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 2, + abi: Align(2 bytes), + pref: Align(4 bytes), }, + size: Size(2 bytes), }, ], }, @@ -229,9 +189,7 @@ error: layout_of(C) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I16, false, @@ -240,16 +198,10 @@ error: layout_of(C) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 1, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 2, + abi: Align(2 bytes), + pref: Align(4 bytes), }, + size: Size(2 bytes), } --> $DIR/thumb-enum.rs:24:1 | @@ -259,9 +211,7 @@ LL | enum C { Chaenomeles = 256, } error: layout_of(P) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -291,16 +241,10 @@ error: layout_of(P) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 4, + abi: Align(4 bytes), + pref: Align(4 bytes), }, + size: Size(4 bytes), }, ], }, @@ -315,9 +259,7 @@ error: layout_of(P) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I32, false, @@ -326,16 +268,10 @@ error: layout_of(P) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 4, + abi: Align(4 bytes), + pref: Align(4 bytes), }, + size: Size(4 bytes), } --> $DIR/thumb-enum.rs:28:1 | @@ -345,9 +281,7 @@ LL | enum P { Peach = 0x1000_0000isize, } error: layout_of(T) = Layout { fields: Arbitrary { offsets: [ - Size { - raw: 0, - }, + Size(0 bytes), ], memory_index: [ 0, @@ -377,16 +311,10 @@ error: layout_of(T) = Layout { }, largest_niche: None, align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 4, + abi: Align(4 bytes), + pref: Align(4 bytes), }, + size: Size(4 bytes), }, ], }, @@ -401,9 +329,7 @@ error: layout_of(T) = Layout { ), largest_niche: Some( Niche { - offset: Size { - raw: 0, - }, + offset: Size(0 bytes), value: Int( I32, true, @@ -412,16 +338,10 @@ error: layout_of(T) = Layout { }, ), align: AbiAndPrefAlign { - abi: Align { - pow2: 2, - }, - pref: Align { - pow2: 2, - }, - }, - size: Size { - raw: 4, + abi: Align(4 bytes), + pref: Align(4 bytes), }, + size: Size(4 bytes), } --> $DIR/thumb-enum.rs:34:1 | diff --git a/src/test/ui/layout/zero-sized-array-union.stderr b/src/test/ui/layout/zero-sized-array-union.stderr index 43b1588266b..8faf8593294 100644 --- a/src/test/ui/layout/zero-sized-array-union.stderr +++ b/src/test/ui/layout/zero-sized-array-union.stderr @@ -1,22 +1,22 @@ -error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) --> $DIR/zero-sized-array-union.rs:59:1 | LL | type TestBaz1 = Baz1; | ^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) --> $DIR/zero-sized-array-union.rs:70:1 | LL | type TestBaz2 = Baz2; | ^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) --> $DIR/zero-sized-array-union.rs:81:1 | LL | type TestBaz3 = Baz3; | ^^^^^^^^^^^^^^^^^^^^^ -error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })) +error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size(4 bytes) })) --> $DIR/zero-sized-array-union.rs:92:1 | LL | type TestBaz4 = Baz4; diff --git a/src/test/ui/let-else/let-else-non-diverging.stderr b/src/test/ui/let-else/let-else-non-diverging.stderr index fd5a18ce7ea..b961b16b6f6 100644 --- a/src/test/ui/let-else/let-else-non-diverging.stderr +++ b/src/test/ui/let-else/let-else-non-diverging.stderr @@ -5,7 +5,7 @@ LL | let Some(x) = Some(1) else { Some(2) }; | ^^^^^^^^^^^ expected `!`, found enum `Option` | = note: expected type `!` - found type `Option<{integer}>` + found enum `Option<{integer}>` = help: try adding a diverging expression, such as `return` or `panic!(..)` = help: ...or use `match` instead of `let...else` @@ -20,8 +20,8 @@ LL | | } LL | | }; | |_____^ expected `!`, found `()` | - = note: expected type `!` - found type `()` + = note: expected type `!` + found unit type `()` = help: try adding a diverging expression, such as `return` or `panic!(..)` = help: ...or use `match` instead of `let...else` @@ -35,7 +35,7 @@ LL | | }; | |_____^ expected `!`, found enum `Option` | = note: expected type `!` - found type `Option<{integer}>` + found enum `Option<{integer}>` = help: try adding a diverging expression, such as `return` or `panic!(..)` = help: ...or use `match` instead of `let...else` diff --git a/src/test/ui/lifetimes/issue-64173-unused-lifetimes.rs b/src/test/ui/lifetimes/issue-64173-unused-lifetimes.rs new file mode 100644 index 00000000000..8080dd7dc34 --- /dev/null +++ b/src/test/ui/lifetimes/issue-64173-unused-lifetimes.rs @@ -0,0 +1,19 @@ +use std::mem::size_of; + +struct Foo<'s> { //~ ERROR: parameter `'s` is never used + array: [(); size_of::<&Self>()], + //~^ ERROR: generic `Self` types are currently not permitted in anonymous constants +} + +// The below is taken from https://github.com/rust-lang/rust/issues/66152#issuecomment-550275017 +// as the root cause seems the same. + +const fn foo<T>() -> usize { + 0 +} + +struct Bar<'a> { //~ ERROR: parameter `'a` is never used + beta: [(); foo::<&'a ()>()], //~ ERROR: a non-static lifetime is not allowed in a `const` +} + +fn main() {} diff --git a/src/test/ui/lifetimes/issue-64173-unused-lifetimes.stderr b/src/test/ui/lifetimes/issue-64173-unused-lifetimes.stderr new file mode 100644 index 00000000000..a487cbea537 --- /dev/null +++ b/src/test/ui/lifetimes/issue-64173-unused-lifetimes.stderr @@ -0,0 +1,35 @@ +error[E0658]: a non-static lifetime is not allowed in a `const` + --> $DIR/issue-64173-unused-lifetimes.rs:16:23 + | +LL | beta: [(); foo::<&'a ()>()], + | ^^ + | + = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information + = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable + +error: generic `Self` types are currently not permitted in anonymous constants + --> $DIR/issue-64173-unused-lifetimes.rs:4:28 + | +LL | array: [(); size_of::<&Self>()], + | ^^^^ + +error[E0392]: parameter `'s` is never used + --> $DIR/issue-64173-unused-lifetimes.rs:3:12 + | +LL | struct Foo<'s> { + | ^^ unused parameter + | + = help: consider removing `'s`, referring to it in a field, or using a marker such as `PhantomData` + +error[E0392]: parameter `'a` is never used + --> $DIR/issue-64173-unused-lifetimes.rs:15:12 + | +LL | struct Bar<'a> { + | ^^ unused parameter + | + = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0392, E0658. +For more information about an error, try `rustc --explain E0392`. diff --git a/src/test/ui/macros/macro-outer-attributes.stderr b/src/test/ui/macros/macro-outer-attributes.stderr index 8e064d980af..91073d3698d 100644 --- a/src/test/ui/macros/macro-outer-attributes.stderr +++ b/src/test/ui/macros/macro-outer-attributes.stderr @@ -8,6 +8,11 @@ help: consider importing this function | LL | use b::bar; | +help: if you import `bar`, refer to it directly + | +LL - a::bar(); +LL + bar(); + | error: aborting due to previous error diff --git a/src/test/ui/malformed/malformed-interpolated.rs b/src/test/ui/malformed/malformed-interpolated.rs index b962447e7ed..0d84e723fc3 100644 --- a/src/test/ui/malformed/malformed-interpolated.rs +++ b/src/test/ui/malformed/malformed-interpolated.rs @@ -10,7 +10,7 @@ macro_rules! check { check!("0"); // OK check!(0); // OK check!(0u8); //~ ERROR suffixed literals are not allowed in attributes -check!(-0); //~ ERROR unexpected token: `-0` -check!(0 + 0); //~ ERROR unexpected token: `0 + 0` +check!(-0); //~ ERROR unexpected expression: `-0` +check!(0 + 0); //~ ERROR unexpected expression: `0 + 0` fn main() {} diff --git a/src/test/ui/malformed/malformed-interpolated.stderr b/src/test/ui/malformed/malformed-interpolated.stderr index 4b9332ddd01..c24d9f15388 100644 --- a/src/test/ui/malformed/malformed-interpolated.stderr +++ b/src/test/ui/malformed/malformed-interpolated.stderr @@ -6,13 +6,13 @@ LL | check!(0u8); | = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) -error: unexpected token: `-0` +error: unexpected expression: `-0` --> $DIR/malformed-interpolated.rs:13:8 | LL | check!(-0); | ^^ -error: unexpected token: `0 + 0` +error: unexpected expression: `0 + 0` --> $DIR/malformed-interpolated.rs:14:8 | LL | check!(0 + 0); diff --git a/src/test/ui/mismatched_types/issue-26480.stderr b/src/test/ui/mismatched_types/issue-26480.stderr index 579a5b7ecb9..ae10a00671e 100644 --- a/src/test/ui/mismatched_types/issue-26480.stderr +++ b/src/test/ui/mismatched_types/issue-26480.stderr @@ -15,6 +15,10 @@ note: function defined here LL | fn write(fildes: i32, buf: *const i8, nbyte: u64) -> i64; | ^^^^^ = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) +help: you can convert a `usize` to a `u64` and panic if the converted value doesn't fit + | +LL | ($arr.len() * size_of($arr[0])).try_into().unwrap()); + | + +++++++++++++++++++++ error[E0605]: non-primitive cast: `{integer}` as `()` --> $DIR/issue-26480.rs:22:19 diff --git a/src/test/ui/namespace/namespace-mix.stderr b/src/test/ui/namespace/namespace-mix.stderr index b6108572292..037a858d7e1 100644 --- a/src/test/ui/namespace/namespace-mix.stderr +++ b/src/test/ui/namespace/namespace-mix.stderr @@ -18,6 +18,11 @@ LL | use m2::S; | LL | use xm2::S; | +help: if you import `S`, refer to it directly + | +LL - check(m1::S); +LL + check(S); + | error[E0423]: expected value, found type alias `xm1::S` --> $DIR/namespace-mix.rs:40:11 @@ -41,6 +46,11 @@ LL | use m2::S; | LL | use xm2::S; | +help: if you import `S`, refer to it directly + | +LL - check(xm1::S); +LL + check(S); + | error[E0423]: expected value, found struct variant `m7::V` --> $DIR/namespace-mix.rs:100:11 @@ -67,6 +77,11 @@ LL | use m8::V; | LL | use xm8::V; | +help: if you import `V`, refer to it directly + | +LL - check(m7::V); +LL + check(V); + | error[E0423]: expected value, found struct variant `xm7::V` --> $DIR/namespace-mix.rs:106:11 @@ -95,6 +110,11 @@ LL | use m8::V; | LL | use xm8::V; | +help: if you import `V`, refer to it directly + | +LL - check(xm7::V); +LL + check(V); + | error[E0277]: the trait bound `c::Item: Impossible` is not satisfied --> $DIR/namespace-mix.rs:33:11 diff --git a/src/test/ui/nll/lint-no-err.rs b/src/test/ui/nll/lint-no-err.rs index 4b4169faadf..f5f7bf0a758 100644 --- a/src/test/ui/nll/lint-no-err.rs +++ b/src/test/ui/nll/lint-no-err.rs @@ -23,6 +23,4 @@ fn main() { let mut conflict = Repro; let prev = conflict.get(); conflict.insert(*prev + *x); - //~^ WARN cannot borrow `conflict` as mutable because it is also borrowed as immutable - //~| WARN this borrowing pattern was not meant to be accepted } diff --git a/src/test/ui/nll/lint-no-err.stderr b/src/test/ui/nll/lint-no-err.stderr deleted file mode 100644 index 1e7aecfaa64..00000000000 --- a/src/test/ui/nll/lint-no-err.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: cannot borrow `conflict` as mutable because it is also borrowed as immutable - --> $DIR/lint-no-err.rs:25:5 - | -LL | let prev = conflict.get(); - | -------------- immutable borrow occurs here -LL | conflict.insert(*prev + *x); - | ^^^^^^^^^^^^^^^^-----^^^^^^ - | | | - | | immutable borrow later used here - | mutable borrow occurs here - | - = note: `#[warn(mutable_borrow_reservation_conflict)]` on by default - = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future - = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159> - -warning: 1 warning emitted - diff --git a/src/test/ui/or-patterns/already-bound-name.stderr b/src/test/ui/or-patterns/already-bound-name.stderr index 66112165622..368782c1e0d 100644 --- a/src/test/ui/or-patterns/already-bound-name.stderr +++ b/src/test/ui/or-patterns/already-bound-name.stderr @@ -92,7 +92,7 @@ LL | let (B(A(a, _) | B(a)) | A(a, A(a, _) | B(a))) = B(B(1)); | first introduced with type `{integer}` here | = note: expected type `{integer}` - found type `E<{integer}>` + found enum `E<{integer}>` = note: a binding must have the same type in all alternatives error: aborting due to 15 previous errors diff --git a/src/test/ui/or-patterns/inconsistent-modes.stderr b/src/test/ui/or-patterns/inconsistent-modes.stderr index dae6bb41e74..f6367ef8234 100644 --- a/src/test/ui/or-patterns/inconsistent-modes.stderr +++ b/src/test/ui/or-patterns/inconsistent-modes.stderr @@ -57,8 +57,8 @@ LL | let (Ok(ref a) | Err(ref mut a)): Result<&u8, &mut u8> = Ok(&0); | | types differ in mutability | first introduced with type `&&u8` here | - = note: expected type `&&u8` - found type `&mut &mut u8` + = note: expected reference `&&u8` + found mutable reference `&mut &mut u8` = note: a binding must have the same type in all alternatives error[E0308]: mismatched types @@ -70,8 +70,8 @@ LL | let (Ok((ref a, b)) | Err((ref mut a, ref b))) = Ok((0, &0)); | | types differ in mutability | first introduced with type `&{integer}` here | - = note: expected type `&{integer}` - found type `&mut _` + = note: expected reference `&{integer}` + found mutable reference `&mut _` = note: a binding must have the same type in all alternatives error: aborting due to 9 previous errors diff --git a/src/test/ui/parser/circular_modules_main.stderr b/src/test/ui/parser/circular_modules_main.stderr index ee45f65a3bd..c5434c72b38 100644 --- a/src/test/ui/parser/circular_modules_main.stderr +++ b/src/test/ui/parser/circular_modules_main.stderr @@ -14,6 +14,11 @@ help: consider importing this function | LL | use hi_str; | +help: if you import `hi_str`, refer to it directly + | +LL - println!("{}", circular_modules_main::hi_str()); +LL + println!("{}", hi_str()); + | error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/issues/issue-19398.stderr b/src/test/ui/parser/issues/issue-19398.stderr index f9c3ca763f2..bbd85374b4b 100644 --- a/src/test/ui/parser/issues/issue-19398.stderr +++ b/src/test/ui/parser/issues/issue-19398.stderr @@ -12,7 +12,7 @@ LL | LL | } | - the item list ends here | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.rs b/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.rs index 86fdb78cce8..bbebc99e94b 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.rs +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.rs @@ -11,4 +11,4 @@ async unsafe const fn test() {} //~| NOTE expected one of `extern` or `fn` //~| HELP `const` must come before `async unsafe` //~| SUGGESTION const async unsafe -//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.stderr b/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.stderr index 65cce77be89..f455caba158 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.stderr +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/several-kw-jump.stderr @@ -7,7 +7,7 @@ LL | async unsafe const fn test() {} | | expected one of `extern` or `fn` | help: `const` must come before `async unsafe`: `const async unsafe` | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.rs b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.rs index edfb330d671..4ff4cf5c8ca 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.rs +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.rs @@ -11,4 +11,4 @@ unsafe async fn test() {} //~| NOTE expected one of `extern` or `fn` //~| HELP `async` must come before `unsafe` //~| SUGGESTION async unsafe -//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.stderr b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.stderr index 3acd9e44004..e9eb14bf00e 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.stderr +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-async.stderr @@ -7,7 +7,7 @@ LL | unsafe async fn test() {} | | expected one of `extern` or `fn` | help: `async` must come before `unsafe`: `async unsafe` | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.rs b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.rs index abd692b80d5..2f5fbc513ee 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.rs +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.rs @@ -11,4 +11,4 @@ unsafe const fn test() {} //~| NOTE expected one of `extern` or `fn` //~| HELP `const` must come before `unsafe` //~| SUGGESTION const unsafe -//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.stderr b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.stderr index 9a3e07b1e87..0d2bc347296 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.stderr +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-const.stderr @@ -7,7 +7,7 @@ LL | unsafe const fn test() {} | | expected one of `extern` or `fn` | help: `const` must come before `unsafe`: `const unsafe` | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.rs b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.rs index 7f0761e9938..df2412e3e9b 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.rs +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.rs @@ -11,4 +11,4 @@ extern unsafe fn test() {} //~| NOTE expected `fn` //~| HELP `unsafe` must come before `extern` //~| SUGGESTION unsafe extern -//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` diff --git a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.stderr b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.stderr index 395ee9fedbc..4224713ccb5 100644 --- a/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.stderr +++ b/src/test/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.stderr @@ -7,7 +7,7 @@ LL | extern unsafe fn test() {} | | expected `fn` | help: `unsafe` must come before `extern`: `unsafe extern` | - = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern` + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to previous error diff --git a/src/test/ui/parser/recover-range-pats.stderr b/src/test/ui/parser/recover-range-pats.stderr index 8063ba8e9f7..d0979b23f60 100644 --- a/src/test/ui/parser/recover-range-pats.stderr +++ b/src/test/ui/parser/recover-range-pats.stderr @@ -316,6 +316,9 @@ LL | if let X.. .0 = 0 {} | | | | | expected integer, found floating-point number | this is of type `u8` + | + = note: expected type `u8` + found type `{float}` error[E0029]: only `char` and numeric types are allowed in range patterns --> $DIR/recover-range-pats.rs:33:12 @@ -350,6 +353,9 @@ LL | if let X..=.0 = 0 {} | | | | | expected integer, found floating-point number | this is of type `u8` + | + = note: expected type `u8` + found type `{float}` error[E0029]: only `char` and numeric types are allowed in range patterns --> $DIR/recover-range-pats.rs:54:12 @@ -384,6 +390,9 @@ LL | if let X... .0 = 0 {} | | | | | expected integer, found floating-point number | this is of type `u8` + | + = note: expected type `u8` + found type `{float}` error[E0029]: only `char` and numeric types are allowed in range patterns --> $DIR/recover-range-pats.rs:73:12 diff --git a/src/test/ui/proc-macro/auxiliary/expand-expr.rs b/src/test/ui/proc-macro/auxiliary/expand-expr.rs index 2bc34f3c6bf..a2e30e2e93b 100644 --- a/src/test/ui/proc-macro/auxiliary/expand-expr.rs +++ b/src/test/ui/proc-macro/auxiliary/expand-expr.rs @@ -12,6 +12,15 @@ use std::str::FromStr; #[proc_macro] pub fn expand_expr_is(input: TokenStream) -> TokenStream { + expand_expr_is_inner(input, false) +} + +#[proc_macro] +pub fn expand_expr_is_trim(input: TokenStream) -> TokenStream { + expand_expr_is_inner(input, true) +} + +fn expand_expr_is_inner(input: TokenStream, trim_invisible: bool) -> TokenStream { let mut iter = input.into_iter(); let mut expected_tts = Vec::new(); loop { @@ -22,14 +31,18 @@ pub fn expand_expr_is(input: TokenStream) -> TokenStream { } } - let expected = expected_tts.into_iter().collect::<TokenStream>(); - let expanded = iter.collect::<TokenStream>().expand_expr().expect("expand_expr failed"); - assert!( - expected.to_string() == expanded.to_string(), - "assert failed\nexpected: `{}`\nexpanded: `{}`", - expected.to_string(), - expanded.to_string() - ); + // If requested, trim the "invisible" delimiters at the start and end. + let expected = expected_tts.into_iter().collect::<TokenStream>().to_string(); + let expected = if trim_invisible { + let len1 = "/*«*/ ".len(); + let len2 = " /*»*/".len(); + &expected[len1..expected.len() - len2] + } else { + &expected[..] + }; + let expanded = iter.collect::<TokenStream>().expand_expr().unwrap().to_string(); + + assert_eq!(expected, expanded); TokenStream::new() } diff --git a/src/test/ui/proc-macro/capture-macro-rules-invoke.stdout b/src/test/ui/proc-macro/capture-macro-rules-invoke.stdout index 4de8746a1b4..3d0e7eaff00 100644 --- a/src/test/ui/proc-macro/capture-macro-rules-invoke.stdout +++ b/src/test/ui/proc-macro/capture-macro-rules-invoke.stdout @@ -1,4 +1,5 @@ PRINT-BANG INPUT (DISPLAY): self +PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ self /*»*/ PRINT-BANG INPUT (DEBUG): TokenStream [ Group { delimiter: None, @@ -13,8 +14,10 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ ] PRINT-BANG INPUT (DISPLAY): 1 + 1, { "a" }, let a = 1;, String, my_name, 'a, my_val = 30, std::option::Option, pub(in some::path) , [a b c], -30 -PRINT-BANG RE-COLLECTED (DISPLAY): 1 + 1, { "a" }, let a = 1, String, my_name, 'a, my_val = 30, -std :: option :: Option, pub(in some :: path), [a b c], - 30 +PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ 1 + 1 /*»*/, /*«*/ { "a" } /*»*/, /*«*/ let a = 1 /*»*/, /*«*/ +String /*»*/, my_name, /*«*/ 'a /*»*/, /*«*/ my_val = 30 /*»*/, /*«*/ +std :: option :: Option /*»*/, /*«*/ pub(in some :: path) /*»*/, [a b c], +/*«*/ - 30 /*»*/ PRINT-BANG INPUT (DEBUG): TokenStream [ Group { delimiter: None, @@ -295,6 +298,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ }, ] PRINT-BANG INPUT (DISPLAY): (a, b) +PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ (a, b) /*»*/ PRINT-BANG INPUT (DEBUG): TokenStream [ Group { delimiter: None, diff --git a/src/test/ui/proc-macro/capture-unglued-token.stdout b/src/test/ui/proc-macro/capture-unglued-token.stdout index 7e6b540332c..5fe6ff72b45 100644 --- a/src/test/ui/proc-macro/capture-unglued-token.stdout +++ b/src/test/ui/proc-macro/capture-unglued-token.stdout @@ -1,5 +1,5 @@ PRINT-BANG INPUT (DISPLAY): Vec<u8> -PRINT-BANG RE-COLLECTED (DISPLAY): Vec < u8 > +PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ Vec < u8 > /*»*/ PRINT-BANG INPUT (DEBUG): TokenStream [ Group { delimiter: None, diff --git a/src/test/ui/proc-macro/expand-expr.rs b/src/test/ui/proc-macro/expand-expr.rs index d1146d97030..edcb30f892c 100644 --- a/src/test/ui/proc-macro/expand-expr.rs +++ b/src/test/ui/proc-macro/expand-expr.rs @@ -2,9 +2,9 @@ extern crate expand_expr; -use expand_expr::{ - check_expand_expr_file, echo_pm, expand_expr_fail, expand_expr_is, recursive_expand, -}; +use expand_expr::{check_expand_expr_file, echo_pm, expand_expr_fail, expand_expr_is}; +use expand_expr::{expand_expr_is_trim, recursive_expand}; + // Check builtin macros can be expanded. @@ -47,21 +47,21 @@ macro_rules! echo_expr { macro_rules! simple_lit { ($l:literal) => { - expand_expr_is!($l, $l); - expand_expr_is!($l, echo_lit!($l)); - expand_expr_is!($l, echo_expr!($l)); - expand_expr_is!($l, echo_tts!($l)); - expand_expr_is!($l, echo_pm!($l)); + expand_expr_is_trim!($l, $l); + expand_expr_is_trim!($l, echo_lit!($l)); + expand_expr_is_trim!($l, echo_expr!($l)); + expand_expr_is_trim!($l, echo_tts!($l)); + expand_expr_is_trim!($l, echo_pm!($l)); const _: () = { macro_rules! mac { () => { $l }; } - expand_expr_is!($l, mac!()); - expand_expr_is!($l, echo_expr!(mac!())); - expand_expr_is!($l, echo_tts!(mac!())); - expand_expr_is!($l, echo_pm!(mac!())); + expand_expr_is_trim!($l, mac!()); + expand_expr_is_trim!($l, echo_expr!(mac!())); + expand_expr_is_trim!($l, echo_tts!(mac!())); + expand_expr_is_trim!($l, echo_pm!(mac!())); }; }; } diff --git a/src/test/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout b/src/test/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout index 686d53e8876..04b516fd254 100644 --- a/src/test/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout +++ b/src/test/ui/proc-macro/expr-stmt-nonterminal-tokens.stdout @@ -1,5 +1,6 @@ PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = #[allow(warnings)] 0 ; 0 }, } -PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = #[allow(warnings)] #[allow(warnings)] 0 ; 0 }, } +PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E +{ V = { let _ = /*«*/ #[allow(warnings)] #[allow(warnings)] 0 /*»*/ ; 0 }, } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", @@ -123,7 +124,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ }, ] PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { 0; } ; 0 }, } -PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { 0 } ; 0 }, } +PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ 0 /*»*/ } ; 0 }, } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", @@ -203,6 +204,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ }, ] PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { {} } ; 0 }, } +PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ {} /*»*/ } ; 0 }, } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", @@ -281,7 +283,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ }, ] PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { PATH; } ; 0 }, } -PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { PATH } ; 0 }, } +PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ PATH /*»*/ } ; 0 }, } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", @@ -359,7 +361,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ }, ] PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { 0 + 1; } ; 0 }, } -PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { 0 + 1 } ; 0 }, } +PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ 0 + 1 /*»*/ } ; 0 }, } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", @@ -450,7 +452,7 @@ PRINT-DERIVE INPUT (DEBUG): TokenStream [ }, ] PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { PATH + 1; } ; 0 }, } -PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { PATH + 1 } ; 0 }, } +PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ PATH + 1 /*»*/ } ; 0 }, } PRINT-DERIVE INPUT (DEBUG): TokenStream [ Ident { ident: "enum", diff --git a/src/test/ui/proc-macro/issue-75734-pp-paren.stdout b/src/test/ui/proc-macro/issue-75734-pp-paren.stdout index 0fda6654ff3..55818969c71 100644 --- a/src/test/ui/proc-macro/issue-75734-pp-paren.stdout +++ b/src/test/ui/proc-macro/issue-75734-pp-paren.stdout @@ -96,6 +96,7 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ }, ] PRINT-BANG INPUT (DISPLAY): 1 + 1 * 2 +PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ 1 + 1 /*»*/ * 2 PRINT-BANG INPUT (DEBUG): TokenStream [ Group { delimiter: None, 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 60a400a5dea..6cf8043c34f 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 @@ -1,7 +1,7 @@ PRINT-BANG INPUT (DISPLAY): foo! { #[fake_attr] mod bar { #![doc = r" Foo"] } } -PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): foo! { #[fake_attr] mod bar { #! [doc = r" Foo"] } } +PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): foo! { #[fake_attr] /*«*/ mod bar { #! [doc = r" Foo"] } /*»*/ } PRINT-BANG INPUT (DEBUG): TokenStream [ Ident { ident: "foo", diff --git a/src/test/ui/proc-macro/issue-80760-empty-stmt.stdout b/src/test/ui/proc-macro/issue-80760-empty-stmt.stdout index 4b7ed874307..adbd653ead4 100644 --- a/src/test/ui/proc-macro/issue-80760-empty-stmt.stdout +++ b/src/test/ui/proc-macro/issue-80760-empty-stmt.stdout @@ -1,4 +1,5 @@ PRINT-BANG INPUT (DISPLAY): ; +PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ ; /*»*/ PRINT-BANG INPUT (DEBUG): TokenStream [ Group { delimiter: None, diff --git a/src/test/ui/proc-macro/nested-nonterminal-tokens.stdout b/src/test/ui/proc-macro/nested-nonterminal-tokens.stdout index a3d24dd26fe..b912e426d5d 100644 --- a/src/test/ui/proc-macro/nested-nonterminal-tokens.stdout +++ b/src/test/ui/proc-macro/nested-nonterminal-tokens.stdout @@ -1,4 +1,6 @@ PRINT-BANG INPUT (DISPLAY): 0 + 1 + 2 + 3 +PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ 0 + 1 + 2 /*»*/ + 3 +PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): /*«*/ /*«*/ /*«*/ 0 /*»*/ + 1 /*»*/ + 2 /*»*/ + 3 PRINT-BANG INPUT (DEBUG): TokenStream [ Group { delimiter: None, diff --git a/src/test/ui/proc-macro/nodelim-groups.stdout b/src/test/ui/proc-macro/nodelim-groups.stdout index 6b410f0bfb7..0d2f33b4175 100644 --- a/src/test/ui/proc-macro/nodelim-groups.stdout +++ b/src/test/ui/proc-macro/nodelim-groups.stdout @@ -1,4 +1,5 @@ PRINT-BANG INPUT (DISPLAY): "hi" 1 + (25) + 1 (1 + 1) +PRINT-BANG RE-COLLECTED (DISPLAY): "hi" /*«*/ 1 + (25) + 1 /*»*/ (1 + 1) PRINT-BANG INPUT (DEBUG): TokenStream [ Literal { kind: Str, @@ -71,6 +72,9 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ }, ] PRINT-BANG INPUT (DISPLAY): "hi" "hello".len() + "world".len() (1 + 1) +PRINT-BANG RE-COLLECTED (DISPLAY): "hi" /*«*/ "hello".len() + "world".len() /*»*/ (1 + 1) +PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): "hi" /*«*/ /*«*/ "hello".len() /*»*/ + /*«*/ "world".len() /*»*/ /*»*/ +(1 + 1) PRINT-BANG INPUT (DEBUG): TokenStream [ Literal { kind: Str, diff --git a/src/test/ui/proc-macro/nonterminal-expansion.stdout b/src/test/ui/proc-macro/nonterminal-expansion.stdout index 4d884348f2c..32981e7011d 100644 --- a/src/test/ui/proc-macro/nonterminal-expansion.stdout +++ b/src/test/ui/proc-macro/nonterminal-expansion.stdout @@ -1,5 +1,5 @@ PRINT-ATTR_ARGS INPUT (DISPLAY): a, line!(), b -PRINT-ATTR_ARGS RE-COLLECTED (DISPLAY): a, line! (), b +PRINT-ATTR_ARGS RE-COLLECTED (DISPLAY): a, /*«*/ line! () /*»*/, b PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [ Ident { ident: "a", diff --git a/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout b/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout index c08e5308138..ba18ca75d7f 100644 --- a/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout +++ b/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout @@ -1,5 +1,5 @@ PRINT-BANG INPUT (DISPLAY): struct S; -PRINT-BANG RE-COLLECTED (DISPLAY): struct S ; +PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ struct S ; /*»*/ PRINT-BANG INPUT (DEBUG): TokenStream [ Group { delimiter: None, diff --git a/src/test/ui/proc-macro/parent-source-spans.rs b/src/test/ui/proc-macro/parent-source-spans.rs index 354657db4db..71e5065a87a 100644 --- a/src/test/ui/proc-macro/parent-source-spans.rs +++ b/src/test/ui/proc-macro/parent-source-spans.rs @@ -8,16 +8,16 @@ use parent_source_spans::parent_source_spans; macro one($a:expr, $b:expr) { two!($a, $b); - //~^ ERROR first parent: "hello" - //~| ERROR second parent: "world" + //~^ ERROR first parent: /*«*/ "hello" /*»*/ + //~| ERROR second parent: /*«*/ "world" /*»*/ } macro two($a:expr, $b:expr) { three!($a, $b); - //~^ ERROR first final: "hello" - //~| ERROR second final: "world" - //~| ERROR first final: "yay" - //~| ERROR second final: "rust" + //~^ ERROR first final: /*«*/ "hello" /*»*/ + //~| ERROR second final: /*«*/ "world" /*»*/ + //~| ERROR first final: /*«*/ "yay" /*»*/ + //~| ERROR second final: /*«*/ "rust" /*»*/ } // forwarding tokens directly doesn't create a new source chain @@ -34,16 +34,16 @@ macro four($($tokens:tt)*) { fn main() { one!("hello", "world"); - //~^ ERROR first grandparent: "hello" - //~| ERROR second grandparent: "world" - //~| ERROR first source: "hello" - //~| ERROR second source: "world" + //~^ ERROR first grandparent: /*«*/ "hello" /*»*/ + //~| ERROR second grandparent: /*«*/ "world" /*»*/ + //~| ERROR first source: /*«*/ "hello" /*»*/ + //~| ERROR second source: /*«*/ "world" /*»*/ two!("yay", "rust"); - //~^ ERROR first parent: "yay" - //~| ERROR second parent: "rust" - //~| ERROR first source: "yay" - //~| ERROR second source: "rust" + //~^ ERROR first parent: /*«*/ "yay" /*»*/ + //~| ERROR second parent: /*«*/ "rust" /*»*/ + //~| ERROR first source: /*«*/ "yay" /*»*/ + //~| ERROR second source: /*«*/ "rust" /*»*/ three!("hip", "hop"); //~^ ERROR first final: "hip" diff --git a/src/test/ui/proc-macro/parent-source-spans.stderr b/src/test/ui/proc-macro/parent-source-spans.stderr index 4548269b507..e42218ea701 100644 --- a/src/test/ui/proc-macro/parent-source-spans.stderr +++ b/src/test/ui/proc-macro/parent-source-spans.stderr @@ -1,4 +1,4 @@ -error: first final: "hello" +error: first final: /*«*/ "hello" /*»*/ --> $DIR/parent-source-spans.rs:16:12 | LL | three!($a, $b); @@ -9,7 +9,7 @@ LL | one!("hello", "world"); | = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info) -error: second final: "world" +error: second final: /*«*/ "world" /*»*/ --> $DIR/parent-source-spans.rs:16:16 | LL | three!($a, $b); @@ -20,7 +20,7 @@ LL | one!("hello", "world"); | = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info) -error: first parent: "hello" +error: first parent: /*«*/ "hello" /*»*/ --> $DIR/parent-source-spans.rs:10:5 | LL | two!($a, $b); @@ -31,7 +31,7 @@ LL | one!("hello", "world"); | = note: this error originates in the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info) -error: second parent: "world" +error: second parent: /*«*/ "world" /*»*/ --> $DIR/parent-source-spans.rs:10:5 | LL | two!($a, $b); @@ -42,31 +42,31 @@ LL | one!("hello", "world"); | = note: this error originates in the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info) -error: first grandparent: "hello" +error: first grandparent: /*«*/ "hello" /*»*/ --> $DIR/parent-source-spans.rs:36:5 | LL | one!("hello", "world"); | ^^^^^^^^^^^^^^^^^^^^^^ -error: second grandparent: "world" +error: second grandparent: /*«*/ "world" /*»*/ --> $DIR/parent-source-spans.rs:36:5 | LL | one!("hello", "world"); | ^^^^^^^^^^^^^^^^^^^^^^ -error: first source: "hello" +error: first source: /*«*/ "hello" /*»*/ --> $DIR/parent-source-spans.rs:36:5 | LL | one!("hello", "world"); | ^^^^^^^^^^^^^^^^^^^^^^ -error: second source: "world" +error: second source: /*«*/ "world" /*»*/ --> $DIR/parent-source-spans.rs:36:5 | LL | one!("hello", "world"); | ^^^^^^^^^^^^^^^^^^^^^^ -error: first final: "yay" +error: first final: /*«*/ "yay" /*»*/ --> $DIR/parent-source-spans.rs:16:12 | LL | three!($a, $b); @@ -77,7 +77,7 @@ LL | two!("yay", "rust"); | = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info) -error: second final: "rust" +error: second final: /*«*/ "rust" /*»*/ --> $DIR/parent-source-spans.rs:16:16 | LL | three!($a, $b); @@ -88,25 +88,25 @@ LL | two!("yay", "rust"); | = note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info) -error: first parent: "yay" +error: first parent: /*«*/ "yay" /*»*/ --> $DIR/parent-source-spans.rs:42:5 | LL | two!("yay", "rust"); | ^^^^^^^^^^^^^^^^^^^ -error: second parent: "rust" +error: second parent: /*«*/ "rust" /*»*/ --> $DIR/parent-source-spans.rs:42:5 | LL | two!("yay", "rust"); | ^^^^^^^^^^^^^^^^^^^ -error: first source: "yay" +error: first source: /*«*/ "yay" /*»*/ --> $DIR/parent-source-spans.rs:42:5 | LL | two!("yay", "rust"); | ^^^^^^^^^^^^^^^^^^^ -error: second source: "rust" +error: second source: /*«*/ "rust" /*»*/ --> $DIR/parent-source-spans.rs:42:5 | LL | two!("yay", "rust"); diff --git a/src/test/ui/reify-intrinsic.stderr b/src/test/ui/reify-intrinsic.stderr index dff4ca68d31..70a64446f6a 100644 --- a/src/test/ui/reify-intrinsic.stderr +++ b/src/test/ui/reify-intrinsic.stderr @@ -25,8 +25,8 @@ error[E0308]: cannot coerce intrinsics to function pointers LL | std::intrinsics::unlikely, | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers | - = note: expected type `extern "rust-intrinsic" fn(_) -> _ {likely}` - found fn item `extern "rust-intrinsic" fn(_) -> _ {unlikely}` + = note: expected fn item `extern "rust-intrinsic" fn(_) -> _ {likely}` + found fn item `extern "rust-intrinsic" fn(_) -> _ {unlikely}` error: aborting due to 3 previous errors diff --git a/src/test/ui/resolve/enums-are-namespaced-xc.stderr b/src/test/ui/resolve/enums-are-namespaced-xc.stderr index 621686dd292..1d26a2c0058 100644 --- a/src/test/ui/resolve/enums-are-namespaced-xc.stderr +++ b/src/test/ui/resolve/enums-are-namespaced-xc.stderr @@ -8,6 +8,11 @@ help: consider importing this unit variant | LL | use namespaced_enums::Foo::A; | +help: if you import `A`, refer to it directly + | +LL - let _ = namespaced_enums::A; +LL + let _ = A; + | error[E0425]: cannot find function, tuple struct or tuple variant `B` in crate `namespaced_enums` --> $DIR/enums-are-namespaced-xc.rs:7:31 @@ -19,6 +24,11 @@ help: consider importing this tuple variant | LL | use namespaced_enums::Foo::B; | +help: if you import `B`, refer to it directly + | +LL - let _ = namespaced_enums::B(10); +LL + let _ = B(10); + | error[E0422]: cannot find struct, variant or union type `C` in crate `namespaced_enums` --> $DIR/enums-are-namespaced-xc.rs:9:31 @@ -30,6 +40,11 @@ help: consider importing this variant | LL | use namespaced_enums::Foo::C; | +help: if you import `C`, refer to it directly + | +LL - let _ = namespaced_enums::C { a: 10 }; +LL + let _ = C { a: 10 }; + | error: aborting due to 3 previous errors diff --git a/src/test/ui/resolve/issue-2356.stderr b/src/test/ui/resolve/issue-2356.stderr index d1872673885..b8d528efc15 100644 --- a/src/test/ui/resolve/issue-2356.stderr +++ b/src/test/ui/resolve/issue-2356.stderr @@ -48,6 +48,11 @@ error[E0425]: cannot find function `static_method` in this scope | LL | static_method(); | ^^^^^^^^^^^^^ not found in this scope + | +help: consider using the associated function + | +LL | Self::static_method(); + | ~~~~~~~~~~~~~~~~~~~ error[E0425]: cannot find function `purr` in this scope --> $DIR/issue-2356.rs:54:9 @@ -85,6 +90,11 @@ error[E0425]: cannot find function `grow_older` in this scope | LL | grow_older(); | ^^^^^^^^^^ not found in this scope + | +help: consider using the associated function + | +LL | Self::grow_older(); + | ~~~~~~~~~~~~~~~~ error[E0425]: cannot find function `shave` in this scope --> $DIR/issue-2356.rs:74:5 diff --git a/src/test/ui/resolve/issue-50599.stderr b/src/test/ui/resolve/issue-50599.stderr index 7ec567a06f0..4cc035cb11e 100644 --- a/src/test/ui/resolve/issue-50599.stderr +++ b/src/test/ui/resolve/issue-50599.stderr @@ -10,6 +10,11 @@ LL | use std::f32::consts::LOG10_2; | LL | use std::f64::consts::LOG10_2; | +help: if you import `LOG10_2`, refer to it directly + | +LL - const M: usize = (f64::from(N) * std::f64::LOG10_2) as usize; +LL + const M: usize = (f64::from(N) * LOG10_2) as usize; + | error: aborting due to previous error diff --git a/src/test/ui/resolve/missing-in-namespace.stderr b/src/test/ui/resolve/missing-in-namespace.stderr index 8b292aeda50..338a5423aa4 100644 --- a/src/test/ui/resolve/missing-in-namespace.stderr +++ b/src/test/ui/resolve/missing-in-namespace.stderr @@ -8,6 +8,11 @@ help: consider importing this struct | LL | use std::collections::HashMap; | +help: if you import `HashMap`, refer to it directly + | +LL - let _map = std::hahmap::HashMap::new(); +LL + let _map = HashMap::new(); + | error: aborting due to previous error diff --git a/src/test/ui/resolve/privacy-enum-ctor.stderr b/src/test/ui/resolve/privacy-enum-ctor.stderr index c93ba915efb..ed89170fd8a 100644 --- a/src/test/ui/resolve/privacy-enum-ctor.stderr +++ b/src/test/ui/resolve/privacy-enum-ctor.stderr @@ -105,6 +105,11 @@ LL | use std::f32::consts::E; | LL | use std::f64::consts::E; | +help: if you import `E`, refer to it directly + | +LL - let _: E = m::E; +LL + let _: E = E; + | error[E0423]: expected value, found struct variant `m::E::Struct` --> $DIR/privacy-enum-ctor.rs:45:16 diff --git a/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr b/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr index c14dfa3601a..96c1869b4e7 100644 --- a/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr +++ b/src/test/ui/resolve/resolve-inconsistent-binding-mode.stderr @@ -54,8 +54,8 @@ LL | Opts::A(ref mut i) | Opts::B(ref i) => {} | | | first introduced with type `&mut isize` here | - = note: expected type `&mut isize` - found type `&isize` + = note: expected mutable reference `&mut isize` + found reference `&isize` = note: in the same arm, a binding must have the same type in all alternatives error: aborting due to 6 previous errors diff --git a/src/test/ui/resolve/resolve-primitive-fallback.stderr b/src/test/ui/resolve/resolve-primitive-fallback.stderr index 44631f954df..fcbc28475f9 100644 --- a/src/test/ui/resolve/resolve-primitive-fallback.stderr +++ b/src/test/ui/resolve/resolve-primitive-fallback.stderr @@ -14,6 +14,11 @@ help: consider importing this builtin type | LL | use std::primitive::u8; | +help: if you import `u8`, refer to it directly + | +LL - let _: ::u8; +LL + let _: u8; + | error[E0061]: this function takes 0 arguments but 1 argument was supplied --> $DIR/resolve-primitive-fallback.rs:3:5 diff --git a/src/test/ui/rfc-2005-default-binding-mode/lit.stderr b/src/test/ui/rfc-2005-default-binding-mode/lit.stderr index 6d18a39606c..11bc170cdfa 100644 --- a/src/test/ui/rfc-2005-default-binding-mode/lit.stderr +++ b/src/test/ui/rfc-2005-default-binding-mode/lit.stderr @@ -6,8 +6,8 @@ LL | match &s { LL | "abc" => true, | ^^^^^ expected `&str`, found `str` | - = note: expected type `&&str` - found reference `&'static str` + = note: expected reference `&&str` + found reference `&'static str` error[E0308]: mismatched types --> $DIR/lit.rs:16:9 @@ -17,8 +17,8 @@ LL | match &s { LL | b"abc" => true, | ^^^^^^ expected `&[u8]`, found array `[u8; 3]` | - = note: expected type `&&[u8]` - found reference `&'static [u8; 3]` + = note: expected reference `&&[u8]` + found reference `&'static [u8; 3]` error: aborting due to 2 previous errors diff --git a/src/test/ui/span/E0057.rs b/src/test/ui/span/E0057.rs deleted file mode 100644 index 83f941f65b9..00000000000 --- a/src/test/ui/span/E0057.rs +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - let f = |x| x * 3; - let a = f(); //~ ERROR E0057 - let b = f(4); - let c = f(2, 3); //~ ERROR E0057 -} diff --git a/src/test/ui/span/E0057.stderr b/src/test/ui/span/E0057.stderr deleted file mode 100644 index a151b20f865..00000000000 --- a/src/test/ui/span/E0057.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0057]: this function takes 1 argument but 0 arguments were supplied - --> $DIR/E0057.rs:3:13 - | -LL | let a = f(); - | ^-- an argument is missing - | -help: provide the argument - | -LL | let a = f({_}); - | ~~~~~~ - -error[E0057]: this function takes 1 argument but 2 arguments were supplied - --> $DIR/E0057.rs:5:13 - | -LL | let c = f(2, 3); - | ^ - argument unexpected - | -help: remove the extra argument - | -LL | let c = f(2); - | ~~~~ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0057`. diff --git a/src/test/ui/suggestions/assoc_fn_without_self.rs b/src/test/ui/suggestions/assoc_fn_without_self.rs new file mode 100644 index 00000000000..778d9847773 --- /dev/null +++ b/src/test/ui/suggestions/assoc_fn_without_self.rs @@ -0,0 +1,20 @@ +fn main() {} + +struct S; + +impl S { + fn foo() {} + + fn bar(&self) {} + + fn baz(a: u8, b: u8) {} + + fn b() { + fn c() { + foo(); //~ ERROR cannot find function `foo` in this scope + } + foo(); //~ ERROR cannot find function `foo` in this scope + bar(); //~ ERROR cannot find function `bar` in this scope + baz(2, 3); //~ ERROR cannot find function `baz` in this scope + } +} diff --git a/src/test/ui/suggestions/assoc_fn_without_self.stderr b/src/test/ui/suggestions/assoc_fn_without_self.stderr new file mode 100644 index 00000000000..4a0e62e7309 --- /dev/null +++ b/src/test/ui/suggestions/assoc_fn_without_self.stderr @@ -0,0 +1,37 @@ +error[E0425]: cannot find function `foo` in this scope + --> $DIR/assoc_fn_without_self.rs:14:13 + | +LL | foo(); + | ^^^ not found in this scope + +error[E0425]: cannot find function `foo` in this scope + --> $DIR/assoc_fn_without_self.rs:16:9 + | +LL | foo(); + | ^^^ not found in this scope + | +help: consider using the associated function + | +LL | Self::foo(); + | ~~~~~~~~~ + +error[E0425]: cannot find function `bar` in this scope + --> $DIR/assoc_fn_without_self.rs:17:9 + | +LL | bar(); + | ^^^ not found in this scope + +error[E0425]: cannot find function `baz` in this scope + --> $DIR/assoc_fn_without_self.rs:18:9 + | +LL | baz(2, 3); + | ^^^ not found in this scope + | +help: consider using the associated function + | +LL | Self::baz(2, 3); + | ~~~~~~~~~ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/suggestions/issue-81839.stderr b/src/test/ui/suggestions/issue-81839.stderr index 0b1c41b837f..fae474cedb8 100644 --- a/src/test/ui/suggestions/issue-81839.stderr +++ b/src/test/ui/suggestions/issue-81839.stderr @@ -19,8 +19,8 @@ note: while checking the return type of the `async fn` | LL | pub async fn answer_str(&self, _s: &str) -> Test { | ^^^^ checked the `Output` of this `async fn`, found opaque type - = note: expected type `()` - found opaque type `impl Future<Output = Test>` + = note: expected unit type `()` + found opaque type `impl Future<Output = Test>` error: aborting due to previous error diff --git a/src/test/ui/suggestions/issue-96223.rs b/src/test/ui/suggestions/issue-96223.rs new file mode 100644 index 00000000000..85667bb849b --- /dev/null +++ b/src/test/ui/suggestions/issue-96223.rs @@ -0,0 +1,52 @@ +// Previously ICEd because we didn't properly track binders in suggestions +// check-fail + +pub trait Foo<'de>: Sized {} + +pub trait Bar<'a>: 'static { + type Inner: 'a; +} + +pub trait Fubar { + type Bar: for<'a> Bar<'a>; +} + +pub struct Baz<T>(pub T); + +impl<'de, T> Foo<'de> for Baz<T> where T: Foo<'de> {} + +struct Empty; + +impl<M> Dummy<M> for Empty +where + M: Fubar, + for<'de> Baz<<M::Bar as Bar<'de>>::Inner>: Foo<'de>, +{ +} + +pub trait Dummy<M> +where + M: Fubar, +{ +} + +pub struct EmptyBis<'a>(&'a [u8]); + +impl<'a> Bar<'a> for EmptyBis<'static> { + type Inner = EmptyBis<'a>; +} + +pub struct EmptyMarker; + +impl Fubar for EmptyMarker { + type Bar = EmptyBis<'static>; +} + +fn icey_bounds<D: Dummy<EmptyMarker>>(p: &D) {} + +fn trigger_ice() { + let p = Empty; + icey_bounds(&p); //~ERROR the trait bound +} + +fn main() {} diff --git a/src/test/ui/suggestions/issue-96223.stderr b/src/test/ui/suggestions/issue-96223.stderr new file mode 100644 index 00000000000..513725d9962 --- /dev/null +++ b/src/test/ui/suggestions/issue-96223.stderr @@ -0,0 +1,28 @@ +error[E0277]: the trait bound `for<'de> EmptyBis<'de>: Foo<'_>` is not satisfied + --> $DIR/issue-96223.rs:49:17 + | +LL | icey_bounds(&p); + | ----------- ^^ the trait `for<'de> Foo<'_>` is not implemented for `EmptyBis<'de>` + | | + | required by a bound introduced by this call + | + = help: the trait `Foo<'de>` is implemented for `Baz<T>` +note: required because of the requirements on the impl of `for<'de> Foo<'de>` for `Baz<EmptyBis<'de>>` + --> $DIR/issue-96223.rs:16:14 + | +LL | impl<'de, T> Foo<'de> for Baz<T> where T: Foo<'de> {} + | ^^^^^^^^ ^^^^^^ +note: required because of the requirements on the impl of `Dummy<EmptyMarker>` for `Empty` + --> $DIR/issue-96223.rs:20:9 + | +LL | impl<M> Dummy<M> for Empty + | ^^^^^^^^ ^^^^^ +note: required by a bound in `icey_bounds` + --> $DIR/issue-96223.rs:45:19 + | +LL | fn icey_bounds<D: Dummy<EmptyMarker>>(p: &D) {} + | ^^^^^^^^^^^^^^^^^^ required by this bound in `icey_bounds` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/match-prev-arm-needing-semi.rs b/src/test/ui/suggestions/match-prev-arm-needing-semi.rs index 6aa93a24d2f..8c8abe047c2 100644 --- a/src/test/ui/suggestions/match-prev-arm-needing-semi.rs +++ b/src/test/ui/suggestions/match-prev-arm-needing-semi.rs @@ -16,6 +16,9 @@ fn extra_semicolon() { async fn async_dummy() {} //~ NOTE checked the `Output` of this `async fn`, found opaque type //~| NOTE while checking the return type of the `async fn` //~| NOTE in this expansion of desugaring of `async` block or function +//~| NOTE checked the `Output` of this `async fn`, expected opaque type +//~| NOTE while checking the return type of the `async fn` +//~| NOTE in this expansion of desugaring of `async` block or function async fn async_dummy2() {} //~ NOTE checked the `Output` of this `async fn`, found opaque type //~| NOTE checked the `Output` of this `async fn`, found opaque type //~| NOTE while checking the return type of the `async fn` @@ -31,7 +34,7 @@ async fn async_extra_semicolon_same() { } false => async_dummy(), //~ ERROR `match` arms have incompatible types //~^ NOTE expected `()`, found opaque type - //~| NOTE expected type `()` + //~| NOTE expected unit type `()` //~| HELP consider `await`ing on the `Future` }; } @@ -44,7 +47,7 @@ async fn async_extra_semicolon_different() { } false => async_dummy2(), //~ ERROR `match` arms have incompatible types //~^ NOTE expected `()`, found opaque type - //~| NOTE expected type `()` + //~| NOTE expected unit type `()` //~| HELP consider `await`ing on the `Future` }; } @@ -55,7 +58,7 @@ async fn async_different_futures() { //~| HELP consider `await`ing on both `Future`s false => async_dummy2(), //~ ERROR `match` arms have incompatible types //~^ NOTE expected opaque type, found a different opaque type - //~| NOTE expected type `impl Future<Output = ()>` + //~| NOTE expected opaque type `impl Future<Output = ()>` //~| NOTE distinct uses of `impl Trait` result in different opaque types }; } diff --git a/src/test/ui/suggestions/match-prev-arm-needing-semi.stderr b/src/test/ui/suggestions/match-prev-arm-needing-semi.stderr index b55c51b9280..4c4b782bd6f 100644 --- a/src/test/ui/suggestions/match-prev-arm-needing-semi.stderr +++ b/src/test/ui/suggestions/match-prev-arm-needing-semi.stderr @@ -1,5 +1,5 @@ error[E0308]: `match` arms have incompatible types - --> $DIR/match-prev-arm-needing-semi.rs:32:18 + --> $DIR/match-prev-arm-needing-semi.rs:35:18 | LL | let _ = match true { | _____________- @@ -20,8 +20,8 @@ note: while checking the return type of the `async fn` | LL | async fn async_dummy() {} | ^ checked the `Output` of this `async fn`, found opaque type - = note: expected type `()` - found opaque type `impl Future<Output = ()>` + = note: expected unit type `()` + found opaque type `impl Future<Output = ()>` help: consider `await`ing on the `Future` | LL | false => async_dummy().await, @@ -33,7 +33,7 @@ LL + async_dummy() | error[E0308]: `match` arms have incompatible types - --> $DIR/match-prev-arm-needing-semi.rs:45:18 + --> $DIR/match-prev-arm-needing-semi.rs:48:18 | LL | let _ = match true { | _____________- @@ -50,12 +50,12 @@ LL | | }; | |_____- `match` arms have incompatible types | note: while checking the return type of the `async fn` - --> $DIR/match-prev-arm-needing-semi.rs:19:25 + --> $DIR/match-prev-arm-needing-semi.rs:22:25 | LL | async fn async_dummy2() {} | ^ checked the `Output` of this `async fn`, found opaque type - = note: expected type `()` - found opaque type `impl Future<Output = ()>` + = note: expected unit type `()` + found opaque type `impl Future<Output = ()>` help: consider `await`ing on the `Future` | LL | false => async_dummy2().await, @@ -69,7 +69,7 @@ LL ~ false => Box::new(async_dummy2()), | error[E0308]: `match` arms have incompatible types - --> $DIR/match-prev-arm-needing-semi.rs:56:18 + --> $DIR/match-prev-arm-needing-semi.rs:59:18 | LL | let _ = match true { | _____________- @@ -84,12 +84,17 @@ LL | | }; | |_____- `match` arms have incompatible types | note: while checking the return type of the `async fn` - --> $DIR/match-prev-arm-needing-semi.rs:19:25 + --> $DIR/match-prev-arm-needing-semi.rs:16:24 + | +LL | async fn async_dummy() {} + | ^ checked the `Output` of this `async fn`, expected opaque type +note: while checking the return type of the `async fn` + --> $DIR/match-prev-arm-needing-semi.rs:22:25 | LL | async fn async_dummy2() {} | ^ checked the `Output` of this `async fn`, found opaque type - = note: expected type `impl Future<Output = ()>` (opaque type at <$DIR/match-prev-arm-needing-semi.rs:16:24>) - found opaque type `impl Future<Output = ()>` (opaque type at <$DIR/match-prev-arm-needing-semi.rs:19:25>) + = note: expected opaque type `impl Future<Output = ()>` (opaque type at <$DIR/match-prev-arm-needing-semi.rs:16:24>) + found opaque type `impl Future<Output = ()>` (opaque type at <$DIR/match-prev-arm-needing-semi.rs:22:25>) = note: distinct uses of `impl Trait` result in different opaque types help: consider `await`ing on both `Future`s | diff --git a/src/test/ui/suggestions/match-with-different-arm-types-as-stmt-instead-of-expr.stderr b/src/test/ui/suggestions/match-with-different-arm-types-as-stmt-instead-of-expr.stderr index 60f423a1163..be6fc261562 100644 --- a/src/test/ui/suggestions/match-with-different-arm-types-as-stmt-instead-of-expr.stderr +++ b/src/test/ui/suggestions/match-with-different-arm-types-as-stmt-instead-of-expr.stderr @@ -20,8 +20,8 @@ LL | | _ => Box::new(Bar), LL | | }; | |_____- `match` arms have incompatible types | - = note: expected type `Box<Baz>` - found struct `Box<Bar>` + = note: expected struct `Box<Baz>` + found struct `Box<Bar>` note: you might have meant to return the `match` expression --> $DIR/match-with-different-arm-types-as-stmt-instead-of-expr.rs:27:6 | diff --git a/src/test/ui/suggestions/opaque-type-error.stderr b/src/test/ui/suggestions/opaque-type-error.stderr index e065e0aaa8e..133ffb05873 100644 --- a/src/test/ui/suggestions/opaque-type-error.stderr +++ b/src/test/ui/suggestions/opaque-type-error.stderr @@ -1,6 +1,9 @@ error[E0308]: `if` and `else` have incompatible types --> $DIR/opaque-type-error.rs:20:9 | +LL | fn thing_one() -> impl Future<Output = Result<(), ()>> { + | ------------------------------------ the expected opaque type +... LL | fn thing_two() -> impl Future<Output = Result<(), ()>> { | ------------------------------------ the found opaque type ... @@ -13,8 +16,8 @@ LL | | thing_two() LL | | }.await | |_____- `if` and `else` have incompatible types | - = note: expected type `impl Future<Output = Result<(), ()>>` (opaque type at <$DIR/opaque-type-error.rs:8:19>) - found opaque type `impl Future<Output = Result<(), ()>>` (opaque type at <$DIR/opaque-type-error.rs:12:19>) + = note: expected opaque type `impl Future<Output = Result<(), ()>>` (opaque type at <$DIR/opaque-type-error.rs:8:19>) + found opaque type `impl Future<Output = Result<(), ()>>` (opaque type at <$DIR/opaque-type-error.rs:12:19>) = note: distinct uses of `impl Trait` result in different opaque types help: consider `await`ing on both `Future`s | diff --git a/src/test/ui/trait-bounds/shadowed-path-in-trait-bound-suggestion.fixed b/src/test/ui/trait-bounds/shadowed-path-in-trait-bound-suggestion.fixed new file mode 100644 index 00000000000..39e90d7a3f7 --- /dev/null +++ b/src/test/ui/trait-bounds/shadowed-path-in-trait-bound-suggestion.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![allow(non_snake_case)] +mod A { + pub trait Trait {} + impl Trait for i32 {} +} + +mod B { + use A::Trait; + +pub struct A<H: Trait>(pub H); //~ ERROR cannot find trait +} + +fn main() { + let _ = B::A(42); +} diff --git a/src/test/ui/trait-bounds/shadowed-path-in-trait-bound-suggestion.rs b/src/test/ui/trait-bounds/shadowed-path-in-trait-bound-suggestion.rs new file mode 100644 index 00000000000..ee6ed0cae67 --- /dev/null +++ b/src/test/ui/trait-bounds/shadowed-path-in-trait-bound-suggestion.rs @@ -0,0 +1,14 @@ +// run-rustfix +#![allow(non_snake_case)] +mod A { + pub trait Trait {} + impl Trait for i32 {} +} + +mod B { + pub struct A<H: A::Trait>(pub H); //~ ERROR cannot find trait +} + +fn main() { + let _ = B::A(42); +} diff --git a/src/test/ui/trait-bounds/shadowed-path-in-trait-bound-suggestion.stderr b/src/test/ui/trait-bounds/shadowed-path-in-trait-bound-suggestion.stderr new file mode 100644 index 00000000000..a8b275f9803 --- /dev/null +++ b/src/test/ui/trait-bounds/shadowed-path-in-trait-bound-suggestion.stderr @@ -0,0 +1,19 @@ +error[E0405]: cannot find trait `Trait` in `A` + --> $DIR/shadowed-path-in-trait-bound-suggestion.rs:9:24 + | +LL | pub struct A<H: A::Trait>(pub H); + | ^^^^^ not found in `A` + | +help: consider importing this trait + | +LL | use A::Trait; + | +help: if you import `Trait`, refer to it directly + | +LL - pub struct A<H: A::Trait>(pub H); +LL + pub struct A<H: Trait>(pub H); + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0405`. diff --git a/src/test/ui/traits/issue-52893.stderr b/src/test/ui/traits/issue-52893.stderr index f0c718c7a16..0ee44921bf5 100644 --- a/src/test/ui/traits/issue-52893.stderr +++ b/src/test/ui/traits/issue-52893.stderr @@ -9,8 +9,8 @@ LL | builder.push(output); | | | arguments to this function are incorrect | - = note: expected type `F` - found struct `Class<P>` + = note: expected type parameter `F` + found struct `Class<P>` note: associated function defined here --> $DIR/issue-52893.rs:11:8 | diff --git a/src/test/ui/traits/suggest-fully-qualified-path-with-appropriate-params.rs b/src/test/ui/traits/suggest-fully-qualified-path-with-appropriate-params.rs new file mode 100644 index 00000000000..da68b996be9 --- /dev/null +++ b/src/test/ui/traits/suggest-fully-qualified-path-with-appropriate-params.rs @@ -0,0 +1,24 @@ +struct Thing; + +trait Method<T> { + fn method(&self) -> T; + fn mut_method(&mut self) -> T; +} + +impl Method<i32> for Thing { + fn method(&self) -> i32 { 0 } + fn mut_method(&mut self) -> i32 { 0 } +} + +impl Method<u32> for Thing { + fn method(&self) -> u32 { 0 } + fn mut_method(&mut self) -> u32 { 0 } +} + +fn main() { + let thing = Thing; + thing.method(); + //~^ ERROR type annotations needed + //~| ERROR type annotations needed + thing.mut_method(); //~ ERROR type annotations needed +} diff --git a/src/test/ui/traits/suggest-fully-qualified-path-with-appropriate-params.stderr b/src/test/ui/traits/suggest-fully-qualified-path-with-appropriate-params.stderr new file mode 100644 index 00000000000..0c4962417e9 --- /dev/null +++ b/src/test/ui/traits/suggest-fully-qualified-path-with-appropriate-params.stderr @@ -0,0 +1,61 @@ +error[E0282]: type annotations needed + --> $DIR/suggest-fully-qualified-path-with-appropriate-params.rs:20:11 + | +LL | thing.method(); + | ------^^^^^^-- + | | | + | | cannot infer type for type parameter `T` declared on the trait `Method` + | this method call resolves to `T` + +error[E0283]: type annotations needed + --> $DIR/suggest-fully-qualified-path-with-appropriate-params.rs:20:11 + | +LL | thing.method(); + | ------^^^^^^-- + | | | + | | cannot infer type for type parameter `T` declared on the trait `Method` + | this method call resolves to `T` + | +note: multiple `impl`s satisfying `Thing: Method<_>` found + --> $DIR/suggest-fully-qualified-path-with-appropriate-params.rs:8:1 + | +LL | impl Method<i32> for Thing { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | impl Method<u32> for Thing { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: use the fully qualified path for the potential candidates + | +LL | <Thing as Method<i32>>::method(&thing); + | ++++++++++++++++++++++++++++++++ ~ +LL | <Thing as Method<u32>>::method(&thing); + | ++++++++++++++++++++++++++++++++ ~ + +error[E0283]: type annotations needed + --> $DIR/suggest-fully-qualified-path-with-appropriate-params.rs:23:11 + | +LL | thing.mut_method(); + | ------^^^^^^^^^^-- + | | | + | | cannot infer type for type parameter `T` declared on the trait `Method` + | this method call resolves to `T` + | +note: multiple `impl`s satisfying `Thing: Method<_>` found + --> $DIR/suggest-fully-qualified-path-with-appropriate-params.rs:8:1 + | +LL | impl Method<i32> for Thing { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | impl Method<u32> for Thing { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: use the fully qualified path for the potential candidates + | +LL | <Thing as Method<i32>>::mut_method(&mut thing); + | +++++++++++++++++++++++++++++++++++++++ ~ +LL | <Thing as Method<u32>>::mut_method(&mut thing); + | +++++++++++++++++++++++++++++++++++++++ ~ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0282, E0283. +For more information about an error, try `rustc --explain E0282`. diff --git a/src/test/ui/tuple/wrong_argument_ice-4.stderr b/src/test/ui/tuple/wrong_argument_ice-4.stderr index 0c25b6801dc..f8dfc4cd043 100644 --- a/src/test/ui/tuple/wrong_argument_ice-4.stderr +++ b/src/test/ui/tuple/wrong_argument_ice-4.stderr @@ -8,6 +8,11 @@ LL | | let b = 1; LL | | }); | |_____- argument unexpected | +note: closure defined here + --> $DIR/wrong_argument_ice-4.rs:2:6 + | +LL | (|| {})(|| { + | ^^ help: remove the extra argument | LL | (|| {})(); diff --git a/src/test/ui/type-alias-impl-trait/auxiliary/collect_hidden_types.rs b/src/test/ui/type-alias-impl-trait/auxiliary/collect_hidden_types.rs new file mode 100644 index 00000000000..75d20a6fef9 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/auxiliary/collect_hidden_types.rs @@ -0,0 +1,21 @@ +#![feature(type_alias_impl_trait)] + +// edition:2018 + +use std::future::Future; + +pub trait Service<Request> { + type Future: Future<Output = ()>; + fn call(&mut self, req: Request) -> Self::Future; +} + +// NOTE: the pub(crate) here is critical +pub(crate) fn new() -> () {} + +pub struct A; +impl Service<()> for A { + type Future = impl Future<Output = ()>; + fn call(&mut self, _: ()) -> Self::Future { + async { new() } + } +} diff --git a/src/test/ui/type-alias-impl-trait/coherence.rs b/src/test/ui/type-alias-impl-trait/coherence.rs index 1c0f83d6c12..98ac215ad6c 100644 --- a/src/test/ui/type-alias-impl-trait/coherence.rs +++ b/src/test/ui/type-alias-impl-trait/coherence.rs @@ -12,6 +12,6 @@ fn use_alias<T>(val: T) -> AliasOfForeignType<T> { } impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {} -//~^ ERROR the type parameter `T` is not constrained by the impl trait, self type, or predicates +//~^ ERROR cannot implement trait on type alias impl trait fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/coherence.stderr b/src/test/ui/type-alias-impl-trait/coherence.stderr index 6ede0fa14ba..3ce25d94f6e 100644 --- a/src/test/ui/type-alias-impl-trait/coherence.stderr +++ b/src/test/ui/type-alias-impl-trait/coherence.stderr @@ -1,9 +1,14 @@ -error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/coherence.rs:14:6 +error: cannot implement trait on type alias impl trait + --> $DIR/coherence.rs:14:41 | LL | impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {} - | ^ unconstrained type parameter + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: type alias impl trait defined here + --> $DIR/coherence.rs:9:30 + | +LL | type AliasOfForeignType<T> = impl LocalTrait; + | ^^^^^^^^^^^^^^^ error: aborting due to previous error -For more information about this error, try `rustc --explain E0207`. diff --git a/src/test/ui/type-alias-impl-trait/collect_hidden_types.rs b/src/test/ui/type-alias-impl-trait/collect_hidden_types.rs new file mode 100644 index 00000000000..e78f178e464 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/collect_hidden_types.rs @@ -0,0 +1,22 @@ +// aux-build:collect_hidden_types.rs +use collect_hidden_types::Service; +use std::future::Future; +use std::pin::Pin; +use std::task::Context; + +// build-pass + +// edition:2018 + +extern crate collect_hidden_types; + +fn broken(mut a: collect_hidden_types::A, cx: &mut Context<'_>) { + let mut fut = a.call(()); + let _ = unsafe { Pin::new_unchecked(&mut fut) }.poll(cx); +} + +pub async fn meeb(cx: &mut Context<'_>) { + broken(collect_hidden_types::A, cx); +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/cross_inference.rs b/src/test/ui/type-alias-impl-trait/cross_inference.rs new file mode 100644 index 00000000000..dafaf40a69d --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/cross_inference.rs @@ -0,0 +1,10 @@ +// check-pass + +#![feature(type_alias_impl_trait)] + +fn main() { + type T = impl Copy; + let foo: T = (1u32, 2u32); + let x: (_, _) = foo; + println!("{:?}", x); +} diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs new file mode 100644 index 00000000000..9ad7cad39d0 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs @@ -0,0 +1,24 @@ +// known-bug +// failure-status: 101 +// compile-flags: --edition=2021 --crate-type=lib +// rustc-env:RUST_BACKTRACE=0 + +// normalize-stderr-test "thread 'rustc' panicked.*" -> "thread 'rustc' panicked" +// normalize-stderr-test "note:.*RUST_BACKTRACE=1.*\n" -> "" +// normalize-stderr-test "\nerror: internal compiler error.*\n\n" -> "" +// normalize-stderr-test "note:.*unexpectedly panicked.*\n\n" -> "" +// normalize-stderr-test "note: we would appreciate a bug report.*\n\n" -> "" +// normalize-stderr-test "note: compiler flags.*\n\n" -> "" +// normalize-stderr-test "note: rustc.*running on.*\n\n" -> "" +// normalize-stderr-test "#.*\n" -> "" +// normalize-stderr-test ".*delayed.*\n" -> "" + +// tracked in https://github.com/rust-lang/rust/issues/96572 + +#![feature(type_alias_impl_trait)] + +fn main() { + type T = impl Copy; + let foo: T = (1u32, 2u32); + let (a, b): (u32, u32) = foo; +} diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.stderr b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.stderr new file mode 100644 index 00000000000..84d2705bf24 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.stderr @@ -0,0 +1,32 @@ +error: internal compiler error: no errors encountered even though `delay_span_bug` issued + +error: internal compiler error: broken MIR in DefId(0:3 ~ cross_inference_pattern_bug[646d]::main) ((_1.0: u32)): can't project out of PlaceTy { ty: main::T, variant_index: None } + --> $DIR/cross_inference_pattern_bug.rs:23:10 + | +LL | let (a, b): (u32, u32) = foo; + | ^ + | + +error: internal compiler error: TyKind::Error constructed but no error reported + | + +error: internal compiler error: TyKind::Error constructed but no error reported + | + +error: internal compiler error: broken MIR in DefId(0:3 ~ cross_inference_pattern_bug[646d]::main) ((_1.1: u32)): can't project out of PlaceTy { ty: main::T, variant_index: None } + --> $DIR/cross_inference_pattern_bug.rs:23:13 + | +LL | let (a, b): (u32, u32) = foo; + | ^ + | + +error: internal compiler error: TyKind::Error constructed but no error reported + | + +error: internal compiler error: TyKind::Error constructed but no error reported + | + +thread 'rustc' panicked + +query stack during panic: +end of query stack diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs new file mode 100644 index 00000000000..179f525de52 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs @@ -0,0 +1,13 @@ +// known-bug +// compile-flags: --edition=2021 --crate-type=lib +// rustc-env:RUST_BACKTRACE=0 + +// tracked in https://github.com/rust-lang/rust/issues/96572 + +#![feature(type_alias_impl_trait)] + +fn main() { + type T = impl Copy; // error: unconstrained opaque type + let foo: T = (1u32, 2u32); + let (a, b) = foo; // removing this line makes the code compile +} diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.stderr b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.stderr new file mode 100644 index 00000000000..8aa1f495639 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.stderr @@ -0,0 +1,10 @@ +error: unconstrained opaque type + --> $DIR/cross_inference_pattern_bug_no_type.rs:10:14 + | +LL | type T = impl Copy; // error: unconstrained opaque type + | ^^^^^^^^^ + | + = note: `T` must be used in combination with a concrete type within the same module + +error: aborting due to previous error + diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_rpit.rs b/src/test/ui/type-alias-impl-trait/cross_inference_rpit.rs new file mode 100644 index 00000000000..f6affbf1759 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/cross_inference_rpit.rs @@ -0,0 +1,14 @@ +// check-pass + +fn foo(b: bool) -> impl Copy { + if b { + return (5,6) + } + let x: (_, _) = foo(true); + println!("{:?}", x); + (1u32, 2u32) +} + +fn main() { + foo(false); +} diff --git a/src/test/ui/type-alias-impl-trait/issue-53398-cyclic-types.rs b/src/test/ui/type-alias-impl-trait/issue-53398-cyclic-types.rs new file mode 100644 index 00000000000..6c838f41003 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-53398-cyclic-types.rs @@ -0,0 +1,9 @@ +#![feature(type_alias_impl_trait)] + +type Foo = impl Fn() -> Foo; + +fn foo() -> Foo { + foo //~ ERROR: overflow evaluating the requirement `fn() -> Foo {foo}: Sized` +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-53398-cyclic-types.stderr b/src/test/ui/type-alias-impl-trait/issue-53398-cyclic-types.stderr new file mode 100644 index 00000000000..a9c2c18630c --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-53398-cyclic-types.stderr @@ -0,0 +1,11 @@ +error[E0275]: overflow evaluating the requirement `fn() -> Foo {foo}: Sized` + --> $DIR/issue-53398-cyclic-types.rs:6:5 + | +LL | foo + | ^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_53398_cyclic_types`) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/src/test/ui/type-alias-impl-trait/issue-58662-generator-with-lifetime.rs b/src/test/ui/type-alias-impl-trait/issue-58662-generator-with-lifetime.rs new file mode 100644 index 00000000000..f20ddf02071 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-58662-generator-with-lifetime.rs @@ -0,0 +1,39 @@ +// check-pass + +#![feature(generators, generator_trait)] +#![feature(type_alias_impl_trait)] + +use std::ops::{Generator, GeneratorState}; +use std::pin::Pin; + +type RandGenerator<'a> = impl Generator<Return = (), Yield = u64> + 'a; +fn rand_generator<'a>(rng: &'a ()) -> RandGenerator<'a> { + move || { + let _rng = rng; + loop { + yield 0; + } + } +} + +pub type RandGeneratorWithIndirection<'a> = impl Generator<Return = (), Yield = u64> + 'a; +pub fn rand_generator_with_indirection<'a>(rng: &'a ()) -> RandGeneratorWithIndirection<'a> { + fn helper<'b>(rng: &'b ()) -> impl 'b + Generator<Return = (), Yield = u64> { + move || { + let _rng = rng; + loop { + yield 0; + } + } + } + + helper(rng) +} + +fn main() { + let mut gen = rand_generator(&()); + match unsafe { Pin::new_unchecked(&mut gen) }.resume(()) { + GeneratorState::Yielded(_) => {} + GeneratorState::Complete(_) => {} + }; +} diff --git a/src/test/ui/type-alias-impl-trait/issue-89952.rs b/src/test/ui/type-alias-impl-trait/issue-89952.rs new file mode 100644 index 00000000000..dc0f19c042a --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-89952.rs @@ -0,0 +1,31 @@ +// check-pass + +#![feature(type_alias_impl_trait)] + +trait SomeTrait {} +impl SomeTrait for () {} + +trait MyFuture { + type Output; +} +impl<T> MyFuture for T { + type Output = T; +} + +trait ReturnsFuture { + type Output: SomeTrait; + type Future: MyFuture<Output = Result<Self::Output, ()>>; + fn func() -> Self::Future; +} + +struct Foo; + +impl ReturnsFuture for Foo { + type Output = impl SomeTrait; + type Future = impl MyFuture<Output = Result<Self::Output, ()>>; + fn func() -> Self::Future { + Result::<(), ()>::Err(()) + } +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-94429.rs b/src/test/ui/type-alias-impl-trait/issue-94429.rs new file mode 100644 index 00000000000..51d69c1271e --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-94429.rs @@ -0,0 +1,22 @@ +#![feature(type_alias_impl_trait, generator_trait, generators)] +use std::ops::Generator; + +trait Runnable { + type Gen: Generator<Yield = (), Return = ()>; + + fn run(&mut self) -> Self::Gen; +} + +struct Implementor {} + +impl Runnable for Implementor { + type Gen = impl Generator<Yield = (), Return = ()>; + + fn run(&mut self) -> Self::Gen { + move || { //~ ERROR: type mismatch resolving + yield 1; + } + } +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-94429.stderr b/src/test/ui/type-alias-impl-trait/issue-94429.stderr new file mode 100644 index 00000000000..4546f82b83b --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-94429.stderr @@ -0,0 +1,11 @@ +error[E0271]: type mismatch resolving `<[generator@$DIR/issue-94429.rs:16:9: 18:10] as Generator>::Yield == ()` + --> $DIR/issue-94429.rs:16:9 + | +LL | / move || { +LL | | yield 1; +LL | | } + | |_________^ expected integer, found `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/typeck/issue-96738.rs b/src/test/ui/typeck/issue-96738.rs new file mode 100644 index 00000000000..7f1d1428eb9 --- /dev/null +++ b/src/test/ui/typeck/issue-96738.rs @@ -0,0 +1,3 @@ +fn main() { + Some.nonexistent_method(); //~ ERROR: no method named `nonexistent_method` found +} diff --git a/src/test/ui/typeck/issue-96738.stderr b/src/test/ui/typeck/issue-96738.stderr new file mode 100644 index 00000000000..58c83a36a3b --- /dev/null +++ b/src/test/ui/typeck/issue-96738.stderr @@ -0,0 +1,16 @@ +error[E0599]: no method named `nonexistent_method` found for fn item `fn(_) -> Option<_> {Option::<_>::Some}` in the current scope + --> $DIR/issue-96738.rs:2:10 + | +LL | Some.nonexistent_method(); + | ---- ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}` + | | + | this is the constructor of an enum variant + | +help: call the constructor + | +LL | (Some)(_).nonexistent_method(); + | + ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/typeck/remove-extra-argument.fixed b/src/test/ui/typeck/remove-extra-argument.fixed new file mode 100644 index 00000000000..a9338c76cdc --- /dev/null +++ b/src/test/ui/typeck/remove-extra-argument.fixed @@ -0,0 +1,9 @@ +// run-rustfix +// Check that the HELP suggestion is `l(vec![])` instead of `l($crate::vec::Vec::new())` +fn l(_a: Vec<u8>) {} + +fn main() { + l(vec![]) + //~^ ERROR this function takes 1 argument but 2 arguments were supplied + //~| HELP remove the extra argument +} diff --git a/src/test/ui/typeck/remove-extra-argument.rs b/src/test/ui/typeck/remove-extra-argument.rs new file mode 100644 index 00000000000..659cb8b267f --- /dev/null +++ b/src/test/ui/typeck/remove-extra-argument.rs @@ -0,0 +1,9 @@ +// run-rustfix +// Check that the HELP suggestion is `l(vec![])` instead of `l($crate::vec::Vec::new())` +fn l(_a: Vec<u8>) {} + +fn main() { + l(vec![], vec![]) + //~^ ERROR this function takes 1 argument but 2 arguments were supplied + //~| HELP remove the extra argument +} diff --git a/src/test/ui/typeck/remove-extra-argument.stderr b/src/test/ui/typeck/remove-extra-argument.stderr new file mode 100644 index 00000000000..815297765c1 --- /dev/null +++ b/src/test/ui/typeck/remove-extra-argument.stderr @@ -0,0 +1,19 @@ +error[E0061]: this function takes 1 argument but 2 arguments were supplied + --> $DIR/remove-extra-argument.rs:6:5 + | +LL | l(vec![], vec![]) + | ^ ------ argument unexpected + | +note: function defined here + --> $DIR/remove-extra-argument.rs:3:4 + | +LL | fn l(_a: Vec<u8>) {} + | ^ ----------- +help: remove the extra argument + | +LL | l(vec![]) + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0061`. diff --git a/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr b/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr index ea1ca380b1c..3241c9f8521 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr @@ -6,6 +6,11 @@ LL | let z = f(1_usize, 2); | | | arguments to this function are incorrect | +note: closure defined here + --> $DIR/unboxed-closures-type-mismatch.rs:4:17 + | +LL | let mut f = |x: isize, y: isize| -> isize { x + y }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: change the type of the numeric literal from `usize` to `isize` | LL | let z = f(1_isize, 2); diff --git a/src/test/ui/unsized/box-instead-of-dyn-fn.stderr b/src/test/ui/unsized/box-instead-of-dyn-fn.stderr index 80f61cb3eae..b9d51d21e9a 100644 --- a/src/test/ui/unsized/box-instead-of-dyn-fn.stderr +++ b/src/test/ui/unsized/box-instead-of-dyn-fn.stderr @@ -3,7 +3,10 @@ error[E0308]: `if` and `else` have incompatible types | LL | / if a % 2 == 0 { LL | | move || println!("{a}") - | | ----------------------- expected because of this + | | ----------------------- + | | | + | | the expected closure + | | expected because of this LL | | } else { LL | | Box::new(move || println!("{}", b)) | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found struct `Box` @@ -11,8 +14,8 @@ LL | | LL | | } | |_____- `if` and `else` have incompatible types | - = note: expected type `[closure@$DIR/box-instead-of-dyn-fn.rs:8:9: 8:32]` - found struct `Box<[closure@$DIR/box-instead-of-dyn-fn.rs:10:18: 10:43]>` + = note: expected closure `[closure@$DIR/box-instead-of-dyn-fn.rs:8:9: 8:32]` + found struct `Box<[closure@$DIR/box-instead-of-dyn-fn.rs:10:18: 10:43]>` error[E0746]: return type cannot have an unboxed trait object --> $DIR/box-instead-of-dyn-fn.rs:5:56 diff --git a/src/test/ui/wf/wf-unsafe-trait-obj-match.stderr b/src/test/ui/wf/wf-unsafe-trait-obj-match.stderr index 9dbd41ca368..3f297d222dc 100644 --- a/src/test/ui/wf/wf-unsafe-trait-obj-match.stderr +++ b/src/test/ui/wf/wf-unsafe-trait-obj-match.stderr @@ -9,8 +9,8 @@ LL | | None => &R, LL | | } | |_____- `match` arms have incompatible types | - = note: expected type `&S` - found reference `&R` + = note: expected reference `&S` + found reference `&R` error[E0038]: the trait `Trait` cannot be made into an object --> $DIR/wf-unsafe-trait-obj-match.rs:26:21 diff --git a/src/tools/cargo b/src/tools/cargo -Subproject f63f23ff1f1a12ede8585bbd1bbf0c536e50293 +Subproject a44758ac805600edbb6ba51e7e6fb81a6077c0c diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.yml index d610e8c7bc4..89884bfc859 100644 --- a/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.yml +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/blank_issue.yml @@ -9,7 +9,7 @@ body: attributes: label: Description description: > - Please provide a discription of the issue, along with any information + Please provide a description of the issue, along with any information you feel relevant to replicate it. validations: required: true diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.yml index 9357ccc4f4e..25e436d30b9 100644 --- a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.yml +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_negative.yml @@ -23,7 +23,7 @@ body: id: reproducer attributes: label: Reproducer - description: Please provide the code and steps to repoduce the bug + description: Please provide the code and steps to reproduce the bug value: | I tried this code: diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.yml index b7dd400ee73..561b65c93a7 100644 --- a/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.yml +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/false_positive.yml @@ -24,7 +24,7 @@ body: attributes: label: Reproducer description: > - Please provide the code and steps to repoduce the bug together with the + Please provide the code and steps to reproduce the bug together with the output from Clippy. value: | I tried this code: diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index cd83bc9642b..0e27cc927ac 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -6,14 +6,14 @@ on: branches-ignore: - auto - try - # Don't run Clippy tests, when only textfiles were modified + # Don't run Clippy tests, when only text files were modified paths-ignore: - 'COPYRIGHT' - 'LICENSE-*' - '**.md' - '**.txt' pull_request: - # Don't run Clippy tests, when only textfiles were modified + # Don't run Clippy tests, when only text files were modified paths-ignore: - 'COPYRIGHT' - 'LICENSE-*' @@ -37,7 +37,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index f571485e6d3..9b3fd3ddfeb 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -25,7 +25,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 with: ref: ${{ github.ref }} @@ -88,7 +88,7 @@ jobs: if: matrix.host == 'i686-unknown-linux-gnu' - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain @@ -154,7 +154,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain @@ -212,7 +212,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index 5dfab1d2ebc..22051093c9c 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -23,7 +23,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 # Run - name: Build diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml index b8be730be32..71d71d10359 100644 --- a/src/tools/clippy/.github/workflows/deploy.yml +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -21,10 +21,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index 56c00544c93..a179bfa7261 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -16,7 +16,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Setup Node.js uses: actions/setup-node@v1.4.4 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index b4097ea86a5..d25ad0ac6fa 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,7 +6,108 @@ document. ## Unreleased / In Rust Nightly -[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master) +[d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master) + +## Rust 1.61 (beta) + +Current beta, released 2022-05-19 + +[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481) + +### New Lints + +* [`only_used_in_recursion`] + [#8422](https://github.com/rust-lang/rust-clippy/pull/8422) +* [`cast_enum_truncation`] + [#8381](https://github.com/rust-lang/rust-clippy/pull/8381) +* [`missing_spin_loop`] + [#8174](https://github.com/rust-lang/rust-clippy/pull/8174) +* [`deref_by_slicing`] + [#8218](https://github.com/rust-lang/rust-clippy/pull/8218) +* [`needless_match`] + [#8471](https://github.com/rust-lang/rust-clippy/pull/8471) +* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`) + [#8504](https://github.com/rust-lang/rust-clippy/pull/8504) +* [`print_in_format_impl`] + [#8253](https://github.com/rust-lang/rust-clippy/pull/8253) +* [`unnecessary_find_map`] + [#8489](https://github.com/rust-lang/rust-clippy/pull/8489) +* [`or_then_unwrap`] + [#8561](https://github.com/rust-lang/rust-clippy/pull/8561) +* [`unnecessary_join`] + [#8579](https://github.com/rust-lang/rust-clippy/pull/8579) +* [`iter_with_drain`] + [#8483](https://github.com/rust-lang/rust-clippy/pull/8483) +* [`cast_enum_constructor`] + [#8562](https://github.com/rust-lang/rust-clippy/pull/8562) +* [`cast_slice_different_sizes`] + [#8445](https://github.com/rust-lang/rust-clippy/pull/8445) + +### Moves and Deprecations + +* Moved [`transmute_undefined_repr`] to `nursery` (now allow-by-default) + [#8432](https://github.com/rust-lang/rust-clippy/pull/8432) +* Moved [`try_err`] to `restriction` + [#8544](https://github.com/rust-lang/rust-clippy/pull/8544) +* Move [`iter_with_drain`] to `nursery` + [#8541](https://github.com/rust-lang/rust-clippy/pull/8541) +* Renamed `to_string_in_display` to [`recursive_format_impl`] + [#8188](https://github.com/rust-lang/rust-clippy/pull/8188) + +### Enhancements + +* [`dbg_macro`]: The lint level can now be set with crate attributes and works inside macros + [#8411](https://github.com/rust-lang/rust-clippy/pull/8411) +* [`ptr_as_ptr`]: Now works inside macros + [#8442](https://github.com/rust-lang/rust-clippy/pull/8442) +* [`use_self`]: Now works for variants in match expressions + [#8456](https://github.com/rust-lang/rust-clippy/pull/8456) +* [`await_holding_lock`]: Now lints for `parking_lot::{Mutex, RwLock}` + [#8419](https://github.com/rust-lang/rust-clippy/pull/8419) +* [`recursive_format_impl`]: Now checks for format calls on `self` + [#8188](https://github.com/rust-lang/rust-clippy/pull/8188) + +### False Positive Fixes + +* [`new_without_default`]: No longer lints for `new()` methods with `#[doc(hidden)]` + [#8472](https://github.com/rust-lang/rust-clippy/pull/8472) +* [`transmute_undefined_repr`]: No longer lints for single field structs with `#[repr(C)]`, + generic parameters, wide pointers, unions, tuples and allow several forms of type erasure + [#8425](https://github.com/rust-lang/rust-clippy/pull/8425) + [#8553](https://github.com/rust-lang/rust-clippy/pull/8553) + [#8440](https://github.com/rust-lang/rust-clippy/pull/8440) + [#8547](https://github.com/rust-lang/rust-clippy/pull/8547) +* [`match_single_binding`], [`match_same_arms`], [`match_as_ref`], [`match_bool`]: No longer + lint `match` expressions with `cfg`ed arms + [#8443](https://github.com/rust-lang/rust-clippy/pull/8443) +* [`single_component_path_imports`]: No longer lint on macros + [#8537](https://github.com/rust-lang/rust-clippy/pull/8537) +* [`ptr_arg`]: Allow `&mut` arguments for `Cow<_>` + [#8552](https://github.com/rust-lang/rust-clippy/pull/8552) +* [`needless_borrow`]: No longer lints for method calls + [#8441](https://github.com/rust-lang/rust-clippy/pull/8441) +* [`match_same_arms`]: Now ensures that interposing arm patterns don't overlap + [#8232](https://github.com/rust-lang/rust-clippy/pull/8232) +* [`default_trait_access`]: Now allows `Default::default` in update expressions + [#8433](https://github.com/rust-lang/rust-clippy/pull/8433) + +### Suggestion Fixes/Improvements + +* [`redundant_slicing`]: Fixed suggestion for a method calls + [#8218](https://github.com/rust-lang/rust-clippy/pull/8218) +* [`map_flatten`]: Long suggestions will now be split up into two help messages + [#8520](https://github.com/rust-lang/rust-clippy/pull/8520) +* [`unnecessary_lazy_evaluations`]: Now shows suggestions for longer code snippets + [#8543](https://github.com/rust-lang/rust-clippy/pull/8543) +* [`unnecessary_sort_by`]: Now suggests `Reverse` including the path + [#8462](https://github.com/rust-lang/rust-clippy/pull/8462) +* [`search_is_some`]: More suggestions are now `MachineApplicable` + [#8536](https://github.com/rust-lang/rust-clippy/pull/8536) + +### Documentation Improvements + +* [`new_without_default`]: Document `pub` requirement for the struct and fields + [#8429](https://github.com/rust-lang/rust-clippy/pull/8429) ## Rust 1.60 @@ -3182,6 +3283,7 @@ Released 2018-09-13 [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops [`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async +[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask @@ -3198,6 +3300,7 @@ Released 2018-09-13 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons @@ -3262,6 +3365,7 @@ Released 2018-09-13 [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else +[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop @@ -3314,6 +3418,7 @@ Released 2018-09-13 [`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args +[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string [`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 [`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10 @@ -3356,6 +3461,7 @@ Released 2018-09-13 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters +[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count @@ -3372,6 +3478,7 @@ Released 2018-09-13 [`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays [`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups [`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant +[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty @@ -3472,6 +3579,7 @@ Released 2018-09-13 [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref +[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop @@ -3527,6 +3635,7 @@ Released 2018-09-13 [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names +[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use [`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark [`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one [`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one @@ -3587,6 +3696,7 @@ Released 2018-09-13 [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive +[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign @@ -3627,6 +3737,7 @@ Released 2018-09-13 [`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr [`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts [`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null +[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace [`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex [`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err @@ -3650,6 +3761,7 @@ Released 2018-09-13 [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation +[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index 81faa5fe5e1..2cfbcea5034 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.1" edition = "2021" [dependencies] +aho-corasick = "0.7" clap = "2.33" indoc = "1.0" itertools = "0.10.1" diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 9c6d754b400..81e807cf10c 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(let_chains)] #![feature(let_else)] #![feature(once_cell)] #![feature(rustc_private)] diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index b1fe35a0243..ebf8f38d490 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -19,9 +19,9 @@ fn main() { if matches.is_present("print-only") { update_lints::print_lints(); } else if matches.is_present("check") { - update_lints::run(update_lints::UpdateMode::Check); + update_lints::update(update_lints::UpdateMode::Check); } else { - update_lints::run(update_lints::UpdateMode::Change); + update_lints::update(update_lints::UpdateMode::Change); } }, ("new_lint", Some(matches)) => { @@ -31,18 +31,36 @@ fn main() { matches.value_of("category"), matches.is_present("msrv"), ) { - Ok(_) => update_lints::run(update_lints::UpdateMode::Change), + Ok(_) => update_lints::update(update_lints::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {}", e), } }, ("setup", Some(sub_command)) => match sub_command.subcommand() { - ("intellij", Some(matches)) => setup::intellij::setup_rustc_src( - matches - .value_of("rustc-repo-path") - .expect("this field is mandatory and therefore always valid"), - ), - ("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")), - ("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")), + ("intellij", Some(matches)) => { + if matches.is_present("remove") { + setup::intellij::remove_rustc_src(); + } else { + setup::intellij::setup_rustc_src( + matches + .value_of("rustc-repo-path") + .expect("this field is mandatory and therefore always valid"), + ); + } + }, + ("git-hook", Some(matches)) => { + if matches.is_present("remove") { + setup::git_hook::remove_hook(); + } else { + setup::git_hook::install_hook(matches.is_present("force-override")); + } + }, + ("vscode-tasks", Some(matches)) => { + if matches.is_present("remove") { + setup::vscode::remove_tasks(); + } else { + setup::vscode::install_tasks(matches.is_present("force-override")); + } + }, _ => {}, }, ("remove", Some(sub_command)) => match sub_command.subcommand() { @@ -60,6 +78,12 @@ fn main() { let path = matches.value_of("path").unwrap(); lint::run(path); }, + ("rename_lint", Some(matches)) => { + let old_name = matches.value_of("old_name").unwrap(); + let new_name = matches.value_of("new_name").unwrap_or(old_name); + let uplift = matches.is_present("uplift"); + update_lints::rename(old_name, new_name, uplift); + }, _ => {}, } } @@ -168,12 +192,19 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { SubCommand::with_name("intellij") .about("Alter dependencies so Intellij Rust can find rustc internals") .arg( + Arg::with_name("remove") + .long("remove") + .help("Remove the dependencies added with 'cargo dev setup intellij'") + .required(false), + ) + .arg( Arg::with_name("rustc-repo-path") .long("repo-path") .short("r") .help("The path to a rustc repo that will be used for setting the dependencies") .takes_value(true) .value_name("path") + .conflicts_with("remove") .required(true), ), ) @@ -181,6 +212,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { SubCommand::with_name("git-hook") .about("Add a pre-commit git hook that formats your code to make it look pretty") .arg( + Arg::with_name("remove") + .long("remove") + .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") + .required(false), + ) + .arg( Arg::with_name("force-override") .long("force-override") .short("f") @@ -192,6 +229,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { SubCommand::with_name("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") .arg( + Arg::with_name("remove") + .long("remove") + .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") + .required(false), + ) + .arg( Arg::with_name("force-override") .long("force-override") .short("f") @@ -242,5 +285,26 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .help("The path to a file or package directory to lint"), ), ) + .subcommand( + SubCommand::with_name("rename_lint") + .about("Renames the given lint") + .arg( + Arg::with_name("old_name") + .index(1) + .required(true) + .help("The name of the lint to rename"), + ) + .arg( + Arg::with_name("new_name") + .index(2) + .required_unless("uplift") + .help("The new name of the lint"), + ) + .arg( + Arg::with_name("uplift") + .long("uplift") + .help("This lint will be uplifted into rustc"), + ), + ) .get_matches() } diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 7a3fd131761..10f67d301f8 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -1,5 +1,6 @@ use crate::clippy_project_root; use indoc::indoc; +use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::io::{self, ErrorKind}; @@ -232,7 +233,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { ) }); - result.push_str(&format!( + let _ = write!( + result, indoc! {r#" declare_clippy_lint! {{ /// ### What it does @@ -256,7 +258,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { version = version, name_upper = name_upper, category = category, - )); + ); result.push_str(&if enable_msrv { format!( diff --git a/src/tools/clippy/clippy_dev/src/setup/mod.rs b/src/tools/clippy/clippy_dev/src/setup/mod.rs index a1e4dd103b8..f691ae4fa45 100644 --- a/src/tools/clippy/clippy_dev/src/setup/mod.rs +++ b/src/tools/clippy/clippy_dev/src/setup/mod.rs @@ -7,7 +7,7 @@ use std::path::Path; const CLIPPY_DEV_DIR: &str = "clippy_dev"; /// This function verifies that the tool is being executed in the clippy directory. -/// This is useful to ensure that setups only modify Clippys resources. The verification +/// This is useful to ensure that setups only modify Clippy's resources. The verification /// is done by checking that `clippy_dev` is a sub directory of the current directory. /// /// It will print an error message and return `false` if the directory could not be @@ -17,7 +17,7 @@ fn verify_inside_clippy_dir() -> bool { if path.exists() && path.is_dir() { true } else { - eprintln!("error: unable to verify that the working directory is clippys directory"); + eprintln!("error: unable to verify that the working directory is clippy's directory"); false } } diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 59db51fbfac..1a6a4336da2 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -1,11 +1,13 @@ -use core::fmt::Write; +use aho_corasick::AhoCorasickBuilder; +use core::fmt::Write as _; use itertools::Itertools; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fs; -use std::path::Path; -use walkdir::WalkDir; +use std::io::{self, Read as _, Seek as _, Write as _}; +use std::path::{Path, PathBuf}; +use walkdir::{DirEntry, WalkDir}; use crate::clippy_project_root; @@ -30,12 +32,19 @@ pub enum UpdateMode { /// # Panics /// /// Panics if a file path could not read from or then written to -#[allow(clippy::too_many_lines)] -pub fn run(update_mode: UpdateMode) { - let (lints, deprecated_lints) = gather_all(); +pub fn update(update_mode: UpdateMode) { + let (lints, deprecated_lints, renamed_lints) = gather_all(); + generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); +} - let internal_lints = Lint::internal_lints(&lints); - let usable_lints = Lint::usable_lints(&lints); +fn generate_lint_files( + update_mode: UpdateMode, + lints: &[Lint], + deprecated_lints: &[DeprecatedLint], + renamed_lints: &[RenamedLint], +) { + let internal_lints = Lint::internal_lints(lints); + let usable_lints = Lint::usable_lints(lints); let mut sorted_usable_lints = usable_lints.clone(); sorted_usable_lints.sort_by_key(|lint| lint.name.clone()); @@ -87,7 +96,7 @@ pub fn run(update_mode: UpdateMode) { process_file( "clippy_lints/src/lib.deprecated.rs", update_mode, - &gen_deprecated(&deprecated_lints), + &gen_deprecated(deprecated_lints), ); let all_group_lints = usable_lints.iter().filter(|l| { @@ -107,10 +116,16 @@ pub fn run(update_mode: UpdateMode) { &content, ); } + + let content = gen_deprecated_lints_test(deprecated_lints); + process_file("tests/ui/deprecated.rs", update_mode, &content); + + let content = gen_renamed_lints_test(renamed_lints); + process_file("tests/ui/rename.rs", update_mode, &content); } pub fn print_lints() { - let (lint_list, _) = gather_all(); + let (lint_list, _, _) = gather_all(); let usable_lints = Lint::usable_lints(&lint_list); let usable_lint_count = usable_lints.len(); let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); @@ -128,6 +143,209 @@ pub fn print_lints() { println!("there are {} lints", usable_lint_count); } +/// Runs the `rename_lint` command. +/// +/// This does the following: +/// * Adds an entry to `renamed_lints.rs`. +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). +/// * Renames the lint struct to the new name. +/// * Renames the module containing the lint struct to the new name if it shares a name with the +/// lint. +/// +/// # Panics +/// Panics for the following conditions: +/// * If a file path could not read from or then written to +/// * If either lint name has a prefix +/// * If `old_name` doesn't name an existing lint. +/// * If `old_name` names a deprecated or renamed lint. +#[allow(clippy::too_many_lines)] +pub fn rename(old_name: &str, new_name: &str, uplift: bool) { + if let Some((prefix, _)) = old_name.split_once("::") { + panic!("`{}` should not contain the `{}` prefix", old_name, prefix); + } + if let Some((prefix, _)) = new_name.split_once("::") { + panic!("`{}` should not contain the `{}` prefix", new_name, prefix); + } + + let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); + let mut old_lint_index = None; + let mut found_new_name = false; + for (i, lint) in lints.iter().enumerate() { + if lint.name == old_name { + old_lint_index = Some(i); + } else if lint.name == new_name { + found_new_name = true; + } + } + let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name)); + + let lint = RenamedLint { + old_name: format!("clippy::{}", old_name), + new_name: if uplift { + new_name.into() + } else { + format!("clippy::{}", new_name) + }, + }; + + // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in + // case. + assert!( + !renamed_lints.iter().any(|l| lint.old_name == l.old_name), + "`{}` has already been renamed", + old_name + ); + assert!( + !deprecated_lints.iter().any(|l| lint.old_name == l.name), + "`{}` has already been deprecated", + old_name + ); + + // Update all lint level attributes. (`clippy::lint_name`) + for file in WalkDir::new(clippy_project_root()) + .into_iter() + .map(Result::unwrap) + .filter(|f| { + let name = f.path().file_name(); + let ext = f.path().extension(); + (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) + && name != Some(OsStr::new("rename.rs")) + && name != Some(OsStr::new("renamed_lints.rs")) + }) + { + rewrite_file(file.path(), |s| { + replace_ident_like(s, &[(&lint.old_name, &lint.new_name)]) + }); + } + + renamed_lints.push(lint); + renamed_lints.sort_by(|lhs, rhs| { + lhs.new_name + .starts_with("clippy::") + .cmp(&rhs.new_name.starts_with("clippy::")) + .reverse() + .then_with(|| lhs.old_name.cmp(&rhs.old_name)) + }); + + write_file( + Path::new("clippy_lints/src/renamed_lints.rs"), + &gen_renamed_lints_list(&renamed_lints), + ); + + if uplift { + write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); + println!( + "`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.", + old_name + ); + } else if found_new_name { + write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); + println!( + "`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.", + new_name + ); + } else { + // Rename the lint struct and source files sharing a name with the lint. + let lint = &mut lints[old_lint_index]; + let old_name_upper = old_name.to_uppercase(); + let new_name_upper = new_name.to_uppercase(); + lint.name = new_name.into(); + + // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. + if try_rename_file( + Path::new(&format!("tests/ui/{}.rs", old_name)), + Path::new(&format!("tests/ui/{}.rs", new_name)), + ) { + try_rename_file( + Path::new(&format!("tests/ui/{}.stderr", old_name)), + Path::new(&format!("tests/ui/{}.stderr", new_name)), + ); + try_rename_file( + Path::new(&format!("tests/ui/{}.fixed", old_name)), + Path::new(&format!("tests/ui/{}.fixed", new_name)), + ); + } + + // Try to rename the file containing the lint if the file name matches the lint's name. + let replacements; + let replacements = if lint.module == old_name + && try_rename_file( + Path::new(&format!("clippy_lints/src/{}.rs", old_name)), + Path::new(&format!("clippy_lints/src/{}.rs", new_name)), + ) { + // Edit the module name in the lint list. Note there could be multiple lints. + for lint in lints.iter_mut().filter(|l| l.module == old_name) { + lint.module = new_name.into(); + } + replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; + replacements.as_slice() + } else if !lint.module.contains("::") + // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` + && try_rename_file( + Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)), + Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)), + ) + { + // Edit the module name in the lint list. Note there could be multiple lints, or none. + let renamed_mod = format!("{}::{}", lint.module, old_name); + for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { + lint.module = format!("{}::{}", lint.module, new_name); + } + replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; + replacements.as_slice() + } else { + replacements = [(&*old_name_upper, &*new_name_upper), ("", "")]; + &replacements[0..1] + }; + + // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being + // renamed. + for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) { + rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); + } + + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("{} has been successfully renamed", old_name); + } + + println!("note: `cargo uitest` still needs to be run to update the test results"); +} + +/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there +/// were no replacements. +fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> { + fn is_ident_char(c: u8) -> bool { + matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') + } + + let searcher = AhoCorasickBuilder::new() + .dfa(true) + .match_kind(aho_corasick::MatchKind::LeftmostLongest) + .build_with_size::<u16, _, _>(replacements.iter().map(|&(x, _)| x.as_bytes())) + .unwrap(); + + let mut result = String::with_capacity(contents.len() + 1024); + let mut pos = 0; + let mut edited = false; + for m in searcher.find_iter(contents) { + let (old, new) = replacements[m.pattern()]; + result.push_str(&contents[pos..m.start()]); + result.push_str( + if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) + && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0)) + { + edited = true; + new + } else { + old + }, + ); + pos = m.end(); + } + result.push_str(&contents[pos..]); + edited.then(|| result) +} + fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } @@ -210,6 +428,19 @@ impl DeprecatedLint { } } +struct RenamedLint { + old_name: String, + new_name: String, +} +impl RenamedLint { + fn new(old_name: &str, new_name: &str) -> Self { + Self { + old_name: remove_line_splices(old_name), + new_name: remove_line_splices(new_name), + } + } +} + /// Generates the code for registering a group fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String { let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect(); @@ -217,12 +448,13 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lin let mut output = GENERATED_FILE_COMMENT.to_string(); - output.push_str(&format!( - "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![\n", + let _ = writeln!( + output, + "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![", group_name - )); + ); for (module, name) in details { - output.push_str(&format!(" LintId::of({}::{}),\n", module, name)); + let _ = writeln!(output, " LintId::of({}::{}),", module, name); } output.push_str("])\n"); @@ -235,7 +467,8 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String { let mut output = GENERATED_FILE_COMMENT.to_string(); output.push_str("{\n"); for lint in lints { - output.push_str(&format!( + let _ = write!( + output, concat!( " store.register_removed(\n", " \"clippy::{}\",\n", @@ -243,7 +476,7 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String { " );\n" ), lint.name, lint.reason, - )); + ); } output.push_str("}\n"); @@ -269,25 +502,62 @@ fn gen_register_lint_list<'a>( if !is_public { output.push_str(" #[cfg(feature = \"internal\")]\n"); } - output.push_str(&format!(" {}::{},\n", module_name, lint_name)); + let _ = writeln!(output, " {}::{},", module_name, lint_name); } output.push_str("])\n"); output } +fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { + let mut res: String = GENERATED_FILE_COMMENT.into(); + for lint in lints { + writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap(); + } + res.push_str("\nfn main() {}\n"); + res +} + +fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { + let mut seen_lints = HashSet::new(); + let mut res: String = GENERATED_FILE_COMMENT.into(); + res.push_str("// run-rustfix\n\n"); + for lint in lints { + if seen_lints.insert(&lint.new_name) { + writeln!(res, "#![allow({})]", lint.new_name).unwrap(); + } + } + seen_lints.clear(); + for lint in lints { + if seen_lints.insert(&lint.old_name) { + writeln!(res, "#![warn({})]", lint.old_name).unwrap(); + } + } + res.push_str("\nfn main() {}\n"); + res +} + +fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String { + const HEADER: &str = "\ + // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\ + #[rustfmt::skip]\n\ + pub static RENAMED_LINTS: &[(&str, &str)] = &[\n"; + + let mut res = String::from(HEADER); + for lint in lints { + writeln!(res, " (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap(); + } + res.push_str("];\n"); + res +} + /// Gathers all lints defined in `clippy_lints/src` -fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>) { +fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) { let mut lints = Vec::with_capacity(1000); let mut deprecated_lints = Vec::with_capacity(50); - let root_path = clippy_project_root().join("clippy_lints/src"); + let mut renamed_lints = Vec::with_capacity(50); - for (rel_path, file) in WalkDir::new(&root_path) - .into_iter() - .map(Result::unwrap) - .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) - .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) - { + for (rel_path, file) in clippy_lints_src_files() { let path = file.path(); let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); @@ -305,13 +575,21 @@ fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>) { module.strip_suffix(".rs").unwrap_or(&module) }; - if module == "deprecated_lints" { - parse_deprecated_contents(&contents, &mut deprecated_lints); - } else { - parse_contents(&contents, module, &mut lints); + match module { + "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints), + "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints), + _ => parse_contents(&contents, module, &mut lints), } } - (lints, deprecated_lints) + (lints, deprecated_lints, renamed_lints) +} + +fn clippy_lints_src_files() -> impl Iterator<Item = (PathBuf, DirEntry)> { + let root_path = clippy_project_root().join("clippy_lints/src"); + let iter = WalkDir::new(&root_path).into_iter(); + iter.map(Result::unwrap) + .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) + .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) } macro_rules! match_tokens { @@ -394,6 +672,25 @@ fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) { } } +fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) { + for line in contents.lines() { + let mut offset = 0usize; + let mut iter = tokenize(line).map(|t| { + let range = offset..offset + t.len; + offset = range.end; + (t.kind, &line[range]) + }); + let (old_name, new_name) = match_tokens!( + iter, + // ("old_name", + Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma + // "new_name"), + Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma + ); + lints.push(RenamedLint::new(old_name, new_name)); + } +} + /// Removes the line splices and surrounding quotes from a string literal fn remove_line_splices(s: &str) -> String { let s = s @@ -462,6 +759,52 @@ fn replace_region_in_text<'a>( Ok(res) } +fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { + match fs::OpenOptions::new().create_new(true).write(true).open(new_name) { + Ok(file) => drop(file), + Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, + Err(e) => panic_file(e, new_name, "create"), + }; + match fs::rename(old_name, new_name) { + Ok(()) => true, + Err(e) => { + drop(fs::remove_file(new_name)); + if e.kind() == io::ErrorKind::NotFound { + false + } else { + panic_file(e, old_name, "rename"); + } + }, + } +} + +#[allow(clippy::needless_pass_by_value)] +fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { + panic!("failed to {} file `{}`: {}", action, name.display(), error) +} + +fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) { + let mut file = fs::OpenOptions::new() + .write(true) + .read(true) + .open(path) + .unwrap_or_else(|e| panic_file(e, path, "open")); + let mut buf = String::new(); + file.read_to_string(&mut buf) + .unwrap_or_else(|e| panic_file(e, path, "read")); + if let Some(new_contents) = f(&buf) { + file.rewind().unwrap_or_else(|e| panic_file(e, path, "write")); + file.write_all(new_contents.as_bytes()) + .unwrap_or_else(|e| panic_file(e, path, "write")); + file.set_len(new_contents.len() as u64) + .unwrap_or_else(|e| panic_file(e, path, "write")); + } +} + +fn write_file(path: &Path, contents: &str) { + fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write")); +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs index d0b03c0a9c2..8b0e11cb802 100644 --- a/src/tools/clippy/clippy_lints/src/attrs.rs +++ b/src/tools/clippy/clippy_lints/src/attrs.rs @@ -6,7 +6,7 @@ use clippy_utils::msrvs; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; -use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; +use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MacArgs, MacArgsEq, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, @@ -335,9 +335,6 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } if let Some(lint_list) = &attr.meta_item_list() { if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) { - // permit `unused_imports`, `deprecated`, `unreachable_pub`, - // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items - // and `unused_imports` for `extern crate` items with `macro_use` for lint in lint_list { match item.kind { ItemKind::Use(..) => { @@ -345,10 +342,12 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { || is_word(lint, sym::deprecated) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) - || extract_clippy_lint(lint) - .map_or(false, |s| s.as_str() == "wildcard_imports") - || extract_clippy_lint(lint) - .map_or(false, |s| s.as_str() == "enum_glob_use") + || extract_clippy_lint(lint).map_or(false, |s| { + matches!( + s.as_str(), + "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate", + ) + }) { return; } @@ -594,6 +593,10 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It }; if attr.style == AttrStyle::Outer { + if let MacArgs::Eq(_, MacArgsEq::Ast(expr)) = &attr_item.args + && !matches!(expr.kind, rustc_ast::ExprKind::Lit(..)) { + return; + } if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { return; } diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs index 4592ca72748..5b7c4591504 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -1,12 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths}; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; -use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::GeneratorInteriorTypeCause; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; +use crate::utils::conf::DisallowedType; + declare_clippy_lint! { /// ### What it does /// Checks for calls to await while holding a non-async-aware MutexGuard. @@ -127,9 +130,75 @@ declare_clippy_lint! { "inside an async function, holding a `RefCell` ref while calling `await`" } -declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]); +declare_clippy_lint! { + /// ### What it does + /// Allows users to configure types which should not be held across `await` + /// suspension points. + /// + /// ### Why is this bad? + /// There are some types which are perfectly "safe" to be used concurrently + /// from a memory access perspective but will cause bugs at runtime if they + /// are held in such a way. + /// + /// ### Known problems + /// + /// ### Example + /// + /// ```toml + /// await-holding-invalid-types = [ + /// # You can specify a type name + /// "CustomLockType", + /// # You can (optionally) specify a reason + /// { path = "OtherCustomLockType", reason = "Relies on a thread local" } + /// ] + /// ``` + /// + /// ```rust + /// # async fn baz() {} + /// struct CustomLockType; + /// struct OtherCustomLockType; + /// async fn foo() { + /// let _x = CustomLockType; + /// let _y = OtherCustomLockType; + /// baz().await; // Lint violation + /// } + /// ``` + #[clippy::version = "1.49.0"] + pub AWAIT_HOLDING_INVALID_TYPE, + suspicious, + "holding a type across an await point which is not allowed to be held as per the configuration" +} + +impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); + +#[derive(Debug)] +pub struct AwaitHolding { + conf_invalid_types: Vec<DisallowedType>, + def_ids: FxHashMap<DefId, DisallowedType>, +} + +impl AwaitHolding { + pub(crate) fn new(conf_invalid_types: Vec<DisallowedType>) -> Self { + Self { + conf_invalid_types, + def_ids: FxHashMap::default(), + } + } +} impl LateLintPass<'_> for AwaitHolding { + fn check_crate(&mut self, cx: &LateContext<'_>) { + for conf in &self.conf_invalid_types { + let path = match conf { + DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path, + }; + let segs: Vec<_> = path.split("::").collect(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) { + self.def_ids.insert(id, conf.clone()); + } + } + } + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { use AsyncGeneratorKind::{Block, Closure, Fn}; if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { @@ -137,7 +206,7 @@ impl LateLintPass<'_> for AwaitHolding { hir_id: body.value.hir_id, }; let typeck_results = cx.tcx.typeck_body(body_id); - check_interior_types( + self.check_interior_types( cx, typeck_results.generator_interior_types.as_ref().skip_binder(), body.value.span, @@ -146,46 +215,68 @@ impl LateLintPass<'_> for AwaitHolding { } } -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { - if is_mutex_guard(cx, adt.did()) { - span_lint_and_then( - cx, - AWAIT_HOLDING_LOCK, - ty_cause.span, - "this `MutexGuard` is held across an `await` point", - |diag| { - diag.help( - "consider using an async-aware `Mutex` type or ensuring the \ +impl AwaitHolding { + fn check_interior_types(&self, cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_mutex_guard(cx, adt.did()) { + span_lint_and_then( + cx, + AWAIT_HOLDING_LOCK, + ty_cause.span, + "this `MutexGuard` is held across an `await` point", + |diag| { + diag.help( + "consider using an async-aware `Mutex` type or ensuring the \ `MutexGuard` is dropped before calling await", - ); - diag.span_note( - ty_cause.scope_span.unwrap_or(span), - "these are all the `await` points this lock is held through", - ); - }, - ); - } - if is_refcell_ref(cx, adt.did()) { - span_lint_and_then( - cx, - AWAIT_HOLDING_REFCELL_REF, - ty_cause.span, - "this `RefCell` reference is held across an `await` point", - |diag| { - diag.help("ensure the reference is dropped before calling `await`"); - diag.span_note( - ty_cause.scope_span.unwrap_or(span), - "these are all the `await` points this reference is held through", - ); - }, - ); + ); + diag.span_note( + ty_cause.scope_span.unwrap_or(span), + "these are all the `await` points this lock is held through", + ); + }, + ); + } else if is_refcell_ref(cx, adt.did()) { + span_lint_and_then( + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this `RefCell` reference is held across an `await` point", + |diag| { + diag.help("ensure the reference is dropped before calling `await`"); + diag.span_note( + ty_cause.scope_span.unwrap_or(span), + "these are all the `await` points this reference is held through", + ); + }, + ); + } else if let Some(disallowed) = self.def_ids.get(&adt.did()) { + emit_invalid_type(cx, ty_cause.span, disallowed); + } } } } } +fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) { + let (type_name, reason) = match disallowed { + DisallowedType::Simple(path) => (path, &None), + DisallowedType::WithReason { path, reason } => (path, reason), + }; + + span_lint_and_then( + cx, + AWAIT_HOLDING_INVALID_TYPE, + span, + &format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",), + |diag| { + if let Some(reason) = reason { + diag.note(reason.clone()); + } + }, + ); +} + fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { match_def_path(cx, def_id, &paths::MUTEX_GUARD) || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) diff --git a/src/tools/clippy/clippy_lints/src/bytes_count_to_len.rs b/src/tools/clippy/clippy_lints/src/bytes_count_to_len.rs new file mode 100644 index 00000000000..d70dbf5b239 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/bytes_count_to_len.rs @@ -0,0 +1,70 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// It checks for `str::bytes().count()` and suggests replacing it with + /// `str::len()`. + /// + /// ### Why is this bad? + /// `str::bytes().count()` is longer and may not be as performant as using + /// `str::len()`. + /// + /// ### Example + /// ```rust + /// "hello".bytes().count(); + /// String::from("hello").bytes().count(); + /// ``` + /// Use instead: + /// ```rust + /// "hello".len(); + /// String::from("hello").len(); + /// ``` + #[clippy::version = "1.62.0"] + pub BYTES_COUNT_TO_LEN, + complexity, + "Using `bytes().count()` when `len()` performs the same functionality" +} + +declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]); + +impl<'tcx> LateLintPass<'tcx> for BytesCountToLen { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind; + if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, expr_def_id, &paths::ITER_COUNT); + + if let [bytes_expr] = &**expr_args; + if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind; + if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id); + if match_def_path(cx, bytes_def_id, &paths::STR_BYTES); + + if let [str_expr] = &**bytes_args; + let ty = cx.typeck_results().expr_ty(str_expr).peel_refs(); + + if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BYTES_COUNT_TO_LEN, + expr.span, + "using long and hard to read `.bytes().count()`", + "consider calling `.len()` instead", + format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)), + applicability + ); + } + }; + } +} diff --git a/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index a8f9c189ade..7af200708ff 100644 --- a/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -42,8 +42,8 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: & if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind; if (2..=6).contains(&ext_literal.as_str().len()); if ext_literal.as_str().starts_with('.'); - if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_digit(10)) - || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_digit(10)); + if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) + || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()); then { let mut ty = ctx.typeck_results().expr_ty(obj); ty = match ty.kind() { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index 421bd6f53f7..64f87c80f8d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -29,21 +29,19 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)), ExprKind::Binary(op, left, right) => match op.node { BinOpKind::Div => { - apply_reductions(cx, nbits, left, signed) - - (if signed { - 0 // let's be conservative here - } else { - // by dividing by 1, we remove 0 bits, etc. - get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1)) - }) + apply_reductions(cx, nbits, left, signed).saturating_sub(if signed { + // let's be conservative here + 0 + } else { + // by dividing by 1, we remove 0 bits, etc. + get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1)) + }) }, BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right) .unwrap_or(u64::max_value()) .min(apply_reductions(cx, nbits, left, signed)), - BinOpKind::Shr => { - apply_reductions(cx, nbits, left, signed) - - constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high")) - }, + BinOpKind::Shr => apply_reductions(cx, nbits, left, signed) + .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))), _ => nbits, }, ExprKind::MethodCall(method, [left, right], _) => { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs index 3608c1654d5..2238668abca 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -1,4 +1,4 @@ -use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source::snippet_opt}; +use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source}; use if_chain::if_chain; use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, Node}; @@ -8,32 +8,7 @@ use rustc_semver::RustcVersion; use super::CAST_SLICE_DIFFERENT_SIZES; -fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let map = cx.tcx.hir(); - if_chain! { - if let Some(parent_id) = map.find_parent_node(expr.hir_id); - if let Some(parent) = map.find(parent_id); - then { - let expr = match parent { - Node::Block(block) => { - if let Some(parent_expr) = block.expr { - parent_expr - } else { - return false; - } - }, - Node::Expr(expr) => expr, - _ => return false, - }; - - matches!(expr.kind, ExprKind::Cast(..)) - } else { - false - } - } -} - -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Option<RustcVersion>) { // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) { return; @@ -45,8 +20,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVe return; } - if let Some((from_slice_ty, to_slice_ty)) = expr_cast_chain_tys(cx, expr) { - if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(from_slice_ty.ty), cx.layout_of(to_slice_ty.ty)) { + if let Some(CastChainInfo { + left_cast, + start_ty, + end_ty, + }) = expr_cast_chain_tys(cx, expr) + { + if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) { let from_size = from_layout.size.bytes(); let to_size = to_layout.size.bytes(); if from_size != to_size && from_size != 0 && to_size != 0 { @@ -56,21 +36,20 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVe expr.span, &format!( "casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count", - from_slice_ty, from_size, to_slice_ty, to_size, + start_ty.ty, from_size, end_ty.ty, to_size, ), |diag| { - let cast_expr = match expr.kind { - ExprKind::Cast(cast_expr, ..) => cast_expr, - _ => unreachable!("expr should be a cast as checked by expr_cast_chain_tys"), - }; - let ptr_snippet = snippet_opt(cx, cast_expr.span).unwrap(); + let ptr_snippet = source::snippet(cx, left_cast.span, ".."); - let (mutbl_fn_str, mutbl_ptr_str) = match to_slice_ty.mutbl { + let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl { Mutability::Mut => ("_mut", "mut"), Mutability::Not => ("", "const"), }; let sugg = format!( - "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {to_slice_ty}, ..)" + "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)", + // get just the ty from the TypeAndMut so that the printed type isn't something like `mut + // T`, extract just the `T` + end_ty.ty ); diag.span_suggestion( @@ -86,6 +65,31 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVe } } +fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let map = cx.tcx.hir(); + if_chain! { + if let Some(parent_id) = map.find_parent_node(expr.hir_id); + if let Some(parent) = map.find(parent_id); + then { + let expr = match parent { + Node::Block(block) => { + if let Some(parent_expr) = block.expr { + parent_expr + } else { + return false; + } + }, + Node::Expr(expr) => expr, + _ => return false, + }; + + matches!(expr.kind, ExprKind::Cast(..)) + } else { + false + } + } +} + /// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if /// the type is one of those slices fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> { @@ -98,18 +102,40 @@ fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> { } } -/// Returns the pair (original ptr T, final ptr U) if the expression is composed of casts +struct CastChainInfo<'tcx> { + /// The left most part of the cast chain, or in other words, the first cast in the chain + /// Used for diagnostics + left_cast: &'tcx Expr<'tcx>, + /// The starting type of the cast chain + start_ty: TypeAndMut<'tcx>, + /// The final type of the cast chain + end_ty: TypeAndMut<'tcx>, +} + +/// Returns a `CastChainInfo` with the left-most cast in the chain and the original ptr T and final +/// ptr U if the expression is composed of casts. /// Returns None if the expr is not a Cast -fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<(TypeAndMut<'tcx>, TypeAndMut<'tcx>)> { +fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<CastChainInfo<'tcx>> { if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind { let cast_to = cx.typeck_results().expr_ty(expr); let to_slice_ty = get_raw_slice_ty_mut(cast_to)?; - if let Some((inner_from_ty, _inner_to_ty)) = expr_cast_chain_tys(cx, cast_expr) { - Some((inner_from_ty, to_slice_ty)) + + // If the expression that makes up the source of this cast is itself a cast, recursively + // call `expr_cast_chain_tys` and update the end type with the final tartet type. + // Otherwise, this cast is not immediately nested, just construct the info for this cast + if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) { + Some(CastChainInfo { + end_ty: to_slice_ty, + ..prev_info + }) } else { let cast_from = cx.typeck_results().expr_ty(cast_expr); let from_slice_ty = get_raw_slice_ty_mut(cast_from)?; - Some((from_slice_ty, to_slice_ty)) + Some(CastChainInfo { + left_cast: cast_expr, + start_ty: from_slice_ty, + end_ty: to_slice_ty, + }) } } else { None diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index 55c1f085657..b58252dcb94 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -270,7 +270,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Casting a function pointer to an integer can have surprising results and can occur - /// accidentally if parantheses are omitted from a function call. If you aren't doing anything + /// accidentally if parentheses are omitted from a function call. If you aren't doing anything /// low-level with function pointers then you can opt-out of casting functions to integers in /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function /// pointer casts in your code. @@ -487,6 +487,7 @@ declare_clippy_lint! { /// let y: u32 = x.abs() as u32; /// ``` /// Use instead: + /// ```rust /// let x: i32 = -42; /// let y: u32 = x.unsigned_abs(); /// ``` diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index eae2723a7da..3227e6e86af 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -13,13 +13,14 @@ //! This lint is **warn** by default use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet_block, snippet_block_with_applicability}; +use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability}; use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -102,7 +103,7 @@ impl EarlyLintPass for CollapsibleIf { fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) { if let ast::ExprKind::If(check, then, else_) = &expr.kind { if let Some(else_) = else_ { - check_collapsible_maybe_if_let(cx, else_); + check_collapsible_maybe_if_let(cx, then.span, else_); } else if let ast::ExprKind::Let(..) = check.kind { // Prevent triggering on `if let a = b { if c { .. } }`. } else { @@ -119,7 +120,7 @@ fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool { trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*") } -fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { +fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) { if_chain! { if let ast::ExprKind::Block(ref block, _) = else_.kind; if !block_starts_with_comment(cx, block); @@ -128,6 +129,11 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { if !else_.span.from_expansion(); if let ast::ExprKind::If(..) = else_.kind; then { + // Prevent "elseif" + // Check that the "else" is followed by whitespace + let up_to_else = then_span.between(block.span); + let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false }; + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -135,7 +141,11 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { block.span, "this `else { if .. }` block can be collapsed", "collapse nested if block", - snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(), + format!( + "{}{}", + if requires_space { " " } else { "" }, + snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability) + ), applicability, ); } diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 503cef76775..b3fd8af4730 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -240,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); } }, - hir::ItemKind::Impl(ref impl_) => { + hir::ItemKind::Impl(impl_) => { self.in_trait_impl = impl_.of_trait.is_some(); }, hir::ItemKind::Trait(_, unsafety, ..) => { @@ -621,10 +621,8 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_errors::DEFAULT_LOCALE_RESOURCES, - false - ); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); let emitter = EmitterWriter::new( Box::new(io::sink()), None, @@ -741,7 +739,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok). /// Plurals are also excluded (`IDs` is ok). fn is_camel_case(s: &str) -> bool { - if s.starts_with(|c: char| c.is_digit(10)) { + if s.starts_with(|c: char| c.is_ascii_digit()) { return false; } diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index 88c54828da8..25014bfa1a5 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -180,7 +180,7 @@ const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that imp const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \ Forgetting a copy leaves the original intact"; const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \ - Dropping such a type only extends it's contained lifetimes"; + Dropping such a type only extends its contained lifetimes"; const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \ Forgetting such a type is the same as dropping it"; diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs new file mode 100644 index 00000000000..325ae2356c1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs @@ -0,0 +1,65 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, peel_blocks}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for empty `Drop` implementations. + /// + /// ### Why is this bad? + /// Empty `Drop` implementations have no effect when dropping an instance of the type. They are + /// most likely useless. However, an empty `Drop` implementation prevents a type from being + /// destructured, which might be the intention behind adding the implementation as a marker. + /// + /// ### Example + /// ```rust + /// struct S; + /// + /// impl Drop for S { + /// fn drop(&mut self) {} + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct S; + /// ``` + #[clippy::version = "1.61.0"] + pub EMPTY_DROP, + restriction, + "empty `Drop` implementations" +} +declare_lint_pass!(EmptyDrop => [EMPTY_DROP]); + +impl LateLintPass<'_> for EmptyDrop { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if_chain! { + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + items: [child], + .. + }) = item.kind; + if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait(); + if let impl_item_hir = child.id.hir_id(); + if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); + if let ImplItemKind::Fn(_, b) = &impl_item.kind; + if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); + let func_expr = peel_blocks(func_expr); + if let ExprKind::Block(block, _) = func_expr.kind; + if block.stmts.is_empty() && block.expr.is_none(); + then { + span_lint_and_sugg( + cx, + EMPTY_DROP, + item.span, + "empty drop implementation", + "try removing this impl", + String::new(), + Applicability::MaybeIncorrect + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs index fdeac8d8255..8430e7b4c82 100644 --- a/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs +++ b/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs @@ -66,7 +66,7 @@ fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Spa } // there might still be field declarations hidden from the AST - // (conditionaly compiled code using #[cfg(..)]) + // (conditionally compiled code using #[cfg(..)]) let Some(braces_span_str) = snippet_opt(cx, braces_span) else { return false; diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 845863bd209..1b19868e4c7 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id}; +use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -103,6 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { let closure_ty = cx.typeck_results().expr_ty(expr); if_chain!( + if !is_adjusted(cx, &body.value); if let ExprKind::Call(callee, args) = body.value.kind; if let ExprKind::Path(_) = callee.kind; if check_inputs(cx, body.params, args); @@ -125,8 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if_chain! { if let ty::Closure(_, substs) = callee_ty.peel_refs().kind(); if substs.as_closure().kind() == ClosureKind::FnMut; - if get_enclosing_loop_or_closure(cx.tcx, expr).is_some() - || path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee)); + if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)); then { // Mutable closure is used after current expr; we cannot consume it. @@ -145,6 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { ); if_chain!( + if !is_adjusted(cx, &body.value); if let ExprKind::MethodCall(path, args, _) = body.value.kind; if check_inputs(cx, body.params, args); let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap(); diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs new file mode 100644 index 00000000000..ee15ae9f59a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_def_path, paths, peel_hir_expr_refs}; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects cases where the result of a `format!` call is + /// appended to an existing `String`. + /// + /// ### Why is this bad? + /// Introduces an extra, avoidable heap allocation. + /// + /// ### Example + /// ```rust + /// let mut s = String::new(); + /// s += &format!("0x{:X}", 1024); + /// s.push_str(&format!("0x{:X}", 1024)); + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt::Write as _; // import without risk of name clashing + /// + /// let mut s = String::new(); + /// let _ = write!(s, "0x{:X}", 1024); + /// ``` + #[clippy::version = "1.61.0"] + pub FORMAT_PUSH_STRING, + perf, + "`format!(..)` appended to existing `String`" +} +declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); + +fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String) +} +fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id { + cx.tcx.get_diagnostic_name(macro_def_id) == Some(sym::format_macro) + } else { + false + } +} + +impl<'tcx> LateLintPass<'tcx> for FormatPushString { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let arg = match expr.kind { + ExprKind::MethodCall(_, [_, arg], _) => { + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && + match_def_path(cx, fn_def_id, &paths::PUSH_STR) { + arg + } else { + return; + } + } + ExprKind::AssignOp(op, left, arg) + if op.node == BinOpKind::Add && is_string(cx, left) => { + arg + }, + _ => return, + }; + let (arg, _) = peel_hir_expr_refs(arg); + if is_format(cx, arg) { + span_lint_and_help( + cx, + FORMAT_PUSH_STRING, + expr.span, + "`format!(..)` appended to existing `String`", + None, + "consider using `write!` to avoid the extra allocation", + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 5462d913fb4..38e943d2eb8 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -20,7 +20,7 @@ use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = must_use_attr(attrs); - if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { + if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if let Some(attr) = attr { @@ -105,12 +105,7 @@ fn check_needless_must_use( fn_header_span, "this unit-returning function has a `#[must_use]` attribute", |diag| { - diag.span_suggestion( - attr.span, - "remove the attribute", - "", - Applicability::MachineApplicable, - ); + diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable); }, ); } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { diff --git a/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs b/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs index 120fcb2619c..2e63a1f920d 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs @@ -14,7 +14,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use super::RESULT_UNIT_ERR; pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { - if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind { + if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if is_public { diff --git a/src/tools/clippy/clippy_lints/src/identity_op.rs b/src/tools/clippy/clippy_lints/src/identity_op.rs index 4d6bef89bea..40cc5cd4bcf 100644 --- a/src/tools/clippy/clippy_lints/src/identity_op.rs +++ b/src/tools/clippy/clippy_lints/src/identity_op.rs @@ -1,3 +1,4 @@ +use clippy_utils::get_parent_expr; use clippy_utils::source::snippet; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -22,6 +23,11 @@ declare_clippy_lint! { /// # let x = 1; /// x / 1 + 0 * 1 - 0 | 0; /// ``` + /// + /// ### Known problems + /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to + /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code. + /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724) #[clippy::version = "pre 1.29.0"] pub IDENTITY_OP, complexity, @@ -31,36 +37,66 @@ declare_clippy_lint! { declare_lint_pass!(IdentityOp => [IDENTITY_OP]); impl<'tcx> LateLintPass<'tcx> for IdentityOp { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if e.span.from_expansion() { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { return; } - if let ExprKind::Binary(cmp, left, right) = e.kind { - if is_allowed(cx, cmp, left, right) { - return; - } - match cmp.node { - BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - check(cx, left, 0, e.span, right.span); - check(cx, right, 0, e.span, left.span); - }, - BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span), - BinOpKind::Mul => { - check(cx, left, 1, e.span, right.span); - check(cx, right, 1, e.span, left.span); - }, - BinOpKind::Div => check(cx, right, 1, e.span, left.span), - BinOpKind::BitAnd => { - check(cx, left, -1, e.span, right.span); - check(cx, right, -1, e.span, left.span); - }, - BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span), - _ => (), + if let ExprKind::Binary(cmp, left, right) = &expr.kind { + if !is_allowed(cx, *cmp, left, right) { + match cmp.node { + BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { + if reducible_to_right(cx, expr, right) { + check(cx, left, 0, expr.span, right.span); + } + check(cx, right, 0, expr.span, left.span); + }, + BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { + check(cx, right, 0, expr.span, left.span); + }, + BinOpKind::Mul => { + if reducible_to_right(cx, expr, right) { + check(cx, left, 1, expr.span, right.span); + } + check(cx, right, 1, expr.span, left.span); + }, + BinOpKind::Div => check(cx, right, 1, expr.span, left.span), + BinOpKind::BitAnd => { + if reducible_to_right(cx, expr, right) { + check(cx, left, -1, expr.span, right.span); + } + check(cx, right, -1, expr.span, left.span); + }, + BinOpKind::Rem => { + // Don't call reducible_to_right because N % N is always reducible to 1 + check_remainder(cx, left, right, expr.span, left.span); + }, + _ => (), + } } } } } +/// Checks if `left op ..right` can be actually reduced to `right` +/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` +/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` +/// See #8724 +fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool { + if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind { + is_toplevel_binary(cx, binary) + } else { + true + } +} + +fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind { + false + } else { + true + } +} + fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { // This lint applies to integers !cx.typeck_results().expr_ty(left).peel_refs().is_integral() diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index d5430a8c917..feb1b1014b1 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -117,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { } match item.kind { - ItemKind::Impl(ref impl_) => { + ItemKind::Impl(impl_) => { let mut vis = ImplicitHasherTypeVisitor::new(cx); vis.visit_ty(impl_.self_ty); @@ -155,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { ); } }, - ItemKind::Fn(ref sig, ref generics, body_id) => { + ItemKind::Fn(ref sig, generics, body_id) => { let body = cx.tcx.hir().body(body_id); for ty in sig.decl.inputs { diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index c8ec2f45137..14b22d2b50d 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -7,6 +7,7 @@ use rustc_hir::{self as hir, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; +use std::fmt::Write as _; declare_clippy_lint! { /// ### What it does @@ -89,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { let mut fields_snippet = String::new(); let (last_ident, idents) = ordered_fields.split_last().unwrap(); for ident in idents { - fields_snippet.push_str(&format!("{}, ", ident)); + let _ = write!(fields_snippet, "{}, ", ident); } fields_snippet.push_str(&last_ident.to_string()); diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 6b62748ffef..8a84513b779 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -116,7 +116,7 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<hir let bound_ty = cx.typeck_results().node_type(pat.hir_id); if let ty::Slice(inner_ty) | ty::Array(inner_ty, _) = bound_ty.peel_refs().kind() { // The values need to use the `ref` keyword if they can't be copied. - // This will need to be adjusted if the lint want to support multable access in the future + // This will need to be adjusted if the lint want to support mutable access in the future let src_is_ref = bound_ty.is_ref() && binding != hir::BindingAnnotation::Ref; let needs_ref = !(src_is_ref || is_copy(cx, *inner_ty)); diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index 254d3f8a4d0..6a031a627df 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> { let id = cx.tcx.hir().local_def_id_to_hir_id(id); if let Node::Item(&Item { - kind: ItemKind::Impl(ref impl_item), + kind: ItemKind::Impl(impl_item), span, .. }) = cx.tcx.hir().get(id) diff --git a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs index 9284e002409..7e1548531f1 100644 --- a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs +++ b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -49,6 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { && fields .iter() .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) + && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..)) { let expr_spans = fields .iter() diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs new file mode 100644 index 00000000000..8bef13c682d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -0,0 +1,86 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::is_lint_allowed; +use clippy_utils::macros::root_macro_call_first_node; +use rustc_ast::LitKind; +use rustc_hir::Expr; +use rustc_hir::ExprKind; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the inclusion of large files via `include_bytes!()` + /// and `include_str!()` + /// + /// ### Why is this bad? + /// Including large files can increase the size of the binary + /// + /// ### Example + /// ```rust,ignore + /// let included_str = include_str!("very_large_file.txt"); + /// let included_bytes = include_bytes!("very_large_file.txt"); + /// ``` + /// + /// Instead, you can load the file at runtime: + /// ```rust,ignore + /// use std::fs; + /// + /// let string = fs::read_to_string("very_large_file.txt")?; + /// let bytes = fs::read("very_large_file.txt")?; + /// ``` + #[clippy::version = "1.62.0"] + pub LARGE_INCLUDE_FILE, + restriction, + "including a large file" +} + +pub struct LargeIncludeFile { + max_file_size: u64, +} + +impl LargeIncludeFile { + #[must_use] + pub fn new(max_file_size: u64) -> Self { + Self { max_file_size } + } +} + +impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]); + +impl LateLintPass<'_> for LargeIncludeFile { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if_chain! { + if let Some(macro_call) = root_macro_call_first_node(cx, expr); + if !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id); + if cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) + || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id); + if let ExprKind::Lit(lit) = &expr.kind; + then { + let len = match &lit.node { + // include_bytes + LitKind::ByteStr(bstr) => bstr.len(), + // include_str + LitKind::Str(sym, _) => sym.as_str().len(), + _ => return, + }; + + if len as u64 <= self.max_file_size { + return; + } + + span_lint_and_note( + cx, + LARGE_INCLUDE_FILE, + expr.span, + "attempted to include a large file", + None, + &format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + ), + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/lib.register_all.rs b/src/tools/clippy/clippy_lints/src/lib.register_all.rs index 14ca93b5f3c..e68718f9fdf 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_all.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_all.rs @@ -14,6 +14,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(attrs::DEPRECATED_SEMVER), LintId::of(attrs::MISMATCHED_TARGET_OS), LintId::of(attrs::USELESS_ATTRIBUTE), + LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(bit_mask::BAD_BIT_MASK), @@ -23,6 +24,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), @@ -77,6 +79,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), + LintId::of(format_push_string::FORMAT_PUSH_STRING), LintId::of(formatting::POSSIBLE_MISSING_COMMA), LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), @@ -165,6 +168,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::FLAT_MAP_IDENTITY), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::IS_DIGIT_ASCII_RADIX), LintId::of(methods::ITERATOR_STEP_BY_ZERO), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_COUNT), @@ -182,6 +186,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), + LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::OK_EXPECT), @@ -243,7 +248,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), @@ -275,8 +279,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), + LintId::of(strings::TRIM_SPLIT_WHITESPACE), LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -306,10 +310,12 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_hash::UNIT_HASH), LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), + LintId::of(unit_types::LET_UNIT_VALUE), LintId::of(unit_types::UNIT_ARG), LintId::of(unit_types::UNIT_CMP), LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs index 10369a855ae..6f3c433af31 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs @@ -5,6 +5,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), LintId::of(derivable_impls::DERIVABLE_IMPLS), @@ -45,6 +46,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), + LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), @@ -66,7 +68,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(no_effect::NO_EFFECT), LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), LintId::of(precedence::PRECEDENCE), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs index 532590aaa5a..c888a5feda2 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs @@ -52,6 +52,7 @@ store.register_lints(&[ attrs::INLINE_ALWAYS, attrs::MISMATCHED_TARGET_OS, attrs::USELESS_ATTRIBUTE, + await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE, await_holding_invalid::AWAIT_HOLDING_LOCK, await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, bit_mask::BAD_BIT_MASK, @@ -64,6 +65,7 @@ store.register_lints(&[ booleans::NONMINIMAL_BOOL, borrow_as_ptr::BORROW_AS_PTR, bytecount::NAIVE_BYTECOUNT, + bytes_count_to_len::BYTES_COUNT_TO_LEN, cargo::CARGO_COMMON_METADATA, cargo::MULTIPLE_CRATE_VERSIONS, cargo::NEGATIVE_FEATURE_NAMES, @@ -132,6 +134,7 @@ store.register_lints(&[ drop_forget_ref::UNDROPPED_MANUALLY_DROPS, duration_subsec::DURATION_SUBSEC, else_if_without_else::ELSE_IF_WITHOUT_ELSE, + empty_drop::EMPTY_DROP, empty_enum::EMPTY_ENUM, empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS, entry::MAP_ENTRY, @@ -165,6 +168,7 @@ store.register_lints(&[ format_args::TO_STRING_IN_FORMAT_ARGS, format_impl::PRINT_IN_FORMAT_IMPL, format_impl::RECURSIVE_FORMAT_IMPL, + format_push_string::FORMAT_PUSH_STRING, formatting::POSSIBLE_MISSING_COMMA, formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, formatting::SUSPICIOUS_ELSE_FORMATTING, @@ -205,6 +209,7 @@ store.register_lints(&[ iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR, large_const_arrays::LARGE_CONST_ARRAYS, large_enum_variant::LARGE_ENUM_VARIANT, + large_include_file::LARGE_INCLUDE_FILE, large_stack_arrays::LARGE_STACK_ARRAYS, len_zero::COMPARISON_TO_EMPTY, len_zero::LEN_WITHOUT_IS_EMPTY, @@ -302,6 +307,7 @@ store.register_lints(&[ methods::INEFFICIENT_TO_STRING, methods::INSPECT_FOR_EACH, methods::INTO_ITER_ON_REF, + methods::IS_DIGIT_ASCII_RADIX, methods::ITERATOR_STEP_BY_ZERO, methods::ITER_CLONED_COLLECT, methods::ITER_COUNT, @@ -321,6 +327,7 @@ store.register_lints(&[ methods::MAP_IDENTITY, methods::MAP_UNWRAP_OR, methods::NEEDLESS_OPTION_AS_DEREF, + methods::NEEDLESS_OPTION_TAKE, methods::NEEDLESS_SPLITN, methods::NEW_RET_NO_SELF, methods::OK_EXPECT, @@ -432,6 +439,7 @@ store.register_lints(&[ ptr::PTR_ARG, ptr_eq::PTR_EQ, ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, + pub_use::PUB_USE, question_mark::QUESTION_MARK, ranges::MANUAL_RANGE_CONTAINS, ranges::RANGE_MINUS_ONE, @@ -467,6 +475,7 @@ store.register_lints(&[ size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, stable_sort_primitive::STABLE_SORT_PRIMITIVE, + significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE, strings::STRING_ADD, strings::STRING_ADD_ASSIGN, strings::STRING_FROM_UTF8_AS_BYTES, @@ -474,6 +483,7 @@ store.register_lints(&[ strings::STRING_SLICE, strings::STRING_TO_STRING, strings::STR_TO_STRING, + strings::TRIM_SPLIT_WHITESPACE, strlen_on_c_strings::STRLEN_ON_C_STRINGS, suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, @@ -523,6 +533,7 @@ store.register_lints(&[ unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, + unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, diff --git a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs index c2fc67afba5..d43c7e03533 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs @@ -20,13 +20,17 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), + LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(regex::TRIVIAL_REGEX), + LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), + LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), + LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs index eb6534cb8ca..2ee2c6e3358 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs @@ -82,14 +82,12 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(ref_option_ref::REF_OPTION_REF), LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE), LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), + LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_ADD_ASSIGN), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(types::LINKEDLIST), LintId::of(types::OPTION_OPTION), LintId::of(unicode::UNICODE_NOT_NFC), - LintId::of(unit_types::LET_UNIT_VALUE), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(unused_async::UNUSED_ASYNC), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs index f2f5c988d8f..82431863e6c 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs @@ -7,6 +7,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(escape::BOXED_LOCAL), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), + LintId::of(format_push_string::FORMAT_PUSH_STRING), LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(loops::MANUAL_MEMCPY), @@ -23,7 +24,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(misc::CMP_OWNED), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(types::BOX_COLLECTION), LintId::of(types::REDUNDANT_ALLOCATION), LintId::of(vec::USELESS_VEC), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs index 4802dd877e9..77ec6c83ba4 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs @@ -16,6 +16,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION), LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS), LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(empty_drop::EMPTY_DROP), LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), @@ -26,6 +27,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(indexing_slicing::INDEXING_SLICING), LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), LintId::of(integer_division::INTEGER_DIVISION), + LintId::of(large_include_file::LARGE_INCLUDE_FILE), LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), LintId::of(map_err_ignore::MAP_ERR_IGNORE), @@ -53,6 +55,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(panic_unimplemented::UNIMPLEMENTED), LintId::of(panic_unimplemented::UNREACHABLE), LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), + LintId::of(pub_use::PUB_USE), LintId::of(redundant_slicing::DEREF_BY_SLICING), LintId::of(same_name_method::SAME_NAME_METHOD), LintId::of(shadow::SHADOW_REUSE), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_style.rs b/src/tools/clippy/clippy_lints/src/lib.register_style.rs index 3114afac886..d183c0449cd 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_style.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_style.rs @@ -61,6 +61,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(methods::CHARS_NEXT_CMP), LintId::of(methods::ERR_EXPECT), LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::IS_DIGIT_ASCII_RADIX), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_NEXT_SLICE), LintId::of(methods::ITER_NTH_ZERO), @@ -104,8 +105,11 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(returns::NEEDLESS_RETURN), LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(strings::TRIM_SPLIT_WHITESPACE), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(unit_types::LET_UNIT_VALUE), + LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_unit::UNUSED_UNIT), LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs index 82f45b5fd58..7a881bfe399 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs @@ -5,6 +5,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![ LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), + LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(casts::CAST_ABS_TO_UNSIGNED), diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index c9b836f9580..09071a255c5 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -163,6 +163,8 @@ mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; +mod renamed_lints; + // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absurd_extreme_comparisons; mod approx_const; @@ -181,6 +183,7 @@ mod bool_assert_comparison; mod booleans; mod borrow_as_ptr; mod bytecount; +mod bytes_count_to_len; mod cargo; mod case_sensitive_file_extension_comparisons; mod casts; @@ -209,6 +212,7 @@ mod double_parens; mod drop_forget_ref; mod duration_subsec; mod else_if_without_else; +mod empty_drop; mod empty_enum; mod empty_structs_with_brackets; mod entry; @@ -231,6 +235,7 @@ mod floating_point_arithmetic; mod format; mod format_args; mod format_impl; +mod format_push_string; mod formatting; mod from_over_into; mod from_str_radix_10; @@ -259,6 +264,7 @@ mod items_after_statements; mod iter_not_returning_iterator; mod large_const_arrays; mod large_enum_variant; +mod large_include_file; mod large_stack_arrays; mod len_zero; mod let_if_seq; @@ -336,6 +342,7 @@ mod precedence; mod ptr; mod ptr_eq; mod ptr_offset_with_cast; +mod pub_use; mod question_mark; mod ranges; mod redundant_clone; @@ -357,6 +364,7 @@ mod self_named_constructors; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; +mod significant_drop_in_scrutinee; mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; @@ -383,6 +391,7 @@ mod unit_hash; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; +mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; @@ -499,7 +508,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: { store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); - store.register_late_pass(|| Box::new(utils::inspector::DeepCodeInspector)); store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls)); store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new())); store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle)); @@ -511,8 +519,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl)); } + store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|| Box::new(utils::author::Author)); - store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding)); + let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); + store.register_late_pass(move || { + Box::new(await_holding_invalid::AwaitHolding::new( + await_holding_invalid_types.clone(), + )) + }); store.register_late_pass(|| Box::new(serde_api::SerdeApi)); let vec_box_size_threshold = conf.vec_box_size_threshold; let type_complexity_threshold = conf.type_complexity_threshold; @@ -572,7 +586,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv))); store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv))); store.register_late_pass(move || Box::new(matches::Matches::new(msrv))); - store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv))); + store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); + store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv))); store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv))); store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv))); @@ -812,6 +827,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); + store.register_late_pass(|| Box::new(empty_drop::EmptyDrop)); store.register_late_pass(|| Box::new(strings::StrToString)); store.register_late_pass(|| Box::new(strings::StringToString)); store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues)); @@ -859,6 +875,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion)); + store.register_late_pass(|| Box::new(significant_drop_in_scrutinee::SignificantDropInScrutinee)); store.register_late_pass(|| Box::new(dbg_macro::DbgMacro)); let cargo_ignore_publish = conf.cargo_ignore_publish; store.register_late_pass(move || { @@ -868,6 +885,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); + store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); + store.register_early_pass(|| Box::new(pub_use::PubUse)); + store.register_late_pass(|| Box::new(format_push_string::FormatPushString)); + store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen)); + let max_include_file_size = conf.max_include_file_size; + store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); + store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); // add lints here, do not remove this comment, it's used in `new_lint` } @@ -919,43 +943,9 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { /// /// Used in `./src/driver.rs`. pub fn register_renamed(ls: &mut rustc_lint::LintStore) { - // NOTE: when renaming a lint, add a corresponding test to tests/ui/rename.rs - ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions"); - ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); - ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); - ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); - ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map"); - ls.register_renamed("clippy::box_vec", "clippy::box_collection"); - ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"); - ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"); - ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); - ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"); - ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"); - ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used"); - ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used"); - ls.register_renamed("clippy::option_expect_used", "clippy::expect_used"); - ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); - ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); - ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); - ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); - ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); - ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str"); - ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok"); - ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types"); - ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods"); - ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow"); - ls.register_renamed("clippy::to_string_in_display", "clippy::recursive_format_impl"); - - // uplifted lints - ls.register_renamed("clippy::invalid_ref", "invalid_value"); - ls.register_renamed("clippy::into_iter_on_array", "array_into_iter"); - ls.register_renamed("clippy::unused_label", "unused_labels"); - ls.register_renamed("clippy::drop_bounds", "drop_bounds"); - ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"); - ls.register_renamed("clippy::panic_params", "non_fmt_panics"); - ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints"); - ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"); - ls.register_renamed("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"); + for (old_name, new_name) in renamed_lints::RENAMED_LINTS { + ls.register_renamed(old_name, new_name); + } } // only exists to let the dogfood integration test works. diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 662a561f171..ab5d3fa7b6d 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -1,16 +1,19 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, Visitor, + walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, + BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Ident, Symbol}; @@ -82,8 +85,10 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Fn(ref sig, ref generics, id) = item.kind { + if let ItemKind::Fn(ref sig, generics, id) = item.kind { check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true); + } else if let ItemKind::Impl(impl_) = item.kind { + report_extra_impl_lifetimes(cx, impl_); } } @@ -95,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { sig.decl, Some(id), None, - &item.generics, + item.generics, item.span, report_extra_lifetimes, ); @@ -108,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig.decl, body, trait_sig, &item.generics, item.span, true); + check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true); } } } @@ -201,8 +206,7 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: visitor.visit_ty(self_ty); !visitor.all_lts().is_empty() - } - else { + } else { false } } @@ -486,11 +490,29 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ false } -struct LifetimeChecker { +struct LifetimeChecker<'cx, 'tcx, F> { + cx: &'cx LateContext<'tcx>, map: FxHashMap<Symbol, Span>, + phantom: std::marker::PhantomData<F>, } -impl<'tcx> Visitor<'tcx> for LifetimeChecker { +impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> { + fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap<Symbol, Span>) -> LifetimeChecker<'cx, 'tcx, F> { + Self { + cx, + map, + phantom: std::marker::PhantomData, + } + } +} + +impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F> +where + F: NestedFilter<'tcx>, +{ + type Map = rustc_middle::hir::map::Map<'tcx>; + type NestedFilter = F; + // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { self.map.remove(&lifetime.name.ident().name); @@ -506,6 +528,10 @@ impl<'tcx> Visitor<'tcx> for LifetimeChecker { walk_generic_param(self, param); } } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } } fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { @@ -517,7 +543,7 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, _ => None, }) .collect(); - let mut checker = LifetimeChecker { map: hs }; + let mut checker = LifetimeChecker::<hir_nested_filter::None>::new(cx, hs); walk_generics(&mut checker, generics); walk_fn_decl(&mut checker, func); @@ -532,6 +558,32 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, } } +fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) { + let hs = impl_ + .generics + .params + .iter() + .filter_map(|par| match par.kind { + GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)), + _ => None, + }) + .collect(); + let mut checker = LifetimeChecker::<middle_nested_filter::All>::new(cx, hs); + + walk_generics(&mut checker, impl_.generics); + if let Some(ref trait_ref) = impl_.of_trait { + walk_trait_ref(&mut checker, trait_ref); + } + walk_ty(&mut checker, impl_.self_ty); + for item in impl_.items { + walk_impl_item_ref(&mut checker, item); + } + + for &v in checker.map.values() { + span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl"); + } +} + struct BodyLifetimeChecker { lifetimes_used_in_body: bool, } diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index b7430f49229..269d3c62eaf 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -42,17 +42,12 @@ declare_clippy_lint! { /// This is most probably a typo /// /// ### Known problems - /// - Recommends a signed suffix, even though the number might be too big and an unsigned - /// suffix is required + /// - Does not match on integers too large to fit in the corresponding unsigned type /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers /// /// ### Example - /// ```rust - /// // Probably mistyped - /// 2_32; - /// - /// // Good - /// 2_i32; + /// `2_32` => `2_i32` + /// `250_8 => `250_u8` /// ``` #[clippy::version = "1.30.0"] pub MISTYPED_LITERAL_SUFFIXES, @@ -310,18 +305,47 @@ impl LiteralDigitGrouping { return true; } - let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { - (exponent, &["32", "64"][..], 'f') + let (part, mistyped_suffixes, is_float) = if let Some((_, exponent)) = &mut num_lit.exponent { + (exponent, &["32", "64"][..], true) } else if num_lit.fraction.is_some() { - (&mut num_lit.integer, &["32", "64"][..], 'f') + return true; } else { - (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i') + (&mut num_lit.integer, &["8", "16", "32", "64"][..], false) }; let mut split = part.rsplit('_'); let last_group = split.next().expect("At least one group"); if split.next().is_some() && mistyped_suffixes.contains(&last_group) { - *part = &part[..part.len() - last_group.len()]; + let main_part = &part[..part.len() - last_group.len()]; + let missing_char; + if is_float { + missing_char = 'f'; + } else { + let radix = match num_lit.radix { + Radix::Binary => 2, + Radix::Octal => 8, + Radix::Decimal => 10, + Radix::Hexadecimal => 16, + }; + if let Ok(int) = u64::from_str_radix(&main_part.replace('_', ""), radix) { + missing_char = match (last_group, int) { + ("8", i) if i8::try_from(i).is_ok() => 'i', + ("16", i) if i16::try_from(i).is_ok() => 'i', + ("32", i) if i32::try_from(i).is_ok() => 'i', + ("64", i) if i64::try_from(i).is_ok() => 'i', + ("8", u) if u8::try_from(u).is_ok() => 'u', + ("16", u) if u16::try_from(u).is_ok() => 'u', + ("32", u) if u32::try_from(u).is_ok() => 'u', + ("64", _) => 'u', + _ => { + return true; + }, + } + } else { + return true; + } + } + *part = main_part; let mut sugg = num_lit.format(); sugg.push('_'); sugg.push(missing_char); diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index 9ba9642fcc8..70a118d6b35 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -168,8 +168,9 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { .operands .iter() .map(|(o, _)| match o { - InlineAsmOperand::In { expr, .. } - | InlineAsmOperand::InOut { expr, .. } => never_loop_expr(expr, main_loop_id), + InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { + never_loop_expr(expr, main_loop_id) + }, InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id), InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id) diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs index dcf44303cf4..ac3d9447b6b 100644 --- a/src/tools/clippy/clippy_lints/src/manual_bits.rs +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_parent_expr, meets_msrv, msrvs}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; @@ -24,7 +24,7 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// usize::BITS; + /// usize::BITS as usize; /// ``` #[clippy::version = "1.60.0"] pub MANUAL_BITS, @@ -59,16 +59,19 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); if let ExprKind::Lit(lit) = &other_expr.kind; if let LitKind::Int(8, _) = lit.node; - then { + let mut app = Applicability::MachineApplicable; + let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app); + let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); + span_lint_and_sugg( cx, MANUAL_BITS, expr.span, "usage of `mem::size_of::<T>()` to obtain the size of `T` in bits", "consider using", - format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()), - Applicability::MachineApplicable, + sugg, + app, ); } } @@ -108,3 +111,36 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option< } } } + +fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String { + if let Some(parent_expr) = get_parent_expr(cx, expr) { + if is_ty_conversion(parent_expr) { + return base_sugg; + } + + // These expressions have precedence over casts, the suggestion therefore + // needs to be wrapped into parentheses + match parent_expr.kind { + ExprKind::Unary(..) | ExprKind::AddrOf(..) | ExprKind::MethodCall(..) => { + return format!("({base_sugg} as usize)"); + }, + _ => {}, + } + } + + format!("{base_sugg} as usize") +} + +fn is_ty_conversion(expr: &Expr<'_>) -> bool { + if let ExprKind::Cast(..) = expr.kind { + true + } else if let ExprKind::MethodCall(path, [_], _) = expr.kind + && path.ident.name == rustc_span::sym::try_into + { + // This is only called for `usize` which implements `TryInto`. Therefore, + // we don't have to check here if `self` implements the `TryInto` trait. + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 33d1bb2985f..b8d620d8171 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -1,13 +1,17 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; -use clippy_utils::{meets_msrv, msrvs}; -use if_chain::if_chain; -use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind}; +use clippy_utils::{is_lint_allowed, meets_msrv, msrvs}; +use rustc_ast::ast::{self, VisibilityKind}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{self as hir, Expr, ExprKind, QPath}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::DefIdTree; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{sym, Span}; declare_clippy_lint! { @@ -58,67 +62,75 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -#[derive(Clone)] -pub struct ManualNonExhaustive { +#[allow(clippy::module_name_repetitions)] +pub struct ManualNonExhaustiveStruct { msrv: Option<RustcVersion>, } -impl ManualNonExhaustive { +impl ManualNonExhaustiveStruct { #[must_use] pub fn new(msrv: Option<RustcVersion>) -> Self { Self { msrv } } } -impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); +impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); -impl EarlyLintPass for ManualNonExhaustive { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { - return; - } +#[allow(clippy::module_name_repetitions)] +pub struct ManualNonExhaustiveEnum { + msrv: Option<RustcVersion>, + constructed_enum_variants: FxHashSet<(DefId, DefId)>, + potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>, +} - match &item.kind { - ItemKind::Enum(def, _) => { - check_manual_non_exhaustive_enum(cx, item, &def.variants); - }, - ItemKind::Struct(variant_data, _) => { - if let VariantData::Unit(..) = variant_data { - return; - } - - check_manual_non_exhaustive_struct(cx, item, variant_data); - }, - _ => {}, +impl ManualNonExhaustiveEnum { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { + msrv, + constructed_enum_variants: FxHashSet::default(), + potential_enums: Vec::new(), } } - - extract_msrv_attr!(EarlyContext); } -fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) { - fn is_non_exhaustive_marker(variant: &Variant) -> bool { - matches!(variant.data, VariantData::Unit(_)) - && variant.ident.as_str().starts_with('_') - && is_doc_hidden(&variant.attrs) - } +impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); - let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)); - if_chain! { - if let Some(marker) = markers.next(); - if markers.count() == 0 && variants.len() > 1; - then { - span_lint_and_then( - cx, - MANUAL_NON_EXHAUSTIVE, - item.span, - "this seems like a manual implementation of the non-exhaustive pattern", - |diag| { - if_chain! { - if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); - let header_span = cx.sess().source_map().span_until_char(item.span, '{'); - if let Some(snippet) = snippet_opt(cx, header_span); - then { +impl EarlyLintPass for ManualNonExhaustiveStruct { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { + return; + } + + if let ast::ItemKind::Struct(variant_data, _) = &item.kind { + let (fields, delimiter) = match variant_data { + ast::VariantData::Struct(fields, _) => (&**fields, '{'), + ast::VariantData::Tuple(fields, _) => (&**fields, '('), + ast::VariantData::Unit(_) => return, + }; + if fields.len() <= 1 { + return; + } + let mut iter = fields.iter().filter_map(|f| match f.vis.kind { + VisibilityKind::Public => None, + VisibilityKind::Inherited => Some(Ok(f)), + _ => Some(Err(())), + }); + if let Some(Ok(field)) = iter.next() + && iter.next().is_none() + && field.ty.kind.is_unit() + && field.ident.map_or(true, |name| name.as_str().starts_with('_')) + { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)) + && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter) + && let Some(snippet) = snippet_opt(cx, header_span) + { diag.span_suggestion( header_span, "add the attribute", @@ -126,61 +138,84 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants Applicability::Unspecified, ); } + diag.span_help(field.span, "remove this field"); } - diag.span_help(marker.span, "remove this variant"); - }); + ); + } } } + + extract_msrv_attr!(EarlyContext); } -fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) { - fn is_private(field: &FieldDef) -> bool { - matches!(field.vis.kind, VisibilityKind::Inherited) - } +impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { + return; + } - fn is_non_exhaustive_marker(field: &FieldDef) -> bool { - is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_')) + if let hir::ItemKind::Enum(def, _) = &item.kind + && def.variants.len() > 1 + { + let mut iter = def.variants.iter().filter_map(|v| { + let id = cx.tcx.hir().local_def_id(v.id); + (matches!(v.data, hir::VariantData::Unit(_)) + && v.ident.as_str().starts_with('_') + && is_doc_hidden(cx.tcx.get_attrs(id.to_def_id()))) + .then(|| (id, v.span)) + }); + if let Some((id, span)) = iter.next() + && iter.next().is_none() + { + self.potential_enums.push((item.def_id, id, item.span, span)); + } + } } - fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span { - let delimiter = match data { - VariantData::Struct(..) => '{', - VariantData::Tuple(..) => '(', - VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"), - }; + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind + && let [.., name] = p.segments + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res + && name.ident.as_str().starts_with('_') + { + let variant_id = cx.tcx.parent(id); + let enum_id = cx.tcx.parent(variant_id); - cx.sess().source_map().span_until_char(item.span, delimiter) + self.constructed_enum_variants.insert((enum_id, variant_id)); + } } - let fields = data.fields(); - let private_fields = fields.iter().filter(|f| is_private(f)).count(); - let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count(); - - if_chain! { - if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1; - if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f)); - then { + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + for &(enum_id, _, enum_span, variant_span) in + self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| { + !self + .constructed_enum_variants + .contains(&(enum_id.to_def_id(), variant_id.to_def_id())) + && !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id)) + }) + { span_lint_and_then( cx, MANUAL_NON_EXHAUSTIVE, - item.span, + enum_span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - if_chain! { - if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); - let header_span = find_header_span(cx, item, data); - if let Some(snippet) = snippet_opt(cx, header_span); - then { + if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive() + && let header_span = cx.sess().source_map().span_until_char(enum_span, '{') + && let Some(snippet) = snippet_opt(cx, header_span) + { diag.span_suggestion( header_span, "add the attribute", format!("#[non_exhaustive] {}", snippet), Applicability::Unspecified, ); - } } - diag.span_help(marker.span, "remove this field"); - }); + diag.span_help(variant_span, "remove this variant"); + }, + ); } } + + extract_msrv_attr!(LateContext); } diff --git a/src/tools/clippy/clippy_lints/src/map_clone.rs b/src/tools/clippy/clippy_lints/src/map_clone.rs index e233300e26a..ceb66947d02 100644 --- a/src/tools/clippy/clippy_lints/src/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/map_clone.rs @@ -143,15 +143,11 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { impl MapClone { fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { let mut applicability = Applicability::MachineApplicable; - let message = if is_copy { - "you are using an explicit closure for copying elements" - } else { - "you are using an explicit closure for cloning elements" - }; - let sugg_method = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { - "copied" + + let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { + ("you are using an explicit closure for copying elements", "copied") } else { - "cloned" + ("you are using an explicit closure for cloning elements", "cloned") }; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs index 77a4917ec58..3349b85f134 100644 --- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs +++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs @@ -24,7 +24,7 @@ declare_clippy_lint! { /// vec.push(value) /// } /// - /// if let Some(valie) = iter.next().ok() { + /// if let Some(value) = iter.next().ok() { /// vec.push(value) /// } /// ``` @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { if_chain! { if let ExprKind::MethodCall(ok_path, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _) if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation - if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; + if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized; if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; diff --git a/src/tools/clippy/clippy_lints/src/matches/infalliable_detructuring_match.rs b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs index 2472acb6f6e..2472acb6f6e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/infalliable_detructuring_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index b8591fe0db0..9b7344fb8b0 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .map(|a| NormalizedPat::from_pat(cx, &arena, a.pat)) .collect(); - // The furthast forwards a pattern can move without semantic changes + // The furthest forwards a pattern can move without semantic changes let forwards_blocking_idxs: Vec<_> = normalized_pats .iter() .enumerate() @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { }) .collect(); - // The furthast backwards a pattern can move without semantic changes + // The furthest backwards a pattern can move without semantic changes let backwards_blocking_idxs: Vec<_> = normalized_pats .iter() .enumerate() diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index e93b494653f..401ecef460c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -1,4 +1,4 @@ -use clippy_utils::source::{snippet_opt, walk_span_to_context}; +use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context}; use clippy_utils::{meets_msrv, msrvs}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; @@ -7,7 +7,7 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, SpanData, SyntaxContext}; -mod infalliable_detructuring_match; +mod infallible_destructuring_match; mod match_as_ref; mod match_bool; mod match_like_matches; @@ -653,6 +653,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } if let ExprKind::Match(ex, arms, source) = expr.kind { + if !span_starts_with(cx, expr.span, "match") { + return; + } if !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) @@ -691,7 +694,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { - self.infallible_destructuring_match_linted |= infalliable_detructuring_match::check(cx, local); + self.infallible_destructuring_match_linted |= infallible_destructuring_match::check(cx, local); } fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 2105a03e03a..f920ad4651f 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -83,7 +83,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { return false; } - // Recurrsively check for each `else if let` phrase, + // Recursively check for each `else if let` phrase, if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) { return check_if_let(cx, nested_if_let); } @@ -99,7 +99,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if let ExprKind::Path(ref qpath) = ret.kind { return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); } - return true; + return false; } return eq_expr_value(cx, if_let.let_expr, ret); } @@ -118,7 +118,7 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { } /// Manually check for coercion casting by checking if the type of the match operand or let expr -/// differs with the assigned local variable or the funtion return type. +/// differs with the assigned local variable or the function return type. fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) { match p_node { diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index aa3552001f4..37b67647efe 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -2,17 +2,16 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type}; +use clippy_utils::ty::needs_ordered_drop; use clippy_utils::{higher, match_def_path}; use clippy_utils::{is_lang_ctor, is_trait_method, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, PollPending}; use rustc_hir::{ intravisit::{walk_expr, Visitor}, - Arm, Block, Expr, ExprKind, LangItem, Node, Pat, PatKind, QPath, UnOp, + Arm, Block, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp, }; use rustc_lint::LateContext; use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty}; @@ -32,59 +31,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { } } -/// Checks if the drop order for a type matters. Some std types implement drop solely to -/// deallocate memory. For these types, and composites containing them, changing the drop order -/// won't result in any observable side effects. -fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) -} - -fn type_needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool { - if !seen.insert(ty) { - return false; - } - if !ty.needs_drop(cx.tcx, cx.param_env) { - false - } else if !cx - .tcx - .lang_items() - .drop_trait() - .map_or(false, |id| implements_trait(cx, ty, id, &[])) - { - // This type doesn't implement drop, so no side effects here. - // Check if any component type has any. - match ty.kind() { - ty::Tuple(fields) => fields.iter().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)), - ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, *ty, seen), - ty::Adt(adt, subs) => adt - .all_fields() - .map(|f| f.ty(cx.tcx, subs)) - .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)), - _ => true, - } - } - // Check for std types which implement drop, but only for memory allocation. - else if is_type_diagnostic_item(cx, ty, sym::Vec) - || is_type_lang_item(cx, ty, LangItem::OwnedBox) - || is_type_diagnostic_item(cx, ty, sym::Rc) - || is_type_diagnostic_item(cx, ty, sym::Arc) - || is_type_diagnostic_item(cx, ty, sym::cstring_type) - || is_type_diagnostic_item(cx, ty, sym::BTreeMap) - || is_type_diagnostic_item(cx, ty, sym::LinkedList) - || match_type(cx, ty, &paths::WEAK_RC) - || match_type(cx, ty, &paths::WEAK_ARC) - { - // Check all of the generic arguments. - if let ty::Adt(_, subs) = ty.kind() { - subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)) - } else { - true - } - } else { - true - } -} - // Extract the generic arguments out of a type fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> { if_chain! { @@ -115,7 +61,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< // e.g. In `(String::new(), 0).1` the string is a temporary value. ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => { if !matches!(expr.kind, ExprKind::Path(_)) { - if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) { + if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) { self.res = true; } else { self.visit_expr(expr); @@ -126,7 +72,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< // e.g. In `(vec![0])[0]` the vector is a temporary value. ExprKind::Index(base, index) => { if !matches!(base.kind, ExprKind::Path(_)) { - if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) { + if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) { self.res = true; } else { self.visit_expr(base); @@ -143,7 +89,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< .typeck_results() .type_dependent_def_id(expr.hir_id) .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref()); - if self_by_ref && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) { + if self_by_ref && needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) { self.res = true; } else { self.visit_expr(self_arg); @@ -243,7 +189,7 @@ fn find_sugg_for_if_let<'tcx>( // scrutinee would be, so they have to be considered as well. // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held // for the duration if body. - let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr); + let needs_drop = needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr); // check that `while_let_on_iterator` lint does not trigger if_chain! { diff --git a/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index 5076239a57c..0aadb482acd 100644 --- a/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -14,6 +14,7 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { if let ty::Adt(def, _) = ty.kind(); if def.is_struct() || def.is_union(); if fields.len() == def.non_enum_variant().fields.len(); + if !def.non_enum_variant().is_field_list_non_exhaustive(); then { span_lint_and_help( diff --git a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs new file mode 100644 index 00000000000..ad333df2f2d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -0,0 +1,50 @@ +//! Lint for `c.is_digit(10)` + +use super::IS_DIGIT_ASCII_RADIX; +use clippy_utils::{ + consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs, + source::snippet_with_applicability, +}; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_semver::RustcVersion; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + self_arg: &'tcx Expr<'_>, + radix: &'tcx Expr<'_>, + msrv: Option<&RustcVersion>, +) { + if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) { + return; + } + + if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_char() { + return; + } + + if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) { + let (num, replacement) = match radix_val { + FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"), + FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"), + _ => return, + }; + let mut applicability = Applicability::MachineApplicable; + + span_lint_and_sugg( + cx, + IS_DIGIT_ASCII_RADIX, + expr.span, + &format!("use of `char::is_digit` with literal radix of {}", num), + "try", + format!( + "{}.{}()", + snippet_with_applicability(cx, self_arg.span, "..", &mut applicability), + replacement + ), + applicability, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs index 958c3773087..152072e09c7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs @@ -1,72 +1,47 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher::Range; use clippy_utils::is_integer_const; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{ - higher::{self, Range}, - SpanlessEq, -}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::sym; use rustc_span::Span; use super::ITER_WITH_DRAIN; -const DRAIN_TYPES: &[Symbol] = &[sym::Vec, sym::VecDeque]; - pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) { - let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if let Some(drained_type) = DRAIN_TYPES.iter().find(|&&sym| is_type_diagnostic_item(cx, ty, sym)) { - // Refuse to emit `into_iter` suggestion on draining struct fields due - // to the strong possibility of processing unmovable field. - if let ExprKind::Field(..) = recv.kind { - return; - } + if !matches!(recv.kind, ExprKind::Field(..)) + && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() + && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did()) + && matches!(ty_name, sym::Vec | sym::VecDeque) + && let Some(range) = Range::hir(arg) + && is_full_range(cx, recv, range) + { + span_lint_and_sugg( + cx, + ITER_WITH_DRAIN, + span.with_hi(expr.span.hi()), + &format!("`drain(..)` used on a `{}`", ty_name), + "try this", + "into_iter()".to_string(), + Applicability::MaybeIncorrect, + ); + }; +} - if let Some(range) = higher::Range::hir(arg) { - let left_full = match range { - Range { start: Some(start), .. } if is_integer_const(cx, start, 0) => true, - Range { start: None, .. } => true, - _ => false, - }; - let full = left_full - && match range { - Range { - end: Some(end), - limits: RangeLimits::HalfOpen, - .. - } => { - // `x.drain(..x.len())` call - if_chain! { - if let ExprKind::MethodCall(len_path, len_args, _) = end.kind; - if len_path.ident.name == sym::len && len_args.len() == 1; - if let ExprKind::Path(QPath::Resolved(_, drain_path)) = recv.kind; - if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path(drain_path, len_path); - then { true } - else { false } - } - }, - Range { - end: None, - limits: RangeLimits::HalfOpen, - .. - } => true, - _ => false, - }; - if full { - span_lint_and_sugg( - cx, - ITER_WITH_DRAIN, - span.with_hi(expr.span.hi()), - &format!("`drain(..)` used on a `{}`", drained_type), - "try this", - "into_iter()".to_string(), - Applicability::MaybeIncorrect, - ); +fn is_full_range(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool { + range.start.map_or(true, |e| is_integer_const(cx, e, 0)) + && range.end.map_or(true, |e| { + if range.limits == RangeLimits::HalfOpen + && let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind + && let ExprKind::MethodCall(name, [self_arg], _) = e.kind + && name.ident.name == sym::len + && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind + { + container_path.res == path.res + } else { + false } - } - } + }) } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 70d021a1668..f3be71f6b8b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -26,6 +26,7 @@ mod implicit_clone; mod inefficient_to_string; mod inspect_for_each; mod into_iter_on_ref; +mod is_digit_ascii_radix; mod iter_cloned_collect; mod iter_count; mod iter_next_slice; @@ -42,6 +43,7 @@ mod map_flatten; mod map_identity; mod map_unwrap_or; mod needless_option_as_deref; +mod needless_option_take; mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; @@ -294,15 +296,15 @@ declare_clippy_lint! { /// Checks for methods with certain name prefixes and which /// doesn't match how self is taken. The actual rules are: /// - /// |Prefix |Postfix |`self` taken | `self` type | - /// |-------|------------|-----------------------|--------------| - /// |`as_` | none |`&self` or `&mut self` | any | - /// |`from_`| none | none | any | - /// |`into_`| none |`self` | any | - /// |`is_` | none |`&self` or none | any | - /// |`to_` | `_mut` |`&mut self` | any | - /// |`to_` | not `_mut` |`self` | `Copy` | - /// |`to_` | not `_mut` |`&self` | not `Copy` | + /// |Prefix |Postfix |`self` taken | `self` type | + /// |-------|------------|-------------------------------|--------------| + /// |`as_` | none |`&self` or `&mut self` | any | + /// |`from_`| none | none | any | + /// |`into_`| none |`self` | any | + /// |`is_` | none |`&mut self` or `&self` or none | any | + /// |`to_` | `_mut` |`&mut self` | any | + /// |`to_` | not `_mut` |`self` | `Copy` | + /// |`to_` | not `_mut` |`&self` | not `Copy` | /// /// Note: Clippy doesn't trigger methods with `to_` prefix in: /// - Traits definition. @@ -1265,7 +1267,7 @@ declare_clippy_lint! { #[clippy::version = "1.55.0"] pub EXTEND_WITH_DRAIN, perf, - "using vec.append(&mut vec) to move the full range of a vecor to another" + "using vec.append(&mut vec) to move the full range of a vector to another" } declare_clippy_lint! { @@ -2007,13 +2009,27 @@ declare_clippy_lint! { /// ### Example /// ```rust,ignore /// // Bad - /// let (key, value) = _.splitn(2, '=').next_tuple()?; - /// let value = _.splitn(2, '=').nth(1)?; + /// let s = "key=value=add"; + /// let (key, value) = s.splitn(2, '=').next_tuple()?; + /// let value = s.splitn(2, '=').nth(1)?; /// + /// let mut parts = s.splitn(2, '='); + /// let key = parts.next()?; + /// let value = parts.next()?; + /// ``` + /// Use instead: + /// ```rust,ignore /// // Good - /// let (key, value) = _.split_once('=')?; - /// let value = _.split_once('=')?.1; + /// let s = "key=value=add"; + /// let (key, value) = s.split_once('=')?; + /// let value = s.split_once('=')?.1; + /// + /// let (key, value) = s.split_once('=')?; /// ``` + /// + /// ### Limitations + /// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()` + /// in two separate `let` statements that immediately follow the `splitn()` #[clippy::version = "1.57.0"] pub MANUAL_SPLIT_ONCE, complexity, @@ -2099,7 +2115,7 @@ declare_clippy_lint! { /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")` /// will prevent loop unrolling and will result in a negative performance impact. /// - /// Additionlly, differences have been observed between aarch64 and x86_64 assembly output, + /// Additionally, differences have been observed between aarch64 and x86_64 assembly output, /// with aarch64 tending to producing faster assembly in more cases when using `.collect::<String>()` #[clippy::version = "1.61.0"] pub UNNECESSARY_JOIN, @@ -2131,6 +2147,56 @@ declare_clippy_lint! { "no-op use of `deref` or `deref_mut` method to `Option`." } +declare_clippy_lint! { + /// ### What it does + /// Finds usages of [`char::is_digit`] + /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that + /// can be replaced with [`is_ascii_digit`] + /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or + /// [`is_ascii_hexdigit`] + /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit). + /// + /// ### Why is this bad? + /// `is_digit(..)` is slower and requires specifying the radix. + /// + /// ### Example + /// ```rust + /// let c: char = '6'; + /// c.is_digit(10); + /// c.is_digit(16); + /// ``` + /// Use instead: + /// ```rust + /// let c: char = '6'; + /// c.is_ascii_digit(); + /// c.is_ascii_hexdigit(); + /// ``` + #[clippy::version = "1.61.0"] + pub IS_DIGIT_ASCII_RADIX, + style, + "use of `char::is_digit(..)` with literal radix of 10 or 16" +} + +declare_clippy_lint! { + /// + /// ### Why is this bad? + /// + /// ### Example + /// ```rust + /// let x = Some(3); + /// x.as_ref().take(); + /// ``` + /// Use instead: + /// ```rust + /// let x = Some(3); + /// x.as_ref(); + /// ``` + #[clippy::version = "1.61.0"] + pub NEEDLESS_OPTION_TAKE, + complexity, + "using `.as_ref().take()` on a temporary value" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>, @@ -2219,6 +2285,8 @@ impl_lint_pass!(Methods => [ UNNECESSARY_JOIN, ERR_EXPECT, NEEDLESS_OPTION_AS_DEREF, + IS_DIGIT_ASCII_RADIX, + NEEDLESS_OPTION_TAKE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2254,7 +2322,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_add_str::check(cx, expr, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args); single_char_pattern::check(cx, expr, method_call.ident.name, args); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, args); + unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref()); }, hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -2516,6 +2584,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), ("is_file", []) => filetype_is_file::check(cx, expr, recv), + ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("join", [join_arg]) => { @@ -2574,12 +2643,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("splitn" | "rsplitn", [count_arg, pat_arg]) => { if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); - if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) { - str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg); - } - if count >= 2 { - str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count); - } + str_splitn::check(cx, name, expr, recv, pat_arg, count, msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { @@ -2595,6 +2659,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } } }, + ("take", []) => needless_option_take::check(cx, expr, recv), ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv); }, diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs new file mode 100644 index 00000000000..829c118d291 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs @@ -0,0 +1,41 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::match_def_path; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::NEEDLESS_OPTION_TAKE; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { + // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place + if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) && has_expr_as_ref_path(cx, recv) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + NEEDLESS_OPTION_TAKE, + expr.span, + "called `Option::take()` on a temporary value", + "try", + format!( + "{}", + snippet_with_applicability(cx, recv.span, "..", &mut applicability) + ), + applicability, + ); + } +} + +fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_type = cx.typeck_results().expr_ty(expr); + is_type_diagnostic_item(cx, expr_type, sym::Option) +} + +fn has_expr_as_ref_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(ref_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + return match_def_path(cx, ref_id, &["core", "option", "Option", "as_ref"]); + } + false +} diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index 8125930b304..52891eeed06 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -1,36 +1,83 @@ use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; -use clippy_utils::{is_diag_item_method, match_def_path, paths}; +use clippy_utils::usage::local_used_after_expr; +use clippy_utils::visitors::expr_visitor; +use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{ + BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, +}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, adjustment::Adjust}; -use rustc_span::{symbol::sym, Span, SyntaxContext}; +use rustc_middle::ty; +use rustc_semver::RustcVersion; +use rustc_span::{sym, Span, Symbol, SyntaxContext}; -use super::MANUAL_SPLIT_ONCE; +use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; -pub(super) fn check_manual_split_once( +pub(super) fn check( cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, + count: u128, + msrv: Option<&RustcVersion>, ) { - if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { + if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { return; } + let needless = |usage_kind| match usage_kind { + IterUsageKind::Nth(n) => count > n + 1, + IterUsageKind::NextTuple => count > 2, + }; + let manual = count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE); + + match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) { + Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg), + Some(usage) if manual => check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage), + None if manual => { + check_manual_split_once_indirect(cx, method_name, expr, self_arg, pat_arg); + }, + _ => {}, + } +} + +fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) { + let mut app = Applicability::MachineApplicable; + let r = if method_name == "splitn" { "" } else { "r" }; + + span_lint_and_sugg( + cx, + NEEDLESS_SPLITN, + expr.span, + &format!("unnecessary use of `{r}splitn`"), + "try this", + format!( + "{}.{r}split({})", + snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut app).0, + snippet_with_context(cx, pat_arg.span, expr.span.ctxt(), "..", &mut app).0, + ), + app, + ); +} + +fn check_manual_split_once( + cx: &LateContext<'_>, + method_name: &str, + expr: &Expr<'_>, + self_arg: &Expr<'_>, + pat_arg: &Expr<'_>, + usage: &IterUsage, +) { let ctxt = expr.span.ctxt(); - let (method_name, msg, reverse) = if method_name == "splitn" { - ("split_once", "manual implementation of `split_once`", false) + let (msg, reverse) = if method_name == "splitn" { + ("manual implementation of `split_once`", false) } else { - ("rsplit_once", "manual implementation of `rsplit_once`", true) - }; - let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) { - Some(x) => x, - None => return, + ("manual implementation of `rsplit_once`", true) }; let mut app = Applicability::MachineApplicable; @@ -39,84 +86,198 @@ pub(super) fn check_manual_split_once( let sugg = match usage.kind { IterUsageKind::NextTuple => { - format!("{}.{}({})", self_snip, method_name, pat_snip) - }, - IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip), - IterUsageKind::Next | IterUsageKind::Second => { - let self_deref = { - let adjust = cx.typeck_results().expr_adjustments(self_arg); - if adjust.len() < 2 { - String::new() - } else if cx.typeck_results().expr_ty(self_arg).is_box() - || adjust - .iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box()) - { - format!("&{}", "*".repeat(adjust.len().saturating_sub(1))) - } else { - "*".repeat(adjust.len().saturating_sub(2)) - } - }; - if matches!(usage.kind, IterUsageKind::Next) { - match usage.unwrap_kind { - Some(UnwrapKind::Unwrap) => { - if reverse { - format!("{}.{}({}).unwrap().0", self_snip, method_name, pat_snip) - } else { - format!( - "{}.{}({}).map_or({}{}, |x| x.0)", - self_snip, method_name, pat_snip, self_deref, &self_snip - ) - } - }, - Some(UnwrapKind::QuestionMark) => { - format!( - "{}.{}({}).map_or({}{}, |x| x.0)", - self_snip, method_name, pat_snip, self_deref, &self_snip - ) - }, - None => { - format!( - "Some({}.{}({}).map_or({}{}, |x| x.0))", - &self_snip, method_name, pat_snip, self_deref, &self_snip - ) - }, - } + if reverse { + format!("{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))") } else { - match usage.unwrap_kind { - Some(UnwrapKind::Unwrap) => { - if reverse { - // In this case, no better suggestion is offered. - return; - } - format!("{}.{}({}).unwrap().1", self_snip, method_name, pat_snip) - }, - Some(UnwrapKind::QuestionMark) => { - format!("{}.{}({})?.1", self_snip, method_name, pat_snip) - }, - None => { - format!("{}.{}({}).map(|x| x.1)", self_snip, method_name, pat_snip) - }, - } + format!("{self_snip}.split_once({pat_snip})") + } + }, + IterUsageKind::Nth(1) => { + let (r, field) = if reverse { ("r", 0) } else { ("", 1) }; + + match usage.unwrap_kind { + Some(UnwrapKind::Unwrap) => { + format!("{self_snip}.{r}split_once({pat_snip}).unwrap().{field}") + }, + Some(UnwrapKind::QuestionMark) => { + format!("{self_snip}.{r}split_once({pat_snip})?.{field}") + }, + None => { + format!("{self_snip}.{r}split_once({pat_snip}).map(|x| x.{field})") + }, } }, + IterUsageKind::Nth(_) => return, }; span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app); } +/// checks for +/// +/// ``` +/// let mut iter = "a.b.c".splitn(2, '.'); +/// let a = iter.next(); +/// let b = iter.next(); +/// ``` +fn check_manual_split_once_indirect( + cx: &LateContext<'_>, + method_name: &str, + expr: &Expr<'_>, + self_arg: &Expr<'_>, + pat_arg: &Expr<'_>, +) -> Option<()> { + let ctxt = expr.span.ctxt(); + let mut parents = cx.tcx.hir().parent_iter(expr.hir_id); + if let (_, Node::Local(local)) = parents.next()? + && let PatKind::Binding(BindingAnnotation::Mutable, iter_binding_id, iter_ident, None) = local.pat.kind + && let (iter_stmt_id, Node::Stmt(_)) = parents.next()? + && let (_, Node::Block(enclosing_block)) = parents.next()? + + && let mut stmts = enclosing_block + .stmts + .iter() + .skip_while(|stmt| stmt.hir_id != iter_stmt_id) + .skip(1) + + && let first = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)? + && let second = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)? + && first.unwrap_kind == second.unwrap_kind + && first.name != second.name + && !local_used_after_expr(cx, iter_binding_id, second.init_expr) + { + let (r, lhs, rhs) = if method_name == "splitn" { + ("", first.name, second.name) + } else { + ("r", second.name, first.name) + }; + let msg = format!("manual implementation of `{r}split_once`"); + + let mut app = Applicability::MachineApplicable; + let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; + let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0; + + span_lint_and_then(cx, MANUAL_SPLIT_ONCE, local.span, &msg, |diag| { + diag.span_label(first.span, "first usage here"); + diag.span_label(second.span, "second usage here"); + + let unwrap = match first.unwrap_kind { + UnwrapKind::Unwrap => ".unwrap()", + UnwrapKind::QuestionMark => "?", + }; + diag.span_suggestion_verbose( + local.span, + &format!("try `{r}split_once`"), + format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"), + app, + ); + + let remove_msg = format!("remove the `{iter_ident}` usages"); + diag.span_suggestion( + first.span, + &remove_msg, + String::new(), + app, + ); + diag.span_suggestion( + second.span, + &remove_msg, + String::new(), + app, + ); + }); + } + + Some(()) +} + +#[derive(Debug)] +struct IndirectUsage<'a> { + name: Symbol, + span: Span, + init_expr: &'a Expr<'a>, + unwrap_kind: UnwrapKind, +} + +/// returns `Some(IndirectUsage)` for e.g. +/// +/// ```ignore +/// let name = binding.next()?; +/// let name = binding.next().unwrap(); +/// ``` +fn indirect_usage<'tcx>( + cx: &LateContext<'tcx>, + stmt: &Stmt<'tcx>, + binding: HirId, + ctxt: SyntaxContext, +) -> Option<IndirectUsage<'tcx>> { + if let StmtKind::Local(Local { + pat: + Pat { + kind: PatKind::Binding(BindingAnnotation::Unannotated, _, ident, None), + .. + }, + init: Some(init_expr), + hir_id: local_hir_id, + .. + }) = stmt.kind + { + let mut path_to_binding = None; + expr_visitor(cx, |expr| { + if path_to_local_id(expr, binding) { + path_to_binding = Some(expr); + } + + path_to_binding.is_none() + }) + .visit_expr(init_expr); + + let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id); + let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?; + + let (parent_id, _) = parents.find(|(_, node)| { + !matches!( + node, + Node::Expr(Expr { + kind: ExprKind::Match(.., MatchSource::TryDesugar), + .. + }) + ) + })?; + + if let IterUsage { + kind: IterUsageKind::Nth(0), + unwrap_kind: Some(unwrap_kind), + .. + } = iter_usage + { + if parent_id == *local_hir_id { + return Some(IndirectUsage { + name: ident.name, + span: stmt.span, + init_expr, + unwrap_kind, + }); + } + } + } + + None +} + +#[derive(Debug, Clone, Copy)] enum IterUsageKind { - Next, - Second, + Nth(u128), NextTuple, - RNextTuple, } +#[derive(Debug, PartialEq)] enum UnwrapKind { Unwrap, QuestionMark, } +#[derive(Debug)] struct IterUsage { kind: IterUsageKind, unwrap_kind: Option<UnwrapKind>, @@ -128,7 +289,6 @@ fn parse_iter_usage<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>, - reverse: bool, ) -> Option<IterUsage> { let (kind, span) = match iter.next() { Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { @@ -141,13 +301,7 @@ fn parse_iter_usage<'tcx>( let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?; match (name.ident.as_str(), args) { - ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if reverse { - (IterUsageKind::Second, e.span) - } else { - (IterUsageKind::Next, e.span) - } - }, + ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span), ("next_tuple", []) => { return if_chain! { if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE); @@ -157,7 +311,7 @@ fn parse_iter_usage<'tcx>( if subs.len() == 2; then { Some(IterUsage { - kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple }, + kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None }) @@ -185,11 +339,7 @@ fn parse_iter_usage<'tcx>( } } }; - match if reverse { idx ^ 1 } else { idx } { - 0 => (IterUsageKind::Next, span), - 1 => (IterUsageKind::Second, span), - _ => return None, - } + (IterUsageKind::Nth(idx), span) } else { return None; } @@ -238,86 +388,3 @@ fn parse_iter_usage<'tcx>( span, }) } - -use super::NEEDLESS_SPLITN; - -pub(super) fn check_needless_splitn( - cx: &LateContext<'_>, - method_name: &str, - expr: &Expr<'_>, - self_arg: &Expr<'_>, - pat_arg: &Expr<'_>, - count: u128, -) { - if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { - return; - } - let ctxt = expr.span.ctxt(); - let mut app = Applicability::MachineApplicable; - let (reverse, message) = if method_name == "splitn" { - (false, "unnecessary use of `splitn`") - } else { - (true, "unnecessary use of `rsplitn`") - }; - if_chain! { - if count >= 2; - if check_iter(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), count); - then { - span_lint_and_sugg( - cx, - NEEDLESS_SPLITN, - expr.span, - message, - "try this", - format!( - "{}.{}({})", - snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0, - if reverse {"rsplit"} else {"split"}, - snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0 - ), - app, - ); - } - } -} - -fn check_iter<'tcx>( - cx: &LateContext<'tcx>, - ctxt: SyntaxContext, - mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>, - count: u128, -) -> bool { - match iter.next() { - Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { - let (name, args) = if let ExprKind::MethodCall(name, [_, args @ ..], _) = e.kind { - (name, args) - } else { - return false; - }; - if_chain! { - if let Some(did) = cx.typeck_results().type_dependent_def_id(e.hir_id); - if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - then { - match (name.ident.as_str(), args) { - ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - return true; - }, - ("next_tuple", []) if count > 2 => { - return true; - }, - ("nth", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) { - if count > idx + 1 { - return true; - } - } - }, - _ => return false, - } - } - } - }, - _ => return false, - }; - false -} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 1555758fc4a..02b882e8b55 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -5,6 +5,8 @@ use clippy_utils::source::snippet_opt; use clippy_utils::ty::{ contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs, }; +use clippy_utils::{meets_msrv, msrvs}; + use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind}; @@ -13,12 +15,19 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; +use rustc_semver::RustcVersion; use rustc_span::{sym, Symbol}; use std::cmp::max; use super::UNNECESSARY_TO_OWNED; -pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) { +pub fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + method_name: Symbol, + args: &'tcx [Expr<'tcx>], + msrv: Option<&RustcVersion>, +) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let [receiver] = args; @@ -33,7 +42,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { return; } - if check_into_iter_call_arg(cx, expr, method_name, receiver) { + if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { return; } check_other_call_arg(cx, expr, method_name, receiver); @@ -178,7 +187,13 @@ fn check_addr_of_expr( /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its /// call of a `to_owned`-like function is unnecessary. -fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { +fn check_into_iter_call_arg( + cx: &LateContext<'_>, + expr: &Expr<'_>, + method_name: Symbol, + receiver: &Expr<'_>, + msrv: Option<&RustcVersion>, +) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); if let Some(callee_def_id) = fn_def_id(cx, parent); @@ -192,7 +207,7 @@ fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } - let cloned_or_copied = if is_copy(cx, item_ty) { "copied" } else { "cloned" }; + let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" }; // The next suggestion may be incorrect because the removal of the `to_owned`-like // function could cause the iterator to hold a reference to a resource that is used // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. diff --git a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs index aecfea9c141..4b368d3ffae 100644 --- a/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs +++ b/src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs @@ -14,7 +14,7 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]), (&[Convention::StartsWith("from_")], &[SelfKind::No]), (&[Convention::StartsWith("into_")], &[SelfKind::Value]), - (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]), + (&[Convention::StartsWith("is_")], &[SelfKind::RefMut, SelfKind::Ref, SelfKind::No]), (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]), (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]), diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs index d955fad7d41..6860b60acbd 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs @@ -361,7 +361,7 @@ impl MiscEarlyLints { // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression. // FIXME: Find a better way to detect those cases. let lit_snip = match snippet_opt(cx, lit.span) { - Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip, + Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip, _ => return, }; diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index ac2f16b49e3..0d953299189 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -44,7 +44,7 @@ declare_clippy_lint! { /// pub struct PubBaz; /// impl PubBaz { /// fn private() {} // ok - /// pub fn not_ptrivate() {} // missing #[inline] + /// pub fn not_private() {} // missing #[inline] /// } /// /// impl Bar for PubBaz { @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let attrs = cx.tcx.hir().attrs(it.hir_id()); check_missing_inline_attrs(cx, attrs, it.span, desc); }, - hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, _bounds, trait_items) => { + hir::ItemKind::Trait(ref _is_auto, ref _unsafe, _generics, _bounds, trait_items) => { // note: we need to check if the trait is exported so we can't use // `LateLintPass::check_trait_item` here. for tit in trait_items { diff --git a/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs index a8a8d174a82..95395e2e136 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs @@ -53,7 +53,7 @@ fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } -fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> { +fn suggesstion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> { if let ExprKind::Binary(ref op, left, right) = expr.kind { if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) { let op_snippet = match op.node { @@ -75,7 +75,7 @@ impl LateLintPass<'_> for NeedlessBitwiseBool { expr.span, "use of bitwise operator instead of lazy operator between booleans", |diag| { - if let Some(sugg) = suggession_snippet(cx, expr) { + if let Some(sugg) = suggesstion_snippet(cx, expr) { diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index 9957afcbf04..b70871b38be 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -1,10 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_opt; -use clippy_utils::visitors::{expr_visitor, is_local_used}; -use rustc_errors::Applicability; +use clippy_utils::ty::needs_ordered_drop; +use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used}; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind}; +use rustc_hir::{ + BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, + StmtKind, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -73,6 +77,31 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> seen } +fn contains_let(cond: &Expr<'_>) -> bool { + let mut seen = false; + expr_visitor_no_bodies(|expr| { + if let ExprKind::Let(_) = expr.kind { + seen = true; + } + + !seen + }) + .visit_expr(cond); + + seen +} + +fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { + let StmtKind::Local(local) = stmt.kind else { return false }; + !local.pat.walk_short(|pat| { + if let PatKind::Binding(.., None) = pat.kind { + !needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat)) + } else { + true + } + }) +} + #[derive(Debug)] struct LocalAssign { lhs_id: HirId, @@ -187,11 +216,14 @@ fn first_usage<'tcx>( local_stmt_id: HirId, block: &'tcx Block<'tcx>, ) -> Option<Usage<'tcx>> { + let significant_drop = needs_ordered_drop(cx, cx.typeck_results().node_type(binding_id)); + block .stmts .iter() .skip_while(|stmt| stmt.hir_id != local_stmt_id) .skip(1) + .take_while(|stmt| !significant_drop || !stmt_needs_ordered_drop(cx, stmt)) .find(|&stmt| is_local_used(cx, stmt, binding_id)) .and_then(|stmt| match stmt.kind { StmtKind::Expr(expr) => Some(Usage { @@ -235,12 +267,15 @@ fn check<'tcx>( match usage.expr.kind { ExprKind::Assign(..) => { let assign = LocalAssign::new(cx, usage.expr, binding_id)?; + let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); + msg_span.push_span_label(local_stmt.span, "created here"); + msg_span.push_span_label(assign.span, "initialised here"); span_lint_and_then( cx, NEEDLESS_LATE_INIT, - local_stmt.span, - "unneeded late initalization", + msg_span, + "unneeded late initialization", |diag| { diag.tool_only_span_suggestion( local_stmt.span, @@ -258,14 +293,14 @@ fn check<'tcx>( }, ); }, - ExprKind::If(_, then_expr, Some(else_expr)) => { + ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => { let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; span_lint_and_then( cx, NEEDLESS_LATE_INIT, local_stmt.span, - "unneeded late initalization", + "unneeded late initialization", |diag| { diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); @@ -296,7 +331,7 @@ fn check<'tcx>( cx, NEEDLESS_LATE_INIT, local_stmt.span, - "unneeded late initalization", + "unneeded late initialization", |diag| { diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); @@ -333,12 +368,11 @@ fn check<'tcx>( impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); - if_chain! { if let Local { init: None, pat: &Pat { - kind: PatKind::Binding(_, binding_id, _, None), + kind: PatKind::Binding(BindingAnnotation::Unannotated, binding_id, _, None), .. }, source: LocalSource::Normal, diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs index 96c00c205ff..2f733f221d5 100644 --- a/src/tools/clippy/clippy_lints/src/new_without_default.rs +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if let hir::ItemKind::Impl(hir::Impl { of_trait: None, - ref generics, + generics, self_ty: impl_self_ty, items, .. diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index 0d0c88b02c7..e3bc40c4b49 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -197,7 +197,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { if interned_name.chars().any(char::is_uppercase) { return; } - if interned_name.chars().all(|c| c.is_digit(10) || c == '_') { + if interned_name.chars().all(|c| c.is_ascii_digit() || c == '_') { span_lint( self.0.cx, JUST_UNDERSCORES_AND_DIGITS, diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs index c19cea66104..e8532db4f71 100644 --- a/src/tools/clippy/clippy_lints/src/octal_escapes.rs +++ b/src/tools/clippy/clippy_lints/src/octal_escapes.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// /// ### Known problems /// The actual meaning can be the intended one. `\x00` can be used in these - /// cases to be unambigious. + /// cases to be unambiguous. /// /// The lint does not trigger for format strings in `print!()`, `write!()` /// and friends since the string is already preprocessed when Clippy lints diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index 8e61f234776..beb812793f8 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lint_allowed; use itertools::{izip, Itertools}; use rustc_ast::{walk_list, Label, Mutability}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -8,7 +9,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; +use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor}; use rustc_hir::{ Arm, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp, @@ -33,6 +34,9 @@ declare_clippy_lint! { /// and the assigned variables are also only in recursion, it is useless. /// /// ### Known problems + /// Too many code paths in the linting code are currently untested and prone to produce false + /// positives or are prone to have performance implications. + /// /// In some cases, this would not catch all useless arguments. /// /// ```rust @@ -85,7 +89,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.60.0"] pub ONLY_USED_IN_RECURSION, - complexity, + nursery, "arguments that is only used in recursion can be removed" } declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]); @@ -100,6 +104,9 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { _: Span, id: HirId, ) { + if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) { + return; + } if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind { let def_id = id.owner.to_def_id(); let data = cx.tcx.def_path(def_id).data; @@ -145,7 +152,8 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { is_method: matches!(kind, FnKind::Method(..)), has_self, ty_res, - ty_ctx: cx.tcx, + tcx: cx.tcx, + visited_exprs: FxHashSet::default(), }; visitor.visit_expr(&body.value); @@ -206,19 +214,13 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { } pub fn is_primitive(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, - ty::Ref(_, t, _) => is_primitive(*t), - _ => false, - } + let ty = ty.peel_refs(); + ty.is_primitive() || ty.is_str() } pub fn is_array(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Array(..) | ty::Slice(..) => true, - ty::Ref(_, t, _) => is_array(*t), - _ => false, - } + let ty = ty.peel_refs(); + ty.is_array() || ty.is_array_slice() } /// This builds the graph of side effect. @@ -250,40 +252,30 @@ pub struct SideEffectVisit<'tcx> { is_method: bool, has_self: bool, ty_res: &'tcx TypeckResults<'tcx>, - ty_ctx: TyCtxt<'tcx>, + tcx: TyCtxt<'tcx>, + visited_exprs: FxHashSet<HirId>, } impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> { - fn visit_block(&mut self, b: &'tcx Block<'tcx>) { - b.stmts.iter().for_each(|stmt| { - self.visit_stmt(stmt); - self.ret_vars.clear(); - }); - walk_list!(self, visit_expr, b.expr); - } - fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) { match s.kind { StmtKind::Local(Local { pat, init: Some(init), .. }) => { self.visit_pat_expr(pat, init, false); - self.ret_vars.clear(); }, - StmtKind::Item(i) => { - let item = self.ty_ctx.hir().item(i); - self.visit_item(item); - self.ret_vars.clear(); - }, - StmtKind::Expr(e) | StmtKind::Semi(e) => { - self.visit_expr(e); - self.ret_vars.clear(); + StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => { + walk_stmt(self, s); }, StmtKind::Local(_) => {}, } + self.ret_vars.clear(); } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if !self.visited_exprs.insert(ex.hir_id) { + return; + } match ex.kind { ExprKind::Array(exprs) | ExprKind::Tup(exprs) => { self.ret_vars = exprs @@ -307,7 +299,7 @@ impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> { ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms), // since analysing the closure is not easy, just set all variables in it to side-effect ExprKind::Closure(_, _, body_id, _, _) => { - let body = self.ty_ctx.hir().body(body_id); + let body = self.tcx.hir().body(body_id); self.visit_body(body); let vars = std::mem::take(&mut self.ret_vars); self.add_side_effect(vars); diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index c9f807f2aa3..ea5a8f0858b 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -78,7 +78,7 @@ fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { /// A struct containing information about occurrences of the /// `if let Some(..) = .. else` construct that this lint detects. -struct OptionIfLetElseOccurence { +struct OptionIfLetElseOccurrence { option: String, method_sugg: String, some_expr: String, @@ -100,9 +100,9 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo } /// If this expression is the option if let/else construct we're detecting, then -/// this function returns an `OptionIfLetElseOccurence` struct with details if +/// this function returns an `OptionIfLetElseOccurrence` struct with details if /// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurence> { +fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurrence> { if_chain! { if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly if !in_constant(cx, expr.hir_id); @@ -154,7 +154,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> } } } - Some(OptionIfLetElseOccurence { + Some(OptionIfLetElseOccurrence { option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), method_sugg: method_sugg.to_string(), some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")), diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 48a2666a2e0..c35eeeac67a 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -3,6 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::expr_sig; +use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths}; use if_chain::if_chain; use rustc_errors::{Applicability, MultiSpan}; @@ -10,9 +11,9 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{ - self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, + self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn, - TraitItem, TraitItemKind, TyKind, + TraitItem, TraitItemKind, TyKind, Unsafety, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; @@ -88,19 +89,26 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// This lint checks for functions that take immutable - /// references and return mutable ones. + /// This lint checks for functions that take immutable references and return + /// mutable ones. This will not trigger if no unsafe code exists as there + /// are multiple safe functions which will do this transformation + /// + /// To be on the conservative side, if there's at least one mutable + /// reference with the output lifetime, this lint will not trigger. /// /// ### Why is this bad? - /// This is trivially unsound, as one can create two - /// mutable references from the same (immutable!) source. - /// This [error](https://github.com/rust-lang/rust/issues/39465) - /// actually lead to an interim Rust release 1.15.1. + /// Creating a mutable reference which can be repeatably derived from an + /// immutable reference is unsound as it allows creating multiple live + /// mutable references to the same object. + /// + /// This [error](https://github.com/rust-lang/rust/issues/39465) actually + /// lead to an interim Rust release 1.15.1. /// /// ### Known problems - /// To be on the conservative side, if there's at least one - /// mutable reference with the output lifetime, this lint will not trigger. - /// In practice, this case is unlikely anyway. + /// This pattern is used by memory allocators to allow allocating multiple + /// objects while returning mutable references to each one. So long as + /// different mutable references are returned each time such a function may + /// be safe. /// /// ### Example /// ```ignore @@ -145,7 +153,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; } - check_mut_from_ref(cx, sig.decl); + check_mut_from_ref(cx, sig, None); for arg in check_fn_args( cx, cx.tcx.fn_sig(item.def_id).skip_binder().inputs(), @@ -170,10 +178,10 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { let hir = cx.tcx.hir(); let mut parents = hir.parent_iter(body.value.hir_id); - let (item_id, decl, is_trait_item) = match parents.next() { + let (item_id, sig, is_trait_item) = match parents.next() { Some((_, Node::Item(i))) => { if let ItemKind::Fn(sig, ..) = &i.kind { - (i.def_id, sig.decl, false) + (i.def_id, sig, false) } else { return; } @@ -185,14 +193,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; } if let ImplItemKind::Fn(sig, _) = &i.kind { - (i.def_id, sig.decl, false) + (i.def_id, sig, false) } else { return; } }, Some((_, Node::TraitItem(i))) => { if let TraitItemKind::Fn(sig, _) = &i.kind { - (i.def_id, sig.decl, true) + (i.def_id, sig, true) } else { return; } @@ -200,7 +208,8 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { _ => return, }; - check_mut_from_ref(cx, decl); + check_mut_from_ref(cx, sig, Some(body)); + let decl = sig.decl; let sig = cx.tcx.fn_sig(item_id).skip_binder(); let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params) .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not) @@ -473,31 +482,31 @@ fn check_fn_args<'cx, 'tcx: 'cx>( }) } -fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) { - if let FnRetTy::Return(ty) = decl.output { - if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) { - let mut immutables = vec![]; - for (_, mutbl, argspan) in decl - .inputs - .iter() - .filter_map(get_rptr_lm) - .filter(|&(lt, _, _)| lt.name == out.name) - { - if mutbl == Mutability::Mut { - return; - } - immutables.push(argspan); - } - if immutables.is_empty() { - return; - } +fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) { + if let FnRetTy::Return(ty) = sig.decl.output + && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) + { + let args: Option<Vec<_>> = sig + .decl + .inputs + .iter() + .filter_map(get_rptr_lm) + .filter(|&(lt, _, _)| lt.name == out.name) + .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span)) + .collect(); + if let Some(args) = args + && !args.is_empty() + && body.map_or(true, |body| { + sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value) + }) + { span_lint_and_then( cx, MUT_FROM_REF, ty.span, "mutable borrow from immutable input(s)", |diag| { - let ms = MultiSpan::from_spans(immutables); + let ms = MultiSpan::from_spans(args); diag.span_note(ms, "immutable borrow here"); }, ); diff --git a/src/tools/clippy/clippy_lints/src/pub_use.rs b/src/tools/clippy/clippy_lints/src/pub_use.rs new file mode 100644 index 00000000000..9d2b0cedb60 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/pub_use.rs @@ -0,0 +1,56 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// + /// Restricts the usage of `pub use ...` + /// + /// ### Why is this bad? + /// + /// `pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent + /// unintentional exports or to encourage placing exported items directly in public modules + /// + /// ### Example + /// ```rust + /// pub mod outer { + /// mod inner { + /// pub struct Test {} + /// } + /// pub use inner::Test; + /// } + /// + /// use outer::Test; + /// ``` + /// Use instead: + /// ```rust + /// pub mod outer { + /// pub struct Test {} + /// } + /// + /// use outer::Test; + /// ``` + #[clippy::version = "1.62.0"] + pub PUB_USE, + restriction, + "restricts the usage of `pub use`" +} +declare_lint_pass!(PubUse => [PUB_USE]); + +impl EarlyLintPass for PubUse { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if let ItemKind::Use(_) = item.kind && + let VisibilityKind::Public = item.vis.kind { + span_lint_and_help( + cx, + PUB_USE, + item.span, + "using `pub use`", + None, + "move the exported item to a public module instead", + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index e2e2400f8e2..323326381d4 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -1,10 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::CRATE_DEF_ID; +use rustc_span::hygiene::MacroKind; declare_clippy_lint! { /// ### What it does @@ -43,8 +45,11 @@ impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]); impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) { - if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false) { + if_chain! { + if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()); + if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false); + if is_not_macro_export(item); + then { let span = item.span.with_hi(item.ident.span.hi()); let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id()); span_lint_and_then( @@ -75,3 +80,15 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { } } } + +fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool { + if let ItemKind::Use(path, _) = item.kind { + if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res { + return false; + } + } else if let ItemKind::Macro(..) = item.kind { + return false; + } + + true +} diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs new file mode 100644 index 00000000000..bfc03116fe2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs @@ -0,0 +1,39 @@ +// This file is managed by `cargo dev rename_lint`. Prefer using that when possible. + +#[rustfmt::skip] +pub static RENAMED_LINTS: &[(&str, &str)] = &[ + ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"), + ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"), + ("clippy::box_vec", "clippy::box_collection"), + ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), + ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), + ("clippy::disallowed_method", "clippy::disallowed_methods"), + ("clippy::disallowed_type", "clippy::disallowed_types"), + ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"), + ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"), + ("clippy::identity_conversion", "clippy::useless_conversion"), + ("clippy::if_let_some_result", "clippy::match_result_ok"), + ("clippy::new_without_default_derive", "clippy::new_without_default"), + ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), + ("clippy::option_expect_used", "clippy::expect_used"), + ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"), + ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"), + ("clippy::option_unwrap_used", "clippy::unwrap_used"), + ("clippy::ref_in_deref", "clippy::needless_borrow"), + ("clippy::result_expect_used", "clippy::expect_used"), + ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"), + ("clippy::result_unwrap_used", "clippy::unwrap_used"), + ("clippy::single_char_push_str", "clippy::single_char_add_str"), + ("clippy::stutter", "clippy::module_name_repetitions"), + ("clippy::to_string_in_display", "clippy::recursive_format_impl"), + ("clippy::zero_width_space", "clippy::invisible_characters"), + ("clippy::drop_bounds", "drop_bounds"), + ("clippy::into_iter_on_array", "array_into_iter"), + ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), + ("clippy::invalid_ref", "invalid_value"), + ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), + ("clippy::panic_params", "non_fmt_panics"), + ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), + ("clippy::unknown_clippy_lints", "unknown_lints"), + ("clippy::unused_label", "unused_labels"), +]; diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs index a01e2f2db3a..c5c174cc8f6 100644 --- a/src/tools/clippy/clippy_lints/src/same_name_method.rs +++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs @@ -51,114 +51,110 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { let mut map = FxHashMap::<Res, ExistingName>::default(); for id in cx.tcx.hir().items() { - if !matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) { - continue; - } - - let item = cx.tcx.hir().item(id); - if let ItemKind::Impl(Impl { - items, - of_trait, - self_ty, - .. - }) = &item.kind + if matches!(cx.tcx.def_kind(id.def_id), DefKind::Impl) + && let item = cx.tcx.hir().item(id) + && let ItemKind::Impl(Impl { + items, + of_trait, + self_ty, + .. + }) = &item.kind + && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { - if let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { - if !map.contains_key(res) { - map.insert( - *res, - ExistingName { - impl_methods: BTreeMap::new(), - trait_methods: BTreeMap::new(), - }, - ); - } - let existing_name = map.get_mut(res).unwrap(); - - match of_trait { - Some(trait_ref) => { - let mut methods_in_trait: BTreeSet<Symbol> = if_chain! { - if let Some(Node::TraitRef(TraitRef { path, .. })) = - cx.tcx.hir().find(trait_ref.hir_ref_id); - if let Res::Def(DefKind::Trait, did) = path.res; - then{ - // FIXME: if - // `rustc_middle::ty::assoc::AssocItems::items` is public, - // we can iterate its keys instead of `in_definition_order`, - // which's more efficient - cx.tcx - .associated_items(did) - .in_definition_order() - .filter(|assoc_item| { - matches!(assoc_item.kind, AssocKind::Fn) - }) - .map(|assoc_item| assoc_item.name) - .collect() - }else{ - BTreeSet::new() - } - }; - - let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| { - if let Some(impl_span) = existing_name.impl_methods.get(&method_name) { - span_lint_and_then( - cx, - SAME_NAME_METHOD, - *impl_span, - "method's name is the same as an existing method in a trait", - |diag| { - diag.span_note( - trait_method_span, - &format!("existing `{}` defined here", method_name), - ); - }, - ); - } - if let Some(v) = existing_name.trait_methods.get_mut(&method_name) { - v.push(trait_method_span); - } else { - existing_name.trait_methods.insert(method_name, vec![trait_method_span]); - } - }; + if !map.contains_key(res) { + map.insert( + *res, + ExistingName { + impl_methods: BTreeMap::new(), + trait_methods: BTreeMap::new(), + }, + ); + } + let existing_name = map.get_mut(res).unwrap(); - for impl_item_ref in (*items).iter().filter(|impl_item_ref| { - matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }) - }) { - let method_name = impl_item_ref.ident.name; - methods_in_trait.remove(&method_name); - check_trait_method(method_name, impl_item_ref.span); + match of_trait { + Some(trait_ref) => { + let mut methods_in_trait: BTreeSet<Symbol> = if_chain! { + if let Some(Node::TraitRef(TraitRef { path, .. })) = + cx.tcx.hir().find(trait_ref.hir_ref_id); + if let Res::Def(DefKind::Trait, did) = path.res; + then{ + // FIXME: if + // `rustc_middle::ty::assoc::AssocItems::items` is public, + // we can iterate its keys instead of `in_definition_order`, + // which's more efficient + cx.tcx + .associated_items(did) + .in_definition_order() + .filter(|assoc_item| { + matches!(assoc_item.kind, AssocKind::Fn) + }) + .map(|assoc_item| assoc_item.name) + .collect() + }else{ + BTreeSet::new() } + }; - for method_name in methods_in_trait { - check_trait_method(method_name, item.span); + let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| { + if let Some(impl_span) = existing_name.impl_methods.get(&method_name) { + span_lint_and_then( + cx, + SAME_NAME_METHOD, + *impl_span, + "method's name is the same as an existing method in a trait", + |diag| { + diag.span_note( + trait_method_span, + &format!("existing `{}` defined here", method_name), + ); + }, + ); } - }, - None => { - for impl_item_ref in (*items).iter().filter(|impl_item_ref| { - matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }) - }) { - let method_name = impl_item_ref.ident.name; - let impl_span = impl_item_ref.span; - if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) { - span_lint_and_then( - cx, - SAME_NAME_METHOD, - impl_span, - "method's name is the same as an existing method in a trait", - |diag| { - // TODO should we `span_note` on every trait? - // iterate on trait_spans? - diag.span_note( - trait_spans[0], - &format!("existing `{}` defined here", method_name), - ); - }, - ); - } - existing_name.impl_methods.insert(method_name, impl_span); + if let Some(v) = existing_name.trait_methods.get_mut(&method_name) { + v.push(trait_method_span); + } else { + existing_name.trait_methods.insert(method_name, vec![trait_method_span]); } - }, - } + }; + + for impl_item_ref in (*items).iter().filter(|impl_item_ref| { + matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }) + }) { + let method_name = impl_item_ref.ident.name; + methods_in_trait.remove(&method_name); + check_trait_method(method_name, impl_item_ref.span); + } + + for method_name in methods_in_trait { + check_trait_method(method_name, item.span); + } + }, + None => { + for impl_item_ref in (*items).iter().filter(|impl_item_ref| { + matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }) + }) { + let method_name = impl_item_ref.ident.name; + let impl_span = impl_item_ref.span; + if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) { + span_lint_and_then( + cx, + SAME_NAME_METHOD, + impl_span, + "method's name is the same as an existing method in a trait", + |diag| { + // TODO should we `span_note` on every trait? + // iterate on trait_spans? + diag.span_note( + trait_spans[0], + &format!("existing `{}` defined here", method_name), + ); + }, + ); + } + existing_name.impl_methods.insert(method_name, impl_span); + } + }, } } } diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/significant_drop_in_scrutinee.rs new file mode 100644 index 00000000000..94ae0c8f5a6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/significant_drop_in_scrutinee.rs @@ -0,0 +1,408 @@ +use crate::FxHashSet; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_attr; +use clippy_utils::source::{indent_of, snippet}; +use rustc_errors::{Applicability, Diagnostic}; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{Ty, TypeAndMut}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Check for temporaries returned from function calls in a match scrutinee that have the + /// `clippy::has_significant_drop` attribute. + /// + /// ### Why is this bad? + /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have + /// an important side-effect, such as unlocking a mutex, making it important for users to be + /// able to accurately understand their lifetimes. When a temporary is returned in a function + /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may + /// be surprising. + /// + /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a + /// function call that returns a `MutexGuard` and then tries to lock again in one of the match + /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of + /// the match block and thus will not unlock. + /// + /// ### Example + /// ```rust + /// # use std::sync::Mutex; + /// + /// # struct State {} + /// + /// # impl State { + /// # fn foo(&self) -> bool { + /// # true + /// # } + /// + /// # fn bar(&self) {} + /// # } + /// + /// + /// let mutex = Mutex::new(State {}); + /// + /// match mutex.lock().unwrap().foo() { + /// true => { + /// mutex.lock().unwrap().bar(); // Deadlock! + /// } + /// false => {} + /// }; + /// + /// println!("All done!"); + /// + /// ``` + /// Use instead: + /// ```rust + /// # use std::sync::Mutex; + /// + /// # struct State {} + /// + /// # impl State { + /// # fn foo(&self) -> bool { + /// # true + /// # } + /// + /// # fn bar(&self) {} + /// # } + /// + /// let mutex = Mutex::new(State {}); + /// + /// let is_foo = mutex.lock().unwrap().foo(); + /// match is_foo { + /// true => { + /// mutex.lock().unwrap().bar(); + /// } + /// false => {} + /// }; + /// + /// println!("All done!"); + /// ``` + #[clippy::version = "1.60.0"] + pub SIGNIFICANT_DROP_IN_SCRUTINEE, + nursery, + "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime" +} + +declare_lint_pass!(SignificantDropInScrutinee => [SIGNIFICANT_DROP_IN_SCRUTINEE]); + +impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let Some(suggestions) = has_significant_drop_in_scrutinee(cx, expr) { + for found in suggestions { + span_lint_and_then( + cx, + SIGNIFICANT_DROP_IN_SCRUTINEE, + found.found_span, + "temporary with significant drop in match scrutinee", + |diag| set_diagnostic(diag, cx, expr, found), + ) + } + } + } +} + +fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) { + if found.lint_suggestion == LintSuggestion::MoveAndClone { + // If our suggestion is to move and clone, then we want to leave it to the user to + // decide how to address this lint, since it may be that cloning is inappropriate. + // Therefore, we won't to emit a suggestion. + return; + } + + let original = snippet(cx, found.found_span, ".."); + let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0)); + + let replacement = if found.lint_suggestion == LintSuggestion::MoveAndDerefToCopy { + format!("let value = *{};\n{}", original, trailing_indent) + } else if found.is_unit_return_val { + // If the return value of the expression to be moved is unit, then we don't need to + // capture the result in a temporary -- we can just replace it completely with `()`. + format!("{};\n{}", original, trailing_indent) + } else { + format!("let value = {};\n{}", original, trailing_indent) + }; + + let suggestion_message = if found.lint_suggestion == LintSuggestion::MoveOnly { + "try moving the temporary above the match" + } else { + "try moving the temporary above the match and create a copy" + }; + + let scrutinee_replacement = if found.is_unit_return_val { + "()".to_owned() + } else { + "value".to_owned() + }; + + diag.multipart_suggestion( + suggestion_message, + vec![ + (expr.span.shrink_to_lo(), replacement), + (found.found_span, scrutinee_replacement), + ], + Applicability::MaybeIncorrect, + ); +} + +/// If the expression is an ExprKind::Match, check if the scrutinee has a significant drop that may +/// have a surprising lifetime. +fn has_significant_drop_in_scrutinee<'tcx, 'a>( + cx: &'a LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> Option<Vec<FoundSigDrop>> { + let mut helper = SigDropHelper::new(cx); + match expr.kind { + ExprKind::Match(match_expr, _, _) => helper.find_sig_drop(match_expr), + _ => None, + } +} + +struct SigDropHelper<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + is_chain_end: bool, + seen_types: FxHashSet<Ty<'tcx>>, + has_significant_drop: bool, + current_sig_drop: Option<FoundSigDrop>, + sig_drop_spans: Option<Vec<FoundSigDrop>>, + special_handling_for_binary_op: bool, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum LintSuggestion { + MoveOnly, + MoveAndDerefToCopy, + MoveAndClone, +} + +#[derive(Clone, Copy)] +struct FoundSigDrop { + found_span: Span, + is_unit_return_val: bool, + lint_suggestion: LintSuggestion, +} + +impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> SigDropHelper<'a, 'tcx> { + SigDropHelper { + cx, + is_chain_end: true, + seen_types: FxHashSet::default(), + has_significant_drop: false, + current_sig_drop: None, + sig_drop_spans: None, + special_handling_for_binary_op: false, + } + } + + fn find_sig_drop(&mut self, match_expr: &'tcx Expr<'_>) -> Option<Vec<FoundSigDrop>> { + self.visit_expr(match_expr); + + // If sig drop spans is empty but we found a significant drop, it means that we didn't find + // a type that was trivially copyable as we moved up the chain after finding a significant + // drop, so move the entire scrutinee. + if self.has_significant_drop && self.sig_drop_spans.is_none() { + self.try_setting_current_suggestion(match_expr, true); + self.move_current_suggestion(); + } + + self.sig_drop_spans.take() + } + + /// This will try to set the current suggestion (so it can be moved into the suggestions vec + /// later). If allow_move_and_clone is false, the suggestion *won't* be set -- this gives us + /// an opportunity to look for another type in the chain that will be trivially copyable. + /// However, if we are at the the end of the chain, we want to accept whatever is there. (The + /// suggestion won't actually be output, but the diagnostic message will be output, so the user + /// can determine the best way to handle the lint.) + fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) { + if self.current_sig_drop.is_some() { + return; + } + let ty = self.get_type(expr); + if ty.is_ref() { + // We checked that the type was ref, so builtin_deref will return Some TypeAndMut, + // but let's avoid any chance of an ICE + if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) { + if ty.is_trivially_pure_clone_copy() { + self.current_sig_drop.replace(FoundSigDrop { + found_span: expr.span, + is_unit_return_val: false, + lint_suggestion: LintSuggestion::MoveAndDerefToCopy, + }); + } else if allow_move_and_clone { + self.current_sig_drop.replace(FoundSigDrop { + found_span: expr.span, + is_unit_return_val: false, + lint_suggestion: LintSuggestion::MoveAndClone, + }); + } + } + } else if ty.is_trivially_pure_clone_copy() { + self.current_sig_drop.replace(FoundSigDrop { + found_span: expr.span, + is_unit_return_val: false, + lint_suggestion: LintSuggestion::MoveOnly, + }); + } + } + + fn move_current_suggestion(&mut self) { + if let Some(current) = self.current_sig_drop.take() { + self.sig_drop_spans.get_or_insert_with(Vec::new).push(current); + } + } + + fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> { + self.cx.typeck_results().expr_ty(ex) + } + + fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool { + !self.seen_types.insert(ty) + } + + fn visit_exprs_for_binary_ops( + &mut self, + left: &'tcx Expr<'_>, + right: &'tcx Expr<'_>, + is_unit_return_val: bool, + span: Span, + ) { + self.special_handling_for_binary_op = true; + self.visit_expr(left); + self.visit_expr(right); + + // If either side had a significant drop, suggest moving the entire scrutinee to avoid + // unnecessary copies and to simplify cases where both sides have significant drops. + if self.has_significant_drop { + self.current_sig_drop.replace(FoundSigDrop { + found_span: span, + is_unit_return_val, + lint_suggestion: LintSuggestion::MoveOnly, + }); + } + + self.special_handling_for_binary_op = false; + } + + fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let Some(adt) = ty.ty_adt_def() { + if get_attr(cx.sess(), cx.tcx.get_attrs(adt.did()), "has_significant_drop").count() > 0 { + return true; + } + } + + match ty.kind() { + rustc_middle::ty::Adt(a, b) => { + for f in a.all_fields() { + let ty = f.ty(cx.tcx, b); + if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) { + return true; + } + } + + for generic_arg in b.iter() { + if let GenericArgKind::Type(ty) = generic_arg.unpack() { + if self.has_sig_drop_attr(cx, ty) { + return true; + } + } + } + false + }, + rustc_middle::ty::Array(ty, _) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::RawPtr(TypeAndMut { ty, .. }) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::Ref(_, ty, _) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), + _ => false, + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { + if !self.is_chain_end && self.has_sig_drop_attr(self.cx, self.get_type(ex)) { + self.has_significant_drop = true; + return; + } + self.is_chain_end = false; + + match ex.kind { + ExprKind::MethodCall(_, [ref expr, ..], _) => { + self.visit_expr(expr) + } + ExprKind::Binary(_, left, right) => { + self.visit_exprs_for_binary_ops(left, right, false, ex.span); + } + ExprKind::Assign(left, right, _) => { + self.visit_exprs_for_binary_ops(left, right, true, ex.span); + } + ExprKind::AssignOp(_, left, right) => { + self.visit_exprs_for_binary_ops(left, right, true, ex.span); + } + ExprKind::Tup(exprs) => { + for expr in exprs { + self.visit_expr(expr); + if self.has_significant_drop { + // We may have not have set current_sig_drop if all the suggestions were + // MoveAndClone, so add this tuple item's full expression in that case. + if self.current_sig_drop.is_none() { + self.try_setting_current_suggestion(expr, true); + } + + // Now we are guaranteed to have something, so add it to the final vec. + self.move_current_suggestion(); + } + // Reset `has_significant_drop` after each tuple expression so we can look for + // additional cases. + self.has_significant_drop = false; + } + if self.sig_drop_spans.is_some() { + self.has_significant_drop = true; + } + } + ExprKind::Box(..) | + ExprKind::Array(..) | + ExprKind::Call(..) | + ExprKind::Unary(..) | + ExprKind::If(..) | + ExprKind::Match(..) | + ExprKind::Field(..) | + ExprKind::Index(..) | + ExprKind::Ret(..) | + ExprKind::Repeat(..) | + ExprKind::Yield(..) | + ExprKind::MethodCall(..) => walk_expr(self, ex), + ExprKind::AddrOf(_, _, _) | + ExprKind::Block(_, _) | + ExprKind::Break(_, _) | + ExprKind::Cast(_, _) | + // Don't want to check the closure itself, only invocation, which is covered by MethodCall + ExprKind::Closure(_, _, _, _, _) | + ExprKind::ConstBlock(_) | + ExprKind::Continue(_) | + ExprKind::DropTemps(_) | + ExprKind::Err | + ExprKind::InlineAsm(_) | + ExprKind::Let(_) | + ExprKind::Lit(_) | + ExprKind::Loop(_, _, _, _) | + ExprKind::Path(_) | + ExprKind::Struct(_, _, _) | + ExprKind::Type(_, _) => { + return; + } + } + + // Once a significant temporary has been found, we need to go back up at least 1 level to + // find the span to extract for replacement, so the temporary gets dropped. However, for + // binary ops, we want to move the whole scrutinee so we avoid unnecessary copies and to + // simplify cases where both sides have significant drops. + if self.has_significant_drop && !self.special_handling_for_binary_op { + self.try_setting_current_suggestion(ex, false); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs index bcd28b42978..a6c685df721 100644 --- a/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs +++ b/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs @@ -9,15 +9,25 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does /// When sorting primitive values (integers, bools, chars, as well - /// as arrays, slices, and tuples of such items), it is better to + /// as arrays, slices, and tuples of such items), it is typically better to /// use an unstable sort than a stable sort. /// /// ### Why is this bad? - /// Using a stable sort consumes more memory and cpu cycles. Because - /// values which compare equal are identical, preserving their + /// Typically, using a stable sort consumes more memory and cpu cycles. + /// Because values which compare equal are identical, preserving their /// relative order (the guarantee that a stable sort provides) means /// nothing, while the extra costs still apply. /// + /// ### Known problems + /// + /// As pointed out in + /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241), + /// a stable sort can instead be significantly faster for certain scenarios + /// (eg. when a sorted vector is extended with new data and resorted). + /// + /// For more information and benchmarking results, please refer to the + /// issue linked above. + /// /// ### Example /// ```rust /// let mut vec = vec![2, 1, 3]; @@ -30,7 +40,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.47.0"] pub STABLE_SORT_PRIMITIVE, - perf, + pedantic, "use of sort() when sort_unstable() is equivalent" } @@ -126,7 +136,7 @@ impl LateLintPass<'_> for StableSortPrimitive { Applicability::MachineApplicable, ); diag.note( - "an unstable sort would perform faster without any observable difference for this data type", + "an unstable sort typically performs faster without any observable difference for this data type", ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 3573f632a36..7c196ccaa8c 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -5,6 +5,7 @@ use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method use clippy_utils::{peel_blocks, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -451,3 +452,58 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { } } } + +declare_clippy_lint! { + /// ### What it does + /// Warns about calling `str::trim` (or variants) before `str::split_whitespace`. + /// + /// ### Why is this bad? + /// `split_whitespace` already ignores leading and trailing whitespace. + /// + /// ### Example + /// ```rust + /// " A B C ".trim().split_whitespace(); + /// ``` + /// Use instead: + /// ```rust + /// " A B C ".split_whitespace(); + /// ``` + #[clippy::version = "1.62.0"] + pub TRIM_SPLIT_WHITESPACE, + style, + "using `str::trim()` or alike before `str::split_whitespace`" +} +declare_lint_pass!(TrimSplitWhitespace => [TRIM_SPLIT_WHITESPACE]); + +impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + let tyckres = cx.typeck_results(); + if_chain! { + if let ExprKind::MethodCall(path, [split_recv], split_ws_span) = expr.kind; + if path.ident.name == sym!(split_whitespace); + if let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id); + if cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id); + if let ExprKind::MethodCall(path, [_trim_recv], trim_span) = split_recv.kind; + if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str(); + if let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id); + if is_one_of_trim_diagnostic_items(cx, trim_def_id); + then { + span_lint_and_sugg( + cx, + TRIM_SPLIT_WHITESPACE, + trim_span.with_hi(split_ws_span.lo()), + &format!("found call to `str::{}` before `str::split_whitespace`", trim_fn_name), + &format!("remove `{}()`", trim_fn_name), + String::new(), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool { + cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id) + || cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id) + || cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id) +} diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index b5dd27ff80d..c4c1aa11004 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -550,7 +550,7 @@ fn ident_difference_expr_with_base_location( // IdentIter, then the output of this function will be almost always be correct // in practice. // - // If it turns out that problematic cases are more prelavent than we assume, + // If it turns out that problematic cases are more prevalent than we assume, // then we should be able to change this function to do the correct traversal, // without needing to change the rest of the code. diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 3d1b2ee925b..78e388a49af 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -13,6 +13,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; +use std::fmt::Write as _; declare_clippy_lint! { /// ### What it does @@ -34,7 +35,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.38.0"] pub TYPE_REPETITION_IN_BOUNDS, - pedantic, + nursery, "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } @@ -64,7 +65,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.47.0"] pub TRAIT_DUPLICATION_IN_BOUNDS, - pedantic, + nursery, "Check if the same trait bounds are specified twice during a function declaration" } @@ -182,19 +183,19 @@ impl TraitBounds { for b in v.iter() { if let GenericBound::Trait(ref poly_trait_ref, _) = b { let path = &poly_trait_ref.trait_ref.path; - hint_string.push_str(&format!( + let _ = write!(hint_string, " {} +", snippet_with_applicability(cx, path.span, "..", &mut applicability) - )); + ); } } for b in p.bounds.iter() { if let GenericBound::Trait(ref poly_trait_ref, _) = b { let path = &poly_trait_ref.trait_ref.path; - hint_string.push_str(&format!( + let _ = write!(hint_string, " {} +", snippet_with_applicability(cx, path.span, "..", &mut applicability) - )); + ); } } hint_string.truncate(hint_string.len() - 2); @@ -241,7 +242,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { ); } else { - trait_resolutions_direct.push((res_where, span_where)) + trait_resolutions_direct.push((res_where, span_where)); } } } diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs index f359b606e45..0cbf5ccefa6 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs @@ -1,10 +1,9 @@ use clippy_utils::last_path_segment; use clippy_utils::source::snippet; -use clippy_utils::ty::is_normalizable; use if_chain::if_chain; use rustc_hir::{Expr, GenericArg, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, cast::CastKind, Ty}; +use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited}; @@ -34,15 +33,12 @@ pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty // check if the component types of the transmuted collection and the result have different ABI, // size or alignment pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { - let empty_param_env = ty::ParamEnv::empty(); - // check if `from` and `to` are normalizable to avoid ICE (#4968) - if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) { - return false; - } - let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from)); - let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to)); - if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) { - from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi + if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from) + && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to) + && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from)) + && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to)) + { + from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi } else { // no idea about layout, so don't lint false @@ -91,7 +87,7 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx> let res = check.do_check(&fn_ctxt); // do_check's documentation says that it might return Ok and create - // errors in the fcx instead of returing Err in some cases. Those cases + // errors in the fcx instead of returning Err in some cases. Those cases // should be filtered out before getting here. assert!( !fn_ctxt.errors_reported_since_creation(), diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 67cc8913318..353a6f6b899 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -432,8 +432,8 @@ impl Types { fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) { // Ignore functions in trait implementations as they are usually forced by the trait definition. // - // FIXME: idially we would like to warn *if the compicated type can be simplified*, but it's hard to - // check. + // FIXME: ideally we would like to warn *if the complicated type can be simplified*, but it's hard + // to check. if context.is_in_trait_impl { return; } diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index c8912a18f18..465d8a914fb 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -156,8 +156,9 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> .array_windows::<2>() .rev() .map_while(|[start, end]| { - src.get(start.to_usize() - offset..end.to_usize() - offset) - .map(|text| (start.to_usize(), text.trim_start())) + let start = start.to_usize() - offset; + let end = end.to_usize() - offset; + src.get(start..end).map(|text| (start, text.trim_start())) }) .filter(|(_, text)| !text.is_empty()); @@ -182,7 +183,7 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> let (mut line_start, mut line) = (line_start, line); loop { if line.starts_with("/*") { - let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start(); + let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start(); let mut tokens = tokenize(src); return src[..tokens.next().unwrap().len] .to_ascii_uppercase() diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs index b25a6e3375b..f3f1f53aac5 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,18 +1,46 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::visitors::for_each_value_source; +use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::{Stmt, StmtKind}; +use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, Ty, TypeFoldable, TypeVisitor}; use super::LET_UNIT_VALUE; pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { - if let StmtKind::Local(local) = stmt.kind { - if cx.typeck_results().pat_ty(local.pat).is_unit() { - if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { - return; + if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && !local.pat.span.from_expansion() + && !in_external_macro(cx.sess(), stmt.span) + && cx.typeck_results().pat_ty(local.pat).is_unit() + { + let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) { + ControlFlow::Continue(()) + } else { + ControlFlow::Break(()) + }).is_continue(); + + if needs_inferred { + if !matches!(local.pat.kind, PatKind::Wild) { + span_lint_and_then( + cx, + LET_UNIT_VALUE, + stmt.span, + "this let-binding has unit value", + |diag| { + diag.span_suggestion( + local.pat.span, + "use a wild (`_`) binding", + "_", + Applicability::MaybeIncorrect, // snippet + ); + }, + ); } + } else { span_lint_and_then( cx, LET_UNIT_VALUE, @@ -33,3 +61,45 @@ pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } } } + +fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + let id = match e.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(ref path), + hir_id, + .. + }, + _, + ) => cx.qpath_res(path, *hir_id).opt_def_id(), + ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(e.hir_id), + _ => return false, + }; + if let Some(id) = id + && let sig = cx.tcx.fn_sig(id).skip_binder() + && let ty::Param(output_ty) = *sig.output().kind() + { + sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index)) + } else { + false + } +} + +fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool { + struct Visitor(u32); + impl<'tcx> TypeVisitor<'tcx> for Visitor { + type BreakTy = (); + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::Param(ty) = *ty.kind() { + if ty.index == self.0 { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } + } else { + ty.super_visit_with(self) + } + } + } + ty.visit_with(&mut Visitor(index)).is_break() +} diff --git a/src/tools/clippy/clippy_lints/src/unit_types/mod.rs b/src/tools/clippy/clippy_lints/src/unit_types/mod.rs index d9f5b53b413..a9e2073dec2 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/mod.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub LET_UNIT_VALUE, - pedantic, + style, "creating a `let` binding to a value of unit type, which usually can't be used afterwards" } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs new file mode 100644 index 00000000000..8a4f4c0ad97 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -0,0 +1,81 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item}; +use clippy_utils::{match_def_path, paths}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Detects cases of owned empty strings being passed as an argument to a function expecting `&str` + /// + /// ### Why is this bad? + /// + /// This results in longer and less readable code + /// + /// ### Example + /// ```rust + /// vec!["1", "2", "3"].join(&String::new()); + /// ``` + /// Use instead: + /// ```rust + /// vec!["1", "2", "3"].join(""); + /// ``` + #[clippy::version = "1.62.0"] + pub UNNECESSARY_OWNED_EMPTY_STRINGS, + style, + "detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`" +} +declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRINGS]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind; + if let ExprKind::Call(fun, args) = inner_expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); + if let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); + if inner_str.is_str(); + then { + if match_def_path(cx, fun_def_id, &paths::STRING_NEW) { + span_lint_and_sugg( + cx, + UNNECESSARY_OWNED_EMPTY_STRINGS, + expr.span, + "usage of `&String::new()` for a function expecting a `&str` argument", + "try", + "\"\"".to_owned(), + Applicability::MachineApplicable, + ); + } else { + if_chain! { + if match_def_path(cx, fun_def_id, &paths::FROM_FROM); + if let [.., last_arg] = args; + if let ExprKind::Lit(spanned) = &last_arg.kind; + if let LitKind::Str(symbol, _) = spanned.node; + if symbol.is_empty(); + let inner_expr_type = cx.typeck_results().expr_ty(inner_expr); + if is_type_diagnostic_item(cx, inner_expr_type, sym::String); + then { + span_lint_and_sugg( + cx, + UNNECESSARY_OWNED_EMPTY_STRINGS, + expr.span, + "usage of `&String::from(\"\")` for a function expecting a `&str` argument", + "try", + "\"\"".to_owned(), + Applicability::MachineApplicable, + ); + } + } + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 1d4fe9cfd3c..ae431aac83b 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{meets_msrv, msrvs, over}; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; +use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -25,7 +25,7 @@ declare_clippy_lint! { /// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*. /// /// ### Why is this bad? - /// In the example above, `Some` is repeated, which unncessarily complicates the pattern. + /// In the example above, `Some` is repeated, which unnecessarily complicates the pattern. /// /// ### Example /// ```rust @@ -230,6 +230,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize) // with which a pattern `C(p_0)` may be formed, // which we would want to join with other `C(p_j)`s. Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) + // Skip immutable refs, as grouping them saves few characters, + // and almost always requires adding parens (increasing noisiness). + // In the case of only two patterns, replacement adds net characters. + | Ref(_, Mutability::Not) // Dealt with elsewhere. | Or(_) | Paren(_) => false, // Transform `box x | ... | box y` into `box (x | y)`. @@ -241,10 +245,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize) |k| matches!(k, Box(_)), |k| always_pat!(k, Box(p) => p), ), - // Transform `&m x | ... | &m y` into `&m (x | y)`. - Ref(target, m1) => extend_with_matching( + // Transform `&mut x | ... | &mut y` into `&mut (x | y)`. + Ref(target, Mutability::Mut) => extend_with_matching( target, start, alternatives, - |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match. + |k| matches!(k, Ref(_, Mutability::Mut)), |k| always_pat!(k, Ref(p, _) => p), ), // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`. diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index f8e1021af0e..138f8bccb3f 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// /// ### Known problems /// - Unaddressed false negative in fn bodies of trait implementations - /// - False positive with assotiated types in traits (#4140) + /// - False positive with associated types in traits (#4140) /// /// ### Example /// ```rust diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index d23c85c033b..ff5be825b78 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -70,7 +70,7 @@ macro_rules! bind { }; } -/// Transforms the given `Option<T>` varibles into `OptionPat<Binding<T>>`. +/// Transforms the given `Option<T>` variables into `OptionPat<Binding<T>>`. /// This displays as `Some($name)` or `None` when printed. The name of the inner binding /// is set to the name of the variable passed to the macro. macro_rules! opt_bind { diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 271c3a3dd18..74b0168a179 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -310,6 +310,12 @@ define_Conf! { /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed. /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. (max_suggested_slice_pattern_length: u64 = 3), + /// Lint: AWAIT_HOLDING_INVALID_TYPE + (await_holding_invalid_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()), + /// Lint: LARGE_INCLUDE_FILE. + /// + /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes + (max_include_file_size: u64 = 1_000_000), } /// Search for the configuration file. diff --git a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs new file mode 100644 index 00000000000..01efc527a8c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs @@ -0,0 +1,55 @@ +use clippy_utils::get_attr; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// It formats the attached node with `{:#?}` and writes the result to the + /// standard output. This is intended for debugging. + /// + /// ### Examples + /// ```rs + /// #[clippy::dump] + /// use std::mem; + /// + /// #[clippy::dump] + /// fn foo(input: u32) -> u64 { + /// input as u64 + /// } + /// ``` + pub DUMP_HIR, + internal_warn, + "helper to dump info about code" +} + +declare_lint_pass!(DumpHir => [DUMP_HIR]); + +impl<'tcx> LateLintPass<'tcx> for DumpHir { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if has_attr(cx, item.hir_id()) { + println!("{item:#?}"); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if has_attr(cx, expr.hir_id) { + println!("{expr:#?}"); + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { + match stmt.kind { + hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return, + _ => {}, + } + if has_attr(cx, stmt.hir_id) { + println!("{stmt:#?}"); + } + } +} + +fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { + let attrs = cx.tcx.hir().attrs(hir_id); + get_attr(cx.sess(), attrs, "dump").count() > 0 +} diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs deleted file mode 100644 index 37b114a0cfb..00000000000 --- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs +++ /dev/null @@ -1,577 +0,0 @@ -//! checks for attributes - -use clippy_utils::get_attr; -use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece}; -use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty; -use rustc_session::Session; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// Dumps every ast/hir node which has the `#[clippy::dump]` - /// attribute - /// - /// ### Example - /// ```rust,ignore - /// #[clippy::dump] - /// extern crate foo; - /// ``` - /// - /// prints - /// - /// ```text - /// item `foo` - /// visibility inherited from outer item - /// extern crate dylib source: "/path/to/foo.so" - /// ``` - pub DEEP_CODE_INSPECTION, - internal_warn, - "helper to dump info about code" -} - -declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]); - -impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) { - return; - } - print_item(cx, item); - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) { - return; - } - println!("impl item `{}`", item.ident.name); - match cx.tcx.visibility(item.def_id) { - ty::Visibility::Public => println!("public"), - ty::Visibility::Restricted(def_id) => { - if def_id.is_top_level_module() { - println!("visible crate wide") - } else { - println!("visible in module `{}`", cx.tcx.def_path_str(def_id)) - } - }, - ty::Visibility::Invisible => println!("invisible"), - } - match item.kind { - hir::ImplItemKind::Const(_, body_id) => { - println!("associated constant"); - print_expr(cx, &cx.tcx.hir().body(body_id).value, 1); - }, - hir::ImplItemKind::Fn(..) => println!("method"), - hir::ImplItemKind::TyAlias(_) => println!("associated type"), - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) { - return; - } - print_expr(cx, expr, 0); - } - - fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(arm.hir_id)) { - return; - } - print_pat(cx, arm.pat, 1); - if let Some(ref guard) = arm.guard { - println!("guard:"); - print_guard(cx, guard, 1); - } - println!("body:"); - print_expr(cx, arm.body, 1); - } - - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(stmt.hir_id)) { - return; - } - match stmt.kind { - hir::StmtKind::Local(local) => { - println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id)); - println!("pattern:"); - print_pat(cx, local.pat, 0); - if let Some(e) = local.init { - println!("init expression:"); - print_expr(cx, e, 0); - } - }, - hir::StmtKind::Item(_) => println!("item decl"), - hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0), - } - } -} - -fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { - get_attr(sess, attrs, "dump").count() > 0 -} - -#[allow(clippy::similar_names)] -#[allow(clippy::too_many_lines)] -fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - println!("{}ty: {}", ind, cx.typeck_results().expr_ty(expr)); - println!( - "{}adjustments: {:?}", - ind, - cx.typeck_results().adjustments().get(expr.hir_id) - ); - match expr.kind { - hir::ExprKind::Box(e) => { - println!("{}Box", ind); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Array(v) => { - println!("{}Array", ind); - for e in v { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Call(func, args) => { - println!("{}Call", ind); - println!("{}function:", ind); - print_expr(cx, func, indent + 1); - println!("{}arguments:", ind); - for arg in args { - print_expr(cx, arg, indent + 1); - } - }, - hir::ExprKind::Let(hir::Let { pat, init, ty, .. }) => { - print_pat(cx, pat, indent + 1); - if let Some(ty) = ty { - println!("{} type annotation: {:?}", ind, ty); - } - print_expr(cx, init, indent + 1); - }, - hir::ExprKind::MethodCall(path, args, _) => { - println!("{}MethodCall", ind); - println!("{}method name: {}", ind, path.ident.name); - for arg in args { - print_expr(cx, arg, indent + 1); - } - }, - hir::ExprKind::Tup(v) => { - println!("{}Tup", ind); - for e in v { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Binary(op, lhs, rhs) => { - println!("{}Binary", ind); - println!("{}op: {:?}", ind, op.node); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::Unary(op, inner) => { - println!("{}Unary", ind); - println!("{}op: {:?}", ind, op); - print_expr(cx, inner, indent + 1); - }, - hir::ExprKind::Lit(ref lit) => { - println!("{}Lit", ind); - println!("{}{:?}", ind, lit); - }, - hir::ExprKind::Cast(e, target) => { - println!("{}Cast", ind); - print_expr(cx, e, indent + 1); - println!("{}target type: {:?}", ind, target); - }, - hir::ExprKind::Type(e, target) => { - println!("{}Type", ind); - print_expr(cx, e, indent + 1); - println!("{}target type: {:?}", ind, target); - }, - hir::ExprKind::Loop(..) => { - println!("{}Loop", ind); - }, - hir::ExprKind::If(cond, _, ref else_opt) => { - println!("{}If", ind); - println!("{}condition:", ind); - print_expr(cx, cond, indent + 1); - if let Some(els) = *else_opt { - println!("{}else:", ind); - print_expr(cx, els, indent + 1); - } - }, - hir::ExprKind::Match(cond, _, ref source) => { - println!("{}Match", ind); - println!("{}condition:", ind); - print_expr(cx, cond, indent + 1); - println!("{}source: {:?}", ind, source); - }, - hir::ExprKind::Closure(ref clause, _, _, _, _) => { - println!("{}Closure", ind); - println!("{}clause: {:?}", ind, clause); - }, - hir::ExprKind::Yield(sub, _) => { - println!("{}Yield", ind); - print_expr(cx, sub, indent + 1); - }, - hir::ExprKind::Block(_, _) => { - println!("{}Block", ind); - }, - hir::ExprKind::Assign(lhs, rhs, _) => { - println!("{}Assign", ind); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::AssignOp(ref binop, lhs, rhs) => { - println!("{}AssignOp", ind); - println!("{}op: {:?}", ind, binop.node); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::Field(e, ident) => { - println!("{}Field", ind); - println!("{}field name: {}", ind, ident.name); - println!("{}struct expr:", ind); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Index(arr, idx) => { - println!("{}Index", ind); - println!("{}array expr:", ind); - print_expr(cx, arr, indent + 1); - println!("{}index expr:", ind); - print_expr(cx, idx, indent + 1); - }, - hir::ExprKind::Path(hir::QPath::Resolved(ref ty, path)) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::ExprKind::Path(hir::QPath::TypeRelative(ty, seg)) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - hir::ExprKind::AddrOf(kind, ref muta, e) => { - println!("{}AddrOf", ind); - println!("kind: {:?}", kind); - println!("mutability: {:?}", muta); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Break(_, ref e) => { - println!("{}Break", ind); - if let Some(e) = *e { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Continue(_) => println!("{}Again", ind), - hir::ExprKind::Ret(ref e) => { - println!("{}Ret", ind); - if let Some(e) = *e { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::InlineAsm(asm) => { - println!("{}InlineAsm", ind); - println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template)); - println!("{}options: {:?}", ind, asm.options); - println!("{}operands:", ind); - for (op, _op_sp) in asm.operands { - match op { - hir::InlineAsmOperand::In { expr, .. } - | hir::InlineAsmOperand::InOut { expr, .. } => { - print_expr(cx, expr, indent + 1); - } - hir::InlineAsmOperand::Out { expr, .. } => { - if let Some(expr) = expr { - print_expr(cx, expr, indent + 1); - } - }, - hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - print_expr(cx, in_expr, indent + 1); - if let Some(out_expr) = out_expr { - print_expr(cx, out_expr, indent + 1); - } - }, - hir::InlineAsmOperand::Const { anon_const } - | hir::InlineAsmOperand::SymFn { anon_const } => { - println!("{}anon_const:", ind); - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - hir::InlineAsmOperand::SymStatic { path, .. } => { - match path { - hir::QPath::Resolved(ref ty, path) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::QPath::TypeRelative(ty, seg) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::QPath::LangItem(lang_item, ..) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - } - } - } - } - }, - hir::ExprKind::Struct(path, fields, ref base) => { - println!("{}Struct", ind); - println!("{}path: {:?}", ind, path); - for field in fields { - println!("{}field \"{}\":", ind, field.ident.name); - print_expr(cx, field.expr, indent + 1); - } - if let Some(base) = *base { - println!("{}base:", ind); - print_expr(cx, base, indent + 1); - } - }, - hir::ExprKind::ConstBlock(ref anon_const) => { - println!("{}ConstBlock", ind); - println!("{}anon_const:", ind); - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - hir::ExprKind::Repeat(val, length) => { - println!("{}Repeat", ind); - println!("{}value:", ind); - print_expr(cx, val, indent + 1); - println!("{}repeat count:", ind); - match length { - hir::ArrayLen::Infer(_, _) => println!("{}repeat count: _", ind), - hir::ArrayLen::Body(anon_const) => { - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - } - }, - hir::ExprKind::Err => { - println!("{}Err", ind); - }, - hir::ExprKind::DropTemps(e) => { - println!("{}DropTemps", ind); - print_expr(cx, e, indent + 1); - }, - } -} - -fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { - let did = item.def_id; - println!("item `{}`", item.ident.name); - match cx.tcx.visibility(item.def_id) { - ty::Visibility::Public => println!("public"), - ty::Visibility::Restricted(def_id) => { - if def_id.is_top_level_module() { - println!("visible crate wide") - } else { - println!("visible in module `{}`", cx.tcx.def_path_str(def_id)) - } - }, - ty::Visibility::Invisible => println!("invisible"), - } - match item.kind { - hir::ItemKind::ExternCrate(ref _renamed_from) => { - if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(did) { - let source = cx.tcx.used_crate_source(crate_id); - if let Some(ref src) = source.dylib { - println!("extern crate dylib source: {:?}", src.0); - } - if let Some(ref src) = source.rlib { - println!("extern crate rlib source: {:?}", src.0); - } - } else { - println!("weird extern crate without a crate id"); - } - }, - hir::ItemKind::Use(path, ref kind) => println!("{:?}, {:?}", path, kind), - hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)), - hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)), - hir::ItemKind::Fn(..) => { - let item_ty = cx.tcx.type_of(did); - println!("function of type {:#?}", item_ty); - }, - hir::ItemKind::Macro(ref macro_def, _) => { - if macro_def.macro_rules { - println!("macro introduced by `macro_rules!`"); - } else { - println!("macro introduced by `macro`"); - } - }, - hir::ItemKind::Mod(..) => println!("module"), - hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi), - hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm), - hir::ItemKind::TyAlias(..) => { - println!("type alias for {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::OpaqueTy(..) => { - println!("existential type with real type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Enum(..) => { - println!("enum definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Struct(..) => { - println!("struct definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Union(..) => { - println!("union definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Trait(..) => { - println!("trait decl"); - if cx.tcx.trait_is_auto(did.to_def_id()) { - println!("trait is auto"); - } else { - println!("trait is not auto"); - } - }, - hir::ItemKind::TraitAlias(..) => { - println!("trait alias"); - }, - hir::ItemKind::Impl(hir::Impl { - of_trait: Some(ref _trait_ref), - .. - }) => { - println!("trait impl"); - }, - hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) => { - println!("impl"); - }, - } -} - -#[allow(clippy::similar_names)] -#[allow(clippy::too_many_lines)] -fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - match pat.kind { - hir::PatKind::Wild => println!("{}Wild", ind), - hir::PatKind::Binding(ref mode, .., ident, ref inner) => { - println!("{}Binding", ind); - println!("{}mode: {:?}", ind, mode); - println!("{}name: {}", ind, ident.name); - if let Some(inner) = *inner { - println!("{}inner:", ind); - print_pat(cx, inner, indent + 1); - } - }, - hir::PatKind::Or(fields) => { - println!("{}Or", ind); - for field in fields { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Struct(ref path, fields, ignore) => { - println!("{}Struct", ind); - println!( - "{}name: {}", - ind, - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) - ); - println!("{}ignore leftover fields: {}", ind, ignore); - println!("{}fields:", ind); - for field in fields { - println!("{} field name: {}", ind, field.ident.name); - if field.is_shorthand { - println!("{} in shorthand notation", ind); - } - print_pat(cx, field.pat, indent + 1); - } - }, - hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => { - println!("{}TupleStruct", ind); - println!( - "{}path: {}", - ind, - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) - ); - if let Some(dot_position) = opt_dots_position { - println!("{}dot position: {}", ind, dot_position); - } - for field in fields { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Path(hir::QPath::Resolved(ref ty, path)) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::PatKind::Path(hir::QPath::TypeRelative(ty, seg)) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - hir::PatKind::Tuple(pats, opt_dots_position) => { - println!("{}Tuple", ind); - if let Some(dot_position) = opt_dots_position { - println!("{}dot position: {}", ind, dot_position); - } - for field in pats { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Box(inner) => { - println!("{}Box", ind); - print_pat(cx, inner, indent + 1); - }, - hir::PatKind::Ref(inner, ref muta) => { - println!("{}Ref", ind); - println!("{}mutability: {:?}", ind, muta); - print_pat(cx, inner, indent + 1); - }, - hir::PatKind::Lit(e) => { - println!("{}Lit", ind); - print_expr(cx, e, indent + 1); - }, - hir::PatKind::Range(ref l, ref r, ref range_end) => { - println!("{}Range", ind); - if let Some(expr) = l { - print_expr(cx, expr, indent + 1); - } - if let Some(expr) = r { - print_expr(cx, expr, indent + 1); - } - match *range_end { - hir::RangeEnd::Included => println!("{} end included", ind), - hir::RangeEnd::Excluded => println!("{} end excluded", ind), - } - }, - hir::PatKind::Slice(first_pats, ref range, last_pats) => { - println!("{}Slice [a, b, ..i, y, z]", ind); - println!("[a, b]:"); - for pat in first_pats { - print_pat(cx, pat, indent + 1); - } - println!("i:"); - if let Some(pat) = *range { - print_pat(cx, pat, indent + 1); - } - println!("[y, z]:"); - for pat in last_pats { - print_pat(cx, pat, indent + 1); - } - }, - } -} - -fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - match guard { - hir::Guard::If(expr) => { - println!("{}If", ind); - print_expr(cx, expr, indent + 1); - }, - hir::Guard::IfLet(pat, expr) => { - println!("{}IfLet", ind); - print_pat(cx, pat, indent + 1); - print_expr(cx, expr, indent + 1); - }, - } -} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index 25d74b8c499..0e8f40e9210 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -292,7 +292,7 @@ declare_clippy_lint! { /// Checks for unnecessary conversion from Symbol to a string. /// /// ### Why is this bad? - /// It's faster use symbols directly intead of strings. + /// It's faster use symbols directly instead of strings. /// /// ### Example /// Bad: @@ -823,7 +823,7 @@ fn suggest_note( cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.span, - "this call is collspible", + "this call is collapsible", "collapse into", format!( "span_lint_and_note({}, {}, {}, {}, {}, {})", diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index ca03b8010dd..8c1910b3b2a 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -33,7 +33,7 @@ use std::path::Path; /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; /// These lints are excluded from the export. -const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"]; +const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"]; /// These groups will be ignored by the lint group matcher. This is useful for collections like /// `clippy::all` const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"]; @@ -117,7 +117,7 @@ const APPLICABILITY_NAME_INDEX: usize = 2; /// This applicability will be set for unresolved applicability values. const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved"; /// The version that will be displayed if none has been defined -const VERION_DEFAULT_STR: &str = "Unknown"; +const VERSION_DEFAULT_STR: &str = "Unknown"; declare_clippy_lint! { /// ### What it does @@ -571,7 +571,7 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> { fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String { extract_clippy_version_value(cx, item).map_or_else( - || VERION_DEFAULT_STR.to_string(), + || VERSION_DEFAULT_STR.to_string(), |version| version.as_str().to_string(), ) } @@ -872,7 +872,7 @@ impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> { self.suggestion_count += 2; } - /// Checks if the suggestions include multiple spanns + /// Checks if the suggestions include multiple spans fn is_multi_part(&self) -> bool { self.suggestion_count > 1 } diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index dc385ebacba..787e9fd982c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -1,5 +1,5 @@ pub mod author; pub mod conf; -pub mod inspector; +pub mod dump_hir; #[cfg(feature = "internal")] pub mod internal_lints; diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index f3d818cc348..54b93a20a05 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -13,7 +13,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{sym, BytePos, Span, DUMMY_SP}; +use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP}; declare_clippy_lint! { /// ### What it does @@ -454,6 +454,7 @@ impl SimpleFormatArgs { } }, ArgumentNamed(n, _) => { + let n = Symbol::intern(n); if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) { match x.1.as_slice() { // A non-empty format string has been seen already. @@ -495,7 +496,7 @@ impl Write { let span = parser .arg_places .last() - .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(x)); + .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(InnerSpan::new(x.start, x.end))); if !self.in_debug_impl && arg.format.ty == "?" { // FIXME: modify rustc's fmt string parser to give us the current span diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 3fce4987679..7919800483f 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -688,7 +688,8 @@ 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(_, lt), Eq(_, rt)) => lt.kind == rt.kind, + (Eq(_, MacArgsEq::Ast(le)), Eq(_, MacArgsEq::Ast(re))) => eq_expr(le, re), + (Eq(_, MacArgsEq::Hir(ll)), Eq(_, MacArgsEq::Hir(rl))) => ll.kind == rl.kind, _ => false, } } diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 25a84d16650..7f448175e32 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -22,6 +22,7 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[ ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")), ("dump", DeprecationStatus::None), ("msrv", DeprecationStatus::None), + ("has_significant_drop", DeprecationStatus::None), ]; pub struct LimitStack { diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index be46b791aa4..fdb822c3e5b 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -5,7 +5,7 @@ use if_chain::if_chain; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp}; +use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::mir::interpret::Scalar; use rustc_middle::ty::subst::{Subst, SubstsRef}; @@ -400,6 +400,22 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let res = self.typeck_results.qpath_res(qpath, id); match res { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { + // Check if this constant is based on `cfg!(..)`, + // which is NOT constant for our purposes. + if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) && + let Node::Item(&Item { + kind: ItemKind::Const(_, body_id), + .. + }) = node && + let Node::Expr(&Expr { + kind: ExprKind::Lit(_), + span, + .. + }) = self.lcx.tcx.hir().get(body_id.hir_id) && + is_direct_expn_of(span, "cfg").is_some() { + return None; + } + let substs = self.typeck_results.node_substs(id); let substs = if self.substs.is_empty() { substs diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index c05317f59b7..f4da625f1e3 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant_context, constant_simple}; +use crate::consts::constant_simple; use crate::source::snippet_opt; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; @@ -16,15 +16,14 @@ use rustc_span::Symbol; use std::hash::{Hash, Hasher}; /// Type used to check whether two ast are the same. This is different from the -/// operator -/// `==` on ast types as this operator would compare true equality with ID and -/// span. +/// operator `==` on ast types as this operator would compare true equality with +/// ID and span. /// /// Note that some expressions kinds are not considered but could be added. pub struct SpanlessEq<'a, 'tcx> { /// Context used to evaluate constant expressions. cx: &'a LateContext<'tcx>, - maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, + maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>, allow_side_effects: bool, expr_fallback: Option<Box<dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a>>, } @@ -33,7 +32,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, - maybe_typeck_results: cx.maybe_typeck_results(), + maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)), allow_side_effects: true, expr_fallback: None, } @@ -102,9 +101,9 @@ impl HirEqInterExpr<'_, '_, '_> { (&StmtKind::Local(l), &StmtKind::Local(r)) => { // This additional check ensures that the type of the locals are equivalent even if the init // expression or type have some inferred parts. - if let Some(typeck) = self.inner.maybe_typeck_results { - let l_ty = typeck.pat_ty(l.pat); - let r_ty = typeck.pat_ty(r.pat); + if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { + let l_ty = typeck_lhs.pat_ty(l.pat); + let r_ty = typeck_rhs.pat_ty(r.pat); if l_ty != r_ty { return false; } @@ -182,9 +181,17 @@ impl HirEqInterExpr<'_, '_, '_> { } pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool { - let cx = self.inner.cx; - let eval_const = |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value); - eval_const(left) == eval_const(right) + // swap out TypeckResults when hashing a body + let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace(( + self.inner.cx.tcx.typeck_body(left), + self.inner.cx.tcx.typeck_body(right), + )); + let res = self.eq_expr( + &self.inner.cx.tcx.hir().body(left).value, + &self.inner.cx.tcx.hir().body(right).value, + ); + self.inner.maybe_typeck_results = old_maybe_typeck_results; + res } #[allow(clippy::similar_names)] @@ -193,10 +200,10 @@ impl HirEqInterExpr<'_, '_, '_> { return false; } - if let Some(typeck_results) = self.inner.maybe_typeck_results { + if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { if let (Some(l), Some(r)) = ( - constant_simple(self.inner.cx, typeck_results, left), - constant_simple(self.inner.cx, typeck_results, right), + constant_simple(self.inner.cx, typeck_lhs, left), + constant_simple(self.inner.cx, typeck_rhs, right), ) { if l == r { return true; @@ -674,8 +681,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(out_expr); } }, - InlineAsmOperand::Const { anon_const } => self.hash_body(anon_const.body), - InlineAsmOperand::SymFn { anon_const } => self.hash_body(anon_const.body), + InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => { + self.hash_body(anon_const.body); + }, InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path), } } diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index e7d4c5a4952..a268e339bb1 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -367,7 +367,7 @@ impl<'tcx> FormatArgsExpn<'tcx> { expr_visitor_no_bodies(|e| { // if we're still inside of the macro definition... if e.span.ctxt() == expr.span.ctxt() { - // ArgumnetV1::new_<format_trait>(<value>) + // ArgumentV1::new_<format_trait>(<value>) if_chain! { if let ExprKind::Call(callee, [val]) = e.kind; if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind; diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 0424e067202..134fd1ce505 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -32,4 +32,5 @@ msrv_aliases! { 1,28,0 { FROM_BOOL } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } + 1,24,0 { IS_ASCII_DIGIT } } diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs index 908ff822712..b92d42e8323 100644 --- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs +++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs @@ -57,7 +57,7 @@ impl<'a> NumericLiteral<'a> { .trim_start() .chars() .next() - .map_or(false, |c| c.is_digit(10)) + .map_or(false, |c| c.is_ascii_digit()) { let (unsuffixed, suffix) = split_suffix(src, lit_kind); let float = matches!(lit_kind, LitKind::Float(..)); diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index e5fa6deefc5..60971fb716d 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -61,6 +61,7 @@ pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; +pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; #[allow(clippy::invalid_paths)] // internal lints do not know about all external crates pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; @@ -148,6 +149,8 @@ pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; +pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; +pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"]; pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"]; diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index fe411220484..75808b1b174 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -211,8 +211,9 @@ fn check_statement<'tcx>( StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body), // just an assignment - StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => - check_place(tcx, **place, span, body), + StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { + check_place(tcx, **place, span, body) + }, StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => { check_operand(tcx, dst, span, body)?; diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index dbad607c58e..c69a3d8d2a1 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -7,9 +7,28 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::hygiene; +use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Pos, Span, SyntaxContext}; use std::borrow::Cow; +/// Checks if the span starts with the given text. This will return false if the span crosses +/// multiple files or if source is not available. +/// +/// This is used to check for proc macros giving unhelpful spans to things. +pub fn span_starts_with<T: LintContext>(cx: &T, span: Span, text: &str) -> bool { + fn helper(sm: &SourceMap, span: Span, text: &str) -> bool { + let pos = sm.lookup_byte_offset(span.lo()); + let Some(ref src) = pos.sf.src else { + return false; + }; + let end = span.hi() - pos.sf.start_pos; + src.get(pos.pos.0 as usize..end.0 as usize) + // Expression spans can include wrapping parenthesis. Remove them first. + .map_or(false, |s| s.trim_start_matches('(').starts_with(text)) + } + helper(cx.sess().source_map(), span, text) +} + /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. /// Also takes an `Option<String>` which can be put inside the braces. pub fn expr_block<'a, T: LintContext>( @@ -89,7 +108,7 @@ pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool { true } -/// Returns the positon just before rarrow +/// Returns the position just before rarrow /// /// ```rust,ignore /// fn into(self) -> () {} diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 794d2e1026f..18915553e61 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -18,7 +18,7 @@ use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext}; use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::borrow::Cow; use std::convert::TryInto; -use std::fmt::Display; +use std::fmt::{Display, Write as _}; use std::iter; use std::ops::{Add, Neg, Not, Sub}; @@ -902,7 +902,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { if cmt.place.projections.is_empty() { // handle item without any projection, that needs an explicit borrowing // i.e.: suggest `&x` instead of `x` - self.suggestion_start.push_str(&format!("{}&{}", start_snip, ident_str)); + let _ = write!(self.suggestion_start, "{}&{}", start_snip, ident_str); } else { // cases where a parent `Call` or `MethodCall` is using the item // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()` @@ -917,8 +917,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // given expression is the self argument and will be handled completely by the compiler // i.e.: `|x| x.is_something()` ExprKind::MethodCall(_, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => { - self.suggestion_start - .push_str(&format!("{}{}", start_snip, ident_str_with_proj)); + let _ = write!(self.suggestion_start, "{}{}", start_snip, ident_str_with_proj); self.next_pos = span.hi(); return; }, @@ -1026,8 +1025,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { } } - self.suggestion_start - .push_str(&format!("{}{}", start_snip, replacement_str)); + let _ = write!(self.suggestion_start, "{}{}", start_snip, replacement_str); } self.next_pos = span.hi(); } diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index e3fc76f4e1a..901e3e5390c 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -3,11 +3,11 @@ #![allow(clippy::module_name_repetitions)] use rustc_ast::ast::Mutability; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, TyKind, Unsafety}; +use rustc_hir::{Expr, LangItem, TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; @@ -22,7 +22,7 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::normalize::AtExt; use std::iter; -use crate::{match_def_path, must_use_attr, path_res}; +use crate::{match_def_path, must_use_attr, path_res, paths}; // Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { @@ -83,6 +83,20 @@ pub fn get_associated_type<'tcx>( }) } +/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type +/// implements a trait marked with a diagnostic item use [`implements_trait`]. +/// +/// For a further exploitation what diagnostic items are see [diagnostic items] in +/// rustc-dev-guide. +/// +/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html +pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> { + match ty.kind() { + ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()), + _ => None, + } +} + /// Returns true if ty has `iter` or `iter_mut` methods pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> { // FIXME: instead of this hard-coded list, we should check if `<adt>::iter` @@ -319,6 +333,57 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { } } +/// Checks if the drop order for a type matters. Some std types implement drop solely to +/// deallocate memory. For these types, and composites containing them, changing the drop order +/// won't result in any observable side effects. +pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool { + if !seen.insert(ty) { + return false; + } + if !ty.has_significant_drop(cx.tcx, cx.param_env) { + false + } + // Check for std types which implement drop, but only for memory allocation. + else if is_type_lang_item(cx, ty, LangItem::OwnedBox) + || matches!( + get_type_diagnostic_name(cx, ty), + Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type) + ) + || match_type(cx, ty, &paths::WEAK_RC) + || match_type(cx, ty, &paths::WEAK_ARC) + { + // Check all of the generic arguments. + if let ty::Adt(_, subs) = ty.kind() { + subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen)) + } else { + true + } + } else if !cx + .tcx + .lang_items() + .drop_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + // This type doesn't implement drop, so no side effects here. + // Check if any component type has any. + match ty.kind() { + ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)), + ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen), + ty::Adt(adt, subs) => adt + .all_fields() + .map(|f| f.ty(cx.tcx, subs)) + .any(|ty| needs_ordered_drop_inner(cx, ty, seen)), + _ => true, + } + } else { + true + } + } + + needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) +} + /// Peels off all references on the type. Returns the underlying type and the number of references /// removed. pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index 405e306359b..4236e3aae2f 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -3,7 +3,7 @@ use crate::visitors::{expr_visitor, expr_visitor_no_bodies}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirIdSet; -use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_hir::{Expr, ExprKind, HirId, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -169,6 +169,32 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool { let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false }; + + // for _ in 1..3 { + // local + // } + // + // let closure = || local; + // closure(); + // closure(); + let in_loop_or_closure = cx + .tcx + .hir() + .parent_iter(after.hir_id) + .take_while(|&(id, _)| id != block.hir_id) + .any(|(_, node)| { + matches!( + node, + Node::Expr(Expr { + kind: ExprKind::Loop(..) | ExprKind::Closure(..), + .. + }) + ) + }); + if in_loop_or_closure { + return true; + } + let mut used_after_expr = false; let mut past_expr = false; expr_visitor(cx, |expr| { @@ -178,7 +204,10 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr if expr.hir_id == after.hir_id { past_expr = true; - } else if past_expr && utils::path_to_local_id(expr, local_id) { + return false; + } + + if past_expr && utils::path_to_local_id(expr, local_id) { used_after_expr = true; } !used_after_expr diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 40451b17a9c..c00bc2bd213 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -1,9 +1,11 @@ use crate::path_to_local_id; +use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor}; use rustc_hir::{ - Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety, + Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, UnsafeSource, + Unsafety, }; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -370,3 +372,67 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { v.visit_expr(e); v.is_unsafe } + +/// Checks if the given expression contains an unsafe block +pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + found_unsafe: bool, + } + impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_block(&mut self, b: &'tcx Block<'_>) { + if self.found_unsafe { + return; + } + if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { + self.found_unsafe = true; + return; + } + walk_block(self, b); + } + } + let mut v = V { + cx, + found_unsafe: false, + }; + v.visit_expr(e); + v.found_unsafe +} + +/// Runs the given function for each sub-expression producing the final value consumed by the parent +/// of the give expression. +/// +/// e.g. for the following expression +/// ```rust,ignore +/// if foo { +/// f(0) +/// } else { +/// 1 + 1 +/// } +/// ``` +/// this will pass both `f(0)` and `1+1` to the given function. +pub fn for_each_value_source<'tcx, B>( + e: &'tcx Expr<'tcx>, + f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, +) -> ControlFlow<B> { + match e.kind { + ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f), + ExprKind::Match(_, arms, _) => { + for arm in arms { + for_each_value_source(arm.body, f)?; + } + ControlFlow::Continue(()) + }, + ExprKind::If(_, if_expr, Some(else_expr)) => { + for_each_value_source(if_expr, f)?; + for_each_value_source(else_expr, f) + }, + ExprKind::DropTemps(e) => for_each_value_source(e, f), + _ => f(e), + } +} diff --git a/src/tools/clippy/doc/adding_lints.md b/src/tools/clippy/doc/adding_lints.md index cf16a1d5d3d..307cf2f3a90 100644 --- a/src/tools/clippy/doc/adding_lints.md +++ b/src/tools/clippy/doc/adding_lints.md @@ -22,6 +22,7 @@ because that's clearly a non-descriptive name. - [Adding the lint logic](#adding-the-lint-logic) - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv) - [Author lint](#author-lint) + - [Print HIR lint](#print-hir-lint) - [Documentation](#documentation) - [Running rustfmt](#running-rustfmt) - [Debugging](#debugging) @@ -484,6 +485,19 @@ you are implementing your lint. [author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 +## Print HIR lint + +To implement a lint, it's helpful to first understand the internal representation +that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the +[_High-Level Intermediate Representation (HIR)_] of the item, statement, or +expression that the attribute is attached to. To attach the attribute to expressions +you often need to enable `#![feature(stmt_expr_attributes)]`. + +[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_. + +[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html +[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb + ## Documentation The final thing before submitting our PR is to add some documentation to our diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index 9af8dcc7726..816efbdaedf 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -8,6 +8,7 @@ #![allow(clippy::collapsible_else_if)] use std::ffi::OsStr; +use std::fmt::Write as _; use std::process::Command; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{collections::HashMap, io::ErrorKind}; @@ -110,11 +111,12 @@ impl ClippyWarning { let lint = format!("`{}`", self.linttype); let mut output = String::from("| "); - output.push_str(&format!( + let _ = write!( + output, "[`{}`](../target/lintcheck/sources/{}#L{})", file_with_pos, file, self.line - )); - output.push_str(&format!(r#" | {:<50} | "{}" |"#, lint, self.message)); + ); + let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message); output.push('\n'); output } else { @@ -304,7 +306,7 @@ impl Crate { let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); let mut args = if fix { - vec!["--fix", "--allow-no-vcs", "--"] + vec!["--fix", "--"] } else { vec!["--", "--message-format=json", "--"] }; @@ -391,7 +393,7 @@ struct LintcheckConfig { lintcheck_results_path: PathBuf, /// whether to just run --fix and not collect all the warnings fix: bool, - /// A list of lint that this lintcheck run shound focus on + /// A list of lints that this lintcheck run should focus on lint_filter: Vec<String>, /// Indicate if the output should support markdown syntax markdown: bool, @@ -835,10 +837,11 @@ pub fn main() { text.push_str("| file | lint | message |\n"); text.push_str("| --- | --- | --- |\n"); } - text.push_str(&format!("{}", all_msgs.join(""))); + write!(text, "{}", all_msgs.join("")); text.push_str("\n\n### ICEs:\n"); - ices.iter() - .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg))); + for (cratename, msg) in ices.iter() { + let _ = write!(text, "{}: '{}'", cratename, msg); + } println!("Writing logs to {}", config.lintcheck_results_path.display()); std::fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap(); diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index bb29c71e9f4..03acb51306d 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-04-07" +channel = "nightly-2022-05-05" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 32a09fdb9d9..7de40fe63ac 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -165,8 +165,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); - let fallback_bundle = - rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index 67af9d05bf4..eb97d1933d5 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -80,9 +80,13 @@ fn run_clippy_for_package(project: &str) { .args(&["-D", "clippy::pedantic"]) .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - // internal lints only exist if we build with the internal feature if cfg!(feature = "internal") { + // internal lints only exist if we build with the internal feature command.args(&["-D", "clippy::internal"]); + } else { + // running a clippy built without internal lints on the clippy source + // that contains e.g. `allow(clippy::invalid_paths)` + command.args(&["-A", "unknown_lints"]); } let output = command.output().unwrap(); diff --git a/src/tools/clippy/tests/lint_message_convention.rs b/src/tools/clippy/tests/lint_message_convention.rs index dc82ba891fb..dd1d4412036 100644 --- a/src/tools/clippy/tests/lint_message_convention.rs +++ b/src/tools/clippy/tests/lint_message_convention.rs @@ -66,7 +66,7 @@ fn lint_message_convention() { // make sure that lint messages: // * are not capitalized - // * don't have puncuation at the end of the last sentence + // * don't have punctuation at the end of the last sentence // these directories have interesting tests let test_dirs = ["ui", "ui-cargo", "ui-internal", "ui-toml"] diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr index 558d1299160..0852fe65aaf 100644 --- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr +++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -29,7 +29,7 @@ LL | | db.help(help_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` -error: this call is collspible +error: this call is collapsible --> $DIR/collapsible_span_lint_calls.rs:45:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { @@ -37,7 +37,7 @@ LL | | db.span_note(expr.span, note_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` -error: this call is collspible +error: this call is collapsible --> $DIR/collapsible_span_lint_calls.rs:48:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed index 6b7fd6efe39..eaea218e128 100644 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.fixed @@ -1,6 +1,6 @@ // run-rustfix #![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] #![feature(rustc_private)] extern crate rustc_span; diff --git a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs index 98d7d7adad1..7efebb8fae4 100644 --- a/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs +++ b/src/tools/clippy/tests/ui-internal/interning_defined_symbol.rs @@ -1,6 +1,6 @@ // run-rustfix #![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] #![feature(rustc_private)] extern crate rustc_span; diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs new file mode 100644 index 00000000000..fbef5c4564b --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs @@ -0,0 +1,41 @@ +#![warn(clippy::await_holding_invalid_type)] +use std::net::Ipv4Addr; + +async fn bad() -> u32 { + let _x = String::from("hello"); + baz().await +} + +async fn bad_reason() -> u32 { + let _x = Ipv4Addr::new(127, 0, 0, 1); + baz().await +} + +async fn good() -> u32 { + { + let _x = String::from("hi!"); + let _y = Ipv4Addr::new(127, 0, 0, 1); + } + baz().await; + let _x = String::from("hi!"); + 47 +} + +async fn baz() -> u32 { + 42 +} + +#[allow(clippy::manual_async_fn)] +fn block_bad() -> impl std::future::Future<Output = u32> { + async move { + let _x = String::from("hi!"); + baz().await + } +} + +fn main() { + good(); + bad(); + bad_reason(); + block_bad(); +} diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr new file mode 100644 index 00000000000..62c45b54634 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -0,0 +1,25 @@ +error: `std::string::String` may not be held across an `await` point per `clippy.toml` + --> $DIR/await_holding_invalid_type.rs:5:9 + | +LL | let _x = String::from("hello"); + | ^^ + | + = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings` + = note: strings are bad + +error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml` + --> $DIR/await_holding_invalid_type.rs:10:9 + | +LL | let _x = Ipv4Addr::new(127, 0, 0, 1); + | ^^ + +error: `std::string::String` may not be held across an `await` point per `clippy.toml` + --> $DIR/await_holding_invalid_type.rs:31:13 + | +LL | let _x = String::from("hi!"); + | ^^ + | + = note: strings are bad + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/clippy.toml b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/clippy.toml new file mode 100644 index 00000000000..79990096b84 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/clippy.toml @@ -0,0 +1,4 @@ +await-holding-invalid-types = [ + { path = "std::string::String", reason = "strings are bad" }, + "std::net::Ipv4Addr", +] diff --git a/src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs index e678c896fd3..4ac0378544c 100644 --- a/src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs +++ b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.rs @@ -1,4 +1,5 @@ #![warn(clippy::too_many_lines)] +#![allow(clippy::let_unit_value)] // This function should be considered one line. fn many_comments_but_one_line_of_code() { diff --git a/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr index d736bf89973..dc255bdcaba 100644 --- a/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr +++ b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr @@ -1,5 +1,5 @@ error: this function has too many lines (2/1) - --> $DIR/test.rs:18:1 + --> $DIR/test.rs:19:1 | LL | / fn too_many_lines() { LL | | println!("This is bad."); @@ -10,7 +10,7 @@ LL | | } = note: `-D clippy::too-many-lines` implied by `-D warnings` error: this function has too many lines (4/1) - --> $DIR/test.rs:24:1 + --> $DIR/test.rs:25:1 | LL | / async fn async_too_many_lines() { LL | | println!("This is bad."); @@ -19,7 +19,7 @@ LL | | } | |_^ error: this function has too many lines (4/1) - --> $DIR/test.rs:30:1 + --> $DIR/test.rs:31:1 | LL | / fn closure_too_many_lines() { LL | | let _ = { @@ -30,7 +30,7 @@ LL | | } | |_^ error: this function has too many lines (2/1) - --> $DIR/test.rs:52:1 + --> $DIR/test.rs:53:1 | LL | / fn comment_before_code() { LL | | let _ = "test"; diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/clippy.toml b/src/tools/clippy/tests/ui-toml/large_include_file/clippy.toml new file mode 100644 index 00000000000..ea34bf9fbe0 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/large_include_file/clippy.toml @@ -0,0 +1 @@ +max-include-file-size = 600 diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs new file mode 100644 index 00000000000..f3dbb6ad1cf --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs @@ -0,0 +1,16 @@ +#![warn(clippy::large_include_file)] + +// Good +const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs"); +const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs"); + +#[allow(clippy::large_include_file)] +const ALLOWED_TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); +#[allow(clippy::large_include_file)] +const ALLOWED_TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + +// Bad +const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); +const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr new file mode 100644 index 00000000000..6a685a58318 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr @@ -0,0 +1,21 @@ +error: attempted to include a large file + --> $DIR/large_include_file.rs:13:43 + | +LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::large-include-file` implied by `-D warnings` + = note: the configuration allows a maximum size of 600 bytes + = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: attempted to include a large file + --> $DIR/large_include_file.rs:14:35 + | +LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the configuration allows a maximum size of 600 bytes + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/too_big.txt b/src/tools/clippy/tests/ui-toml/large_include_file/too_big.txt new file mode 100644 index 00000000000..9829c46bc00 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/large_include_file/too_big.txt @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Maecenas accumsan lacus vel facilisis volutpat. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Tellus id interdum velit laoreet id donec ultrices. Est ultricies integer quis auctor elit sed vulputate. Erat velit scelerisque in dictum non consectetur a erat nam. Sed blandit libero volutpat sed. Tortor condimentum lacinia quis vel eros. Enim ut tellus elementum sagittis vitae et leo duis. Congue mauris rhoncus aenean vel elit scelerisque. Id consectetur purus ut faucibus pulvinar elementum integer. \ No newline at end of file diff --git a/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.stderr b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.stderr index a1e7361c0cb..5dae5af7eb5 100644 --- a/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.stderr +++ b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.stderr @@ -1,4 +1,4 @@ -error: you are using an explicit closure for copying elements +error: you are using an explicit closure for cloning elements --> $DIR/min_rust_version.rs:74:26 | LL | let _: Option<u64> = Some(&16).map(|b| *b); diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 00ddbd608a7..8701809b4da 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.rs b/src/tools/clippy/tests/ui/assertions_on_constants.rs index 7477c01ca78..7bea9563d47 100644 --- a/src/tools/clippy/tests/ui/assertions_on_constants.rs +++ b/src/tools/clippy/tests/ui/assertions_on_constants.rs @@ -1,4 +1,4 @@ -#![allow(non_fmt_panics)] +#![allow(non_fmt_panics, clippy::needless_bool)] macro_rules! assert_const { ($len:expr) => { @@ -28,6 +28,12 @@ fn main() { assert_const!(3); assert_const!(-1); - // Don't lint on this: + // Don't lint if based on `cfg!(..)`: assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); + + let flag: bool = cfg!(not(feature = "asdf")); + assert!(flag); + + const CFG_FLAG: &bool = &cfg!(feature = "hey"); + assert!(!CFG_FLAG); } diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs index 4b7b7fec78f..ed7b17651e6 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -13,7 +13,7 @@ use proc_macro::{quote, TokenStream}; #[proc_macro_derive(DeriveSomething)] pub fn derive(_: TokenStream) -> TokenStream { - // Shound not trigger `used_underscore_binding` + // Should not trigger `used_underscore_binding` let _inside_derive = 1; assert_eq!(_inside_derive, _inside_derive); diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs new file mode 100644 index 00000000000..8ea631f2bbd --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_with_span.rs @@ -0,0 +1,32 @@ +// compile-flags: --emit=link +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree}; + +#[proc_macro] +pub fn with_span(input: TokenStream) -> TokenStream { + let mut iter = input.into_iter(); + let span = iter.next().unwrap().span(); + let mut res = TokenStream::new(); + write_with_span(span, iter, &mut res); + res +} + +fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) { + for mut tt in input { + if let TokenTree::Group(g) = tt { + let mut stream = TokenStream::new(); + write_with_span(s, g.stream().into_iter(), &mut stream); + let mut group = Group::new(g.delimiter(), stream); + group.set_span(s); + out.extend([TokenTree::Group(group)]); + } else { + tt.set_span(s); + out.extend([tt]); + } + } +} diff --git a/src/tools/clippy/tests/ui/bytes_count_to_len.fixed b/src/tools/clippy/tests/ui/bytes_count_to_len.fixed new file mode 100644 index 00000000000..860642363b5 --- /dev/null +++ b/src/tools/clippy/tests/ui/bytes_count_to_len.fixed @@ -0,0 +1,34 @@ +// run-rustfix +#![warn(clippy::bytes_count_to_len)] +use std::fs::File; +use std::io::Read; + +fn main() { + // should fix, because type is String + let _ = String::from("foo").len(); + + let s1 = String::from("foo"); + let _ = s1.len(); + + // should fix, because type is &str + let _ = "foo".len(); + + let s2 = "foo"; + let _ = s2.len(); + + // make sure using count() normally doesn't trigger warning + let vector = [0, 1, 2]; + let _ = vector.iter().count(); + + // The type is slice, so should not fix + let _ = &[1, 2, 3].bytes().count(); + + let bytes: &[u8] = &[1, 2, 3]; + bytes.bytes().count(); + + // The type is File, so should not fix + let _ = File::open("foobar").unwrap().bytes().count(); + + let f = File::open("foobar").unwrap(); + let _ = f.bytes().count(); +} diff --git a/src/tools/clippy/tests/ui/bytes_count_to_len.rs b/src/tools/clippy/tests/ui/bytes_count_to_len.rs new file mode 100644 index 00000000000..162730c2842 --- /dev/null +++ b/src/tools/clippy/tests/ui/bytes_count_to_len.rs @@ -0,0 +1,34 @@ +// run-rustfix +#![warn(clippy::bytes_count_to_len)] +use std::fs::File; +use std::io::Read; + +fn main() { + // should fix, because type is String + let _ = String::from("foo").bytes().count(); + + let s1 = String::from("foo"); + let _ = s1.bytes().count(); + + // should fix, because type is &str + let _ = "foo".bytes().count(); + + let s2 = "foo"; + let _ = s2.bytes().count(); + + // make sure using count() normally doesn't trigger warning + let vector = [0, 1, 2]; + let _ = vector.iter().count(); + + // The type is slice, so should not fix + let _ = &[1, 2, 3].bytes().count(); + + let bytes: &[u8] = &[1, 2, 3]; + bytes.bytes().count(); + + // The type is File, so should not fix + let _ = File::open("foobar").unwrap().bytes().count(); + + let f = File::open("foobar").unwrap(); + let _ = f.bytes().count(); +} diff --git a/src/tools/clippy/tests/ui/bytes_count_to_len.stderr b/src/tools/clippy/tests/ui/bytes_count_to_len.stderr new file mode 100644 index 00000000000..224deb77987 --- /dev/null +++ b/src/tools/clippy/tests/ui/bytes_count_to_len.stderr @@ -0,0 +1,28 @@ +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:8:13 + | +LL | let _ = String::from("foo").bytes().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `String::from("foo").len()` + | + = note: `-D clippy::bytes-count-to-len` implied by `-D warnings` + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:11:13 + | +LL | let _ = s1.bytes().count(); + | ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s1.len()` + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:14:13 + | +LL | let _ = "foo".bytes().count(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `"foo".len()` + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:17:13 + | +LL | let _ = s2.bytes().count(); + | ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s2.len()` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs index cf85a5ca931..e6031e9adae 100644 --- a/src/tools/clippy/tests/ui/cast.rs +++ b/src/tools/clippy/tests/ui/cast.rs @@ -1,13 +1,13 @@ #![feature(repr128)] #![allow(incomplete_features)] - -#[warn( +#![warn( clippy::cast_precision_loss, clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_possible_wrap )] -#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)] + fn main() { // Test clippy::cast_precision_loss let x0 = 1i32; @@ -252,3 +252,11 @@ fn main() { } } } + +fn avoid_subtract_overflow(q: u32) { + let c = (q >> 16) as u8; + c as usize; + + let c = (q / 1000) as u8; + c as usize; +} diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr index 7a68c0984f1..0c63b4af308 100644 --- a/src/tools/clippy/tests/ui/cast.stderr +++ b/src/tools/clippy/tests/ui/cast.stderr @@ -194,5 +194,17 @@ error: casting `main::E10` to `u16` may truncate the value LL | let _ = self as u16; | ^^^^^^^^^^^ -error: aborting due to 31 previous errors +error: casting `u32` to `u8` may truncate the value + --> $DIR/cast.rs:257:13 + | +LL | let c = (q >> 16) as u8; + | ^^^^^^^^^^^^^^^ + +error: casting `u32` to `u8` may truncate the value + --> $DIR/cast.rs:260:13 + | +LL | let c = (q / 1000) as u8; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 33 previous errors diff --git a/src/tools/clippy/tests/ui/cast_alignment.rs b/src/tools/clippy/tests/ui/cast_alignment.rs index e4e7290a30e..95bb883df1b 100644 --- a/src/tools/clippy/tests/ui/cast_alignment.rs +++ b/src/tools/clippy/tests/ui/cast_alignment.rs @@ -44,8 +44,8 @@ fn main() { let _ = core::ptr::read_unaligned(ptr as *const u16); let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16); let ptr = &mut data as *mut [u8; 2] as *mut u8; - let _ = (ptr as *mut u16).write_unaligned(0); - let _ = core::ptr::write_unaligned(ptr as *mut u16, 0); - let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0); + (ptr as *mut u16).write_unaligned(0); + core::ptr::write_unaligned(ptr as *mut u16, 0); + core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0); } } diff --git a/src/tools/clippy/tests/ui/cast_slice_different_sizes.rs b/src/tools/clippy/tests/ui/cast_slice_different_sizes.rs index cfe1cca2eba..24d7eb28a19 100644 --- a/src/tools/clippy/tests/ui/cast_slice_different_sizes.rs +++ b/src/tools/clippy/tests/ui/cast_slice_different_sizes.rs @@ -1,3 +1,5 @@ +#![allow(clippy::let_unit_value)] + fn main() { let x: [i32; 3] = [1_i32, 2, 3]; let r_x = &x; @@ -37,3 +39,44 @@ fn main() { let long_chain_restore = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8] as *const [u32]; } + +// foo and foo2 should not fire, they're the same size +fn foo(x: *mut [u8]) -> *mut [u8] { + x as *mut [u8] +} + +fn foo2(x: *mut [u8]) -> *mut [u8] { + x as *mut _ +} + +// Test that casts as part of function returns work +fn bar(x: *mut [u16]) -> *mut [u8] { + x as *mut [u8] +} + +fn uwu(x: *mut [u16]) -> *mut [u8] { + x as *mut _ +} + +fn bar2(x: *mut [u16]) -> *mut [u8] { + x as _ +} + +// constify +fn bar3(x: *mut [u16]) -> *const [u8] { + x as _ +} + +// unconstify +fn bar4(x: *const [u16]) -> *mut [u8] { + x as _ +} + +// function returns plus blocks +fn blocks(x: *mut [u16]) -> *mut [u8] { + ({ x }) as _ +} + +fn more_blocks(x: *mut [u16]) -> *mut [u8] { + { ({ x }) as _ } +} diff --git a/src/tools/clippy/tests/ui/cast_slice_different_sizes.stderr b/src/tools/clippy/tests/ui/cast_slice_different_sizes.stderr index a37cec7cb3b..40721dcd05d 100644 --- a/src/tools/clippy/tests/ui/cast_slice_different_sizes.stderr +++ b/src/tools/clippy/tests/ui/cast_slice_different_sizes.stderr @@ -1,5 +1,5 @@ error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:7:13 + --> $DIR/cast_slice_different_sizes.rs:9:13 | LL | let b = a as *const [u8]; | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(a as *const u8, ..)` @@ -7,25 +7,25 @@ LL | let b = a as *const [u8]; = note: `#[deny(clippy::cast_slice_different_sizes)]` on by default error: casting between raw pointers to `[u8]` (element size 1) and `[u32]` (element size 4) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:8:13 + --> $DIR/cast_slice_different_sizes.rs:10:13 | LL | let c = b as *const [u32]; | ^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(b as *const u32, ..)` error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:11:16 + --> $DIR/cast_slice_different_sizes.rs:13:16 | LL | let loss = r_x as *const [i32] as *const [u8]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)` error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:18:24 + --> $DIR/cast_slice_different_sizes.rs:20:24 | LL | let loss_block_1 = { r_x as *const [i32] } as *const [u8]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts({ r_x as *const [i32] } as *const u8, ..)` error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:19:24 + --> $DIR/cast_slice_different_sizes.rs:21:24 | LL | let loss_block_2 = { | ________________________^ @@ -43,10 +43,79 @@ LL ~ } as *const u8, ..); | error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:36:27 + --> $DIR/cast_slice_different_sizes.rs:38:27 | LL | let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const u8, ..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)` -error: aborting due to 6 previous errors +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:53:36 + | +LL | fn bar(x: *mut [u16]) -> *mut [u8] { + | ____________________________________^ +LL | | x as *mut [u8] +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:57:36 + | +LL | fn uwu(x: *mut [u16]) -> *mut [u8] { + | ____________________________________^ +LL | | x as *mut _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:61:37 + | +LL | fn bar2(x: *mut [u16]) -> *mut [u8] { + | _____________________________________^ +LL | | x as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:66:39 + | +LL | fn bar3(x: *mut [u16]) -> *const [u8] { + | _______________________________________^ +LL | | x as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(x as *const u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:71:39 + | +LL | fn bar4(x: *const [u16]) -> *mut [u8] { + | _______________________________________^ +LL | | x as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:76:39 + | +LL | fn blocks(x: *mut [u16]) -> *mut [u8] { + | _______________________________________^ +LL | | ({ x }) as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:80:44 + | +LL | fn more_blocks(x: *mut [u16]) -> *mut [u8] { + | ____________________________________________^ +LL | | { ({ x }) as _ } +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:81:5 + | +LL | { ({ x }) as _ } + | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.fixed b/src/tools/clippy/tests/ui/collapsible_else_if.fixed index bb6c4c0703d..d6a5a785067 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.fixed +++ b/src/tools/clippy/tests/ui/collapsible_else_if.fixed @@ -75,3 +75,10 @@ fn main() { } } } + +#[rustfmt::skip] +#[allow(dead_code)] +fn issue_7318() { + if true { println!("I've been resolved!") + }else if false {} +} diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.rs b/src/tools/clippy/tests/ui/collapsible_else_if.rs index 6d4f688db8c..4399fc8b2bd 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.rs +++ b/src/tools/clippy/tests/ui/collapsible_else_if.rs @@ -89,3 +89,12 @@ fn main() { } } } + +#[rustfmt::skip] +#[allow(dead_code)] +fn issue_7318() { + if true { println!("I've been resolved!") + }else{ + if false {} + } +} diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.stderr b/src/tools/clippy/tests/ui/collapsible_else_if.stderr index 6970f660979..45b2094c994 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.stderr +++ b/src/tools/clippy/tests/ui/collapsible_else_if.stderr @@ -150,5 +150,14 @@ LL + println!("!") LL + } | -error: aborting due to 7 previous errors +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:97:10 + | +LL | }else{ + | __________^ +LL | | if false {} +LL | | } + | |_____^ help: collapse nested if block: `if false {}` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/auxiliary/ice-8681-aux.rs b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-8681-aux.rs new file mode 100644 index 00000000000..95b63151325 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/auxiliary/ice-8681-aux.rs @@ -0,0 +1,6 @@ +pub fn foo(x: &u32) -> u32 { + /* Safety: + * This is totally ok. + */ + unsafe { *(x as *const u32) } +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2865.rs b/src/tools/clippy/tests/ui/crashes/ice-2865.rs index 6b1ceb50569..c6298139601 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-2865.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-2865.rs @@ -1,4 +1,4 @@ -#[allow(dead_code)] +#![allow(dead_code, clippy::extra_unused_lifetimes)] /// Test for https://github.com/rust-lang/rust-clippy/issues/2865 diff --git a/src/tools/clippy/tests/ui/crashes/ice-3151.rs b/src/tools/clippy/tests/ui/crashes/ice-3151.rs index fef4d7db84d..268ba86fc7a 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-3151.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-3151.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/2865 +/// Test for https://github.com/rust-lang/rust-clippy/issues/3151 #[derive(Clone)] pub struct HashMap<V, S> { diff --git a/src/tools/clippy/tests/ui/crashes/ice-5944.rs b/src/tools/clippy/tests/ui/crashes/ice-5944.rs index 5caf29c6197..ce46bc1acc1 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-5944.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-5944.rs @@ -1,4 +1,5 @@ #![warn(clippy::repeat_once)] +#![allow(clippy::let_unit_value)] trait Repeat { fn repeat(&self) {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-8250.stderr b/src/tools/clippy/tests/ui/crashes/ice-8250.stderr index 04ea4456656..8ed8f3b3a06 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-8250.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-8250.stderr @@ -1,11 +1,3 @@ -error: manual implementation of `split_once` - --> $DIR/ice-8250.rs:2:13 - | -LL | let _ = s[1..].splitn(2, '.').next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split_once('.').map_or(s[1..], |x| x.0)` - | - = note: `-D clippy::manual-split-once` implied by `-D warnings` - error: unnecessary use of `splitn` --> $DIR/ice-8250.rs:2:13 | @@ -14,5 +6,5 @@ LL | let _ = s[1..].splitn(2, '.').next()?; | = note: `-D clippy::needless-splitn` implied by `-D warnings` -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/crashes/ice-8681.rs b/src/tools/clippy/tests/ui/crashes/ice-8681.rs new file mode 100644 index 00000000000..ee14f011f63 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-8681.rs @@ -0,0 +1,10 @@ +// aux-build: ice-8681-aux.rs + +#![warn(clippy::undocumented_unsafe_blocks)] + +#[path = "auxiliary/ice-8681-aux.rs"] +mod ice_8681_aux; + +fn main() { + let _ = ice_8681_aux::foo(&0u32); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-96721.rs b/src/tools/clippy/tests/ui/crashes/ice-96721.rs new file mode 100644 index 00000000000..4b3fb764010 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-96721.rs @@ -0,0 +1,10 @@ +macro_rules! foo { + () => { + "bar.rs" + }; +} + +#[path = foo!()] //~ ERROR malformed `path` attribute +mod abc {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-96721.stderr b/src/tools/clippy/tests/ui/crashes/ice-96721.stderr new file mode 100644 index 00000000000..78c567b8e77 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-96721.stderr @@ -0,0 +1,8 @@ +error: malformed `path` attribute input + --> $DIR/ice-96721.rs:7:1 + | +LL | #[path = foo!()] //~ ERROR malformed `path` attribute + | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed index e0b4a2f6942..a28bff76755 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed @@ -2,12 +2,15 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] -#![allow(clippy::match_single_binding)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::match_single_binding, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs index 50bbb6eec6c..b48435cc7b2 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs @@ -2,12 +2,15 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] -#![allow(clippy::match_single_binding)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::match_single_binding, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr index f8a2407b693..f8b6c7746ed 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:18:17 + --> $DIR/default_numeric_fallback_f64.rs:21:17 | LL | let x = 0.12; | ^^^^ help: consider adding suffix: `0.12_f64` @@ -7,133 +7,133 @@ LL | let x = 0.12; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:19:18 + --> $DIR/default_numeric_fallback_f64.rs:22:18 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:19:22 + --> $DIR/default_numeric_fallback_f64.rs:22:22 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `2.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:19:26 + --> $DIR/default_numeric_fallback_f64.rs:22:26 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `3.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:28 + --> $DIR/default_numeric_fallback_f64.rs:23:28 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:32 + --> $DIR/default_numeric_fallback_f64.rs:23:32 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `2.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:46 + --> $DIR/default_numeric_fallback_f64.rs:23:46 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `3.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:50 + --> $DIR/default_numeric_fallback_f64.rs:23:50 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `4.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:21:23 + --> $DIR/default_numeric_fallback_f64.rs:24:23 | LL | let x = match 1. { | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:22:18 + --> $DIR/default_numeric_fallback_f64.rs:25:18 | LL | _ => 1., | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:40:21 + --> $DIR/default_numeric_fallback_f64.rs:43:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:48:21 + --> $DIR/default_numeric_fallback_f64.rs:51:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:54:21 + --> $DIR/default_numeric_fallback_f64.rs:57:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:66:9 + --> $DIR/default_numeric_fallback_f64.rs:69:9 | LL | 1. | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:72:27 + --> $DIR/default_numeric_fallback_f64.rs:75:27 | LL | let f = || -> _ { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:76:29 + --> $DIR/default_numeric_fallback_f64.rs:79:29 | LL | let f = || -> f64 { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:90:21 + --> $DIR/default_numeric_fallback_f64.rs:93:21 | LL | generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:93:32 + --> $DIR/default_numeric_fallback_f64.rs:96:32 | LL | let x: _ = generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:111:28 + --> $DIR/default_numeric_fallback_f64.rs:114:28 | LL | GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:114:36 + --> $DIR/default_numeric_fallback_f64.rs:117:36 | LL | let _ = GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:132:24 + --> $DIR/default_numeric_fallback_f64.rs:135:24 | LL | GenericEnum::X(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:152:23 + --> $DIR/default_numeric_fallback_f64.rs:155:23 | LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:159:21 + --> $DIR/default_numeric_fallback_f64.rs:162:21 | LL | let x = 22.; | ^^^ help: consider adding suffix: `22.0_f64` diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed index bded9e2c0e8..fa85d278c8f 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed @@ -2,11 +2,14 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs index 3fceefa551c..71acccd702b 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs @@ -2,11 +2,14 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr index 6f9e124704b..3cc84ff1132 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:17:17 + --> $DIR/default_numeric_fallback_i32.rs:20:17 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -7,145 +7,145 @@ LL | let x = 22; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:18:18 + --> $DIR/default_numeric_fallback_i32.rs:21:18 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:18:21 + --> $DIR/default_numeric_fallback_i32.rs:21:21 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:18:24 + --> $DIR/default_numeric_fallback_i32.rs:21:24 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:28 + --> $DIR/default_numeric_fallback_i32.rs:22:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:31 + --> $DIR/default_numeric_fallback_i32.rs:22:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:44 + --> $DIR/default_numeric_fallback_i32.rs:22:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:47 + --> $DIR/default_numeric_fallback_i32.rs:22:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:20:23 + --> $DIR/default_numeric_fallback_i32.rs:23:23 | LL | let x = match 1 { | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:13 + --> $DIR/default_numeric_fallback_i32.rs:24:13 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:18 + --> $DIR/default_numeric_fallback_i32.rs:24:18 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:18 + --> $DIR/default_numeric_fallback_i32.rs:25:18 | LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:39:21 + --> $DIR/default_numeric_fallback_i32.rs:42:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:47:21 + --> $DIR/default_numeric_fallback_i32.rs:50:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:53:21 + --> $DIR/default_numeric_fallback_i32.rs:56:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:65:9 + --> $DIR/default_numeric_fallback_i32.rs:68:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:71:27 + --> $DIR/default_numeric_fallback_i32.rs:74:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:75:29 + --> $DIR/default_numeric_fallback_i32.rs:78:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:89:21 + --> $DIR/default_numeric_fallback_i32.rs:92:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:92:32 + --> $DIR/default_numeric_fallback_i32.rs:95:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:110:28 + --> $DIR/default_numeric_fallback_i32.rs:113:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:113:36 + --> $DIR/default_numeric_fallback_i32.rs:116:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:131:24 + --> $DIR/default_numeric_fallback_i32.rs:134:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:151:23 + --> $DIR/default_numeric_fallback_i32.rs:154:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:158:21 + --> $DIR/default_numeric_fallback_i32.rs:161:21 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs index 39a2601fee9..07270bd7636 100644 --- a/src/tools/clippy/tests/ui/deprecated.rs +++ b/src/tools/clippy/tests/ui/deprecated.rs @@ -1,3 +1,7 @@ +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + #![warn(clippy::should_assert_eq)] #![warn(clippy::extend_from_slice)] #![warn(clippy::range_step_by_zero)] diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr index 6095f134d55..0e142ac8f20 100644 --- a/src/tools/clippy/tests/ui/deprecated.stderr +++ b/src/tools/clippy/tests/ui/deprecated.stderr @@ -1,5 +1,5 @@ error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011 - --> $DIR/deprecated.rs:1:9 + --> $DIR/deprecated.rs:5:9 | LL | #![warn(clippy::should_assert_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,91 +7,91 @@ LL | #![warn(clippy::should_assert_eq)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice - --> $DIR/deprecated.rs:2:9 + --> $DIR/deprecated.rs:6:9 | LL | #![warn(clippy::extend_from_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays - --> $DIR/deprecated.rs:3:9 + --> $DIR/deprecated.rs:7:9 | LL | #![warn(clippy::range_step_by_zero)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7 - --> $DIR/deprecated.rs:4:9 + --> $DIR/deprecated.rs:8:9 | LL | #![warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7 - --> $DIR/deprecated.rs:5:9 + --> $DIR/deprecated.rs:9:9 | LL | #![warn(clippy::unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr - --> $DIR/deprecated.rs:6:9 + --> $DIR/deprecated.rs:10:9 | LL | #![warn(clippy::misaligned_transmute)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless - --> $DIR/deprecated.rs:7:9 + --> $DIR/deprecated.rs:11:9 | LL | #![warn(clippy::assign_ops)] | ^^^^^^^^^^^^^^^^^^ error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching - --> $DIR/deprecated.rs:8:9 + --> $DIR/deprecated.rs:12:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior - --> $DIR/deprecated.rs:9:9 + --> $DIR/deprecated.rs:13:9 | LL | #![warn(clippy::unsafe_vector_initialization)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint - --> $DIR/deprecated.rs:10:9 + --> $DIR/deprecated.rs:14:9 | LL | #![warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants - --> $DIR/deprecated.rs:11:9 + --> $DIR/deprecated.rs:15:9 | LL | #![warn(clippy::replace_consts)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018 - --> $DIR/deprecated.rs:12:9 + --> $DIR/deprecated.rs:16:9 | LL | #![warn(clippy::regex_macro)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint - --> $DIR/deprecated.rs:13:9 + --> $DIR/deprecated.rs:17:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint - --> $DIR/deprecated.rs:14:9 + --> $DIR/deprecated.rs:18:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items - --> $DIR/deprecated.rs:15:9 + --> $DIR/deprecated.rs:19:9 | LL | #![warn(clippy::pub_enum_variant_names)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items - --> $DIR/deprecated.rs:16:9 + --> $DIR/deprecated.rs:20:9 | LL | #![warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/doc_unsafe.rs b/src/tools/clippy/tests/ui/doc_unsafe.rs index 4464a21b3b6..b91f7aa0dd8 100644 --- a/src/tools/clippy/tests/ui/doc_unsafe.rs +++ b/src/tools/clippy/tests/ui/doc_unsafe.rs @@ -1,5 +1,7 @@ // aux-build:doc_unsafe_macros.rs +#![allow(clippy::let_unit_value)] + #[macro_use] extern crate doc_unsafe_macros; diff --git a/src/tools/clippy/tests/ui/doc_unsafe.stderr b/src/tools/clippy/tests/ui/doc_unsafe.stderr index d68b8a0c67b..904b88eaef6 100644 --- a/src/tools/clippy/tests/ui/doc_unsafe.stderr +++ b/src/tools/clippy/tests/ui/doc_unsafe.stderr @@ -1,5 +1,5 @@ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:7:1 + --> $DIR/doc_unsafe.rs:9:1 | LL | / pub unsafe fn destroy_the_planet() { LL | | unimplemented!(); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-safety-doc` implied by `-D warnings` error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:30:5 + --> $DIR/doc_unsafe.rs:32:5 | LL | / pub unsafe fn republished() { LL | | unimplemented!(); @@ -17,13 +17,13 @@ LL | | } | |_____^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:38:5 + --> $DIR/doc_unsafe.rs:40:5 | LL | unsafe fn woefully_underdocumented(self); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for unsafe trait missing `# Safety` section - --> $DIR/doc_unsafe.rs:44:1 + --> $DIR/doc_unsafe.rs:46:1 | LL | / pub unsafe trait UnsafeTrait { LL | | fn method(); @@ -31,7 +31,7 @@ LL | | } | |_^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:74:5 + --> $DIR/doc_unsafe.rs:76:5 | LL | / pub unsafe fn more_undocumented_unsafe() -> Self { LL | | unimplemented!(); @@ -39,7 +39,7 @@ LL | | } | |_____^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:90:9 + --> $DIR/doc_unsafe.rs:92:9 | LL | / pub unsafe fn whee() { LL | | unimplemented!() diff --git a/src/tools/clippy/tests/ui/drop_non_drop.stderr b/src/tools/clippy/tests/ui/drop_non_drop.stderr index f73068901c5..30121033de7 100644 --- a/src/tools/clippy/tests/ui/drop_non_drop.stderr +++ b/src/tools/clippy/tests/ui/drop_non_drop.stderr @@ -1,4 +1,4 @@ -error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes --> $DIR/drop_non_drop.rs:22:5 | LL | drop(Foo); @@ -11,7 +11,7 @@ note: argument has type `main::Foo` LL | drop(Foo); | ^^^ -error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes --> $DIR/drop_non_drop.rs:37:5 | LL | drop(Baz(Foo)); diff --git a/src/tools/clippy/tests/ui/empty_drop.fixed b/src/tools/clippy/tests/ui/empty_drop.fixed new file mode 100644 index 00000000000..2e1b768461a --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_drop.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::empty_drop)] +#![allow(unused)] + +// should cause an error +struct Foo; + + + +// shouldn't cause an error +struct Bar; + +impl Drop for Bar { + fn drop(&mut self) { + println!("dropping bar!"); + } +} + +// should error +struct Baz; + + + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_drop.rs b/src/tools/clippy/tests/ui/empty_drop.rs new file mode 100644 index 00000000000..75232b0334d --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_drop.rs @@ -0,0 +1,30 @@ +// run-rustfix +#![warn(clippy::empty_drop)] +#![allow(unused)] + +// should cause an error +struct Foo; + +impl Drop for Foo { + fn drop(&mut self) {} +} + +// shouldn't cause an error +struct Bar; + +impl Drop for Bar { + fn drop(&mut self) { + println!("dropping bar!"); + } +} + +// should error +struct Baz; + +impl Drop for Baz { + fn drop(&mut self) { + {} + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_drop.stderr b/src/tools/clippy/tests/ui/empty_drop.stderr new file mode 100644 index 00000000000..70f7880d036 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_drop.stderr @@ -0,0 +1,22 @@ +error: empty drop implementation + --> $DIR/empty_drop.rs:8:1 + | +LL | / impl Drop for Foo { +LL | | fn drop(&mut self) {} +LL | | } + | |_^ help: try removing this impl + | + = note: `-D clippy::empty-drop` implied by `-D warnings` + +error: empty drop implementation + --> $DIR/empty_drop.rs:24:1 + | +LL | / impl Drop for Baz { +LL | | fn drop(&mut self) { +LL | | {} +LL | | } +LL | | } + | |_^ help: try removing this impl + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed index 5aedbea381f..6c2272f4dff 100644 --- a/src/tools/clippy/tests/ui/eta.fixed +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -37,7 +37,7 @@ fn main() { } // See #815 - let e = Some(1u8).map(divergent); + let e = Some(1u8).map(|a| divergent(a)); let e = Some(1u8).map(generic); let e = Some(1u8).map(generic); // See #515 @@ -211,6 +211,10 @@ fn mutable_closure_in_loop() { let mut closure = |n| value += n; for _ in 0..5 { Some(1).map(&mut closure); + + let mut value = 0; + let mut in_loop = |n| value += n; + Some(1).map(in_loop); } } @@ -229,7 +233,7 @@ fn late_bound_lifetimes() { { } map_str(|s| take_asref_path(s)); - map_str_to_path(std::convert::AsRef::as_ref); + map_str_to_path(|s| s.as_ref()); } mod type_param_bound { @@ -275,3 +279,15 @@ mod bind_by_ref { Some(A).map(|ref a| B::from(a)); } } + +// #7812 False positive on coerced closure +fn coerced_closure() { + fn function_returning_unit<F: FnMut(i32)>(f: F) {} + function_returning_unit(|x| std::process::exit(x)); + + fn arr() -> &'static [u8; 0] { + &[] + } + fn slice_fn(_: impl FnOnce() -> &'static [u8]) {} + slice_fn(|| arr()); +} diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs index 5fdf7fb9771..a1a9c0dfbf3 100644 --- a/src/tools/clippy/tests/ui/eta.rs +++ b/src/tools/clippy/tests/ui/eta.rs @@ -211,6 +211,10 @@ fn mutable_closure_in_loop() { let mut closure = |n| value += n; for _ in 0..5 { Some(1).map(|n| closure(n)); + + let mut value = 0; + let mut in_loop = |n| value += n; + Some(1).map(|n| in_loop(n)); } } @@ -275,3 +279,15 @@ mod bind_by_ref { Some(A).map(|ref a| B::from(a)); } } + +// #7812 False positive on coerced closure +fn coerced_closure() { + fn function_returning_unit<F: FnMut(i32)>(f: F) {} + function_returning_unit(|x| std::process::exit(x)); + + fn arr() -> &'static [u8; 0] { + &[] + } + fn slice_fn(_: impl FnOnce() -> &'static [u8]) {} + slice_fn(|| arr()); +} diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr index cda84982c9b..bf2e97e744a 100644 --- a/src/tools/clippy/tests/ui/eta.stderr +++ b/src/tools/clippy/tests/ui/eta.stderr @@ -25,12 +25,6 @@ LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` error: redundant closure - --> $DIR/eta.rs:40:27 - | -LL | let e = Some(1u8).map(|a| divergent(a)); - | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent` - -error: redundant closure --> $DIR/eta.rs:41:27 | LL | let e = Some(1u8).map(|a| generic(a)); @@ -117,10 +111,10 @@ LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> $DIR/eta.rs:232:21 + --> $DIR/eta.rs:217:21 | -LL | map_str_to_path(|s| s.as_ref()); - | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref` +LL | Some(1).map(|n| in_loop(n)); + | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` -error: aborting due to 20 previous errors +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs index 150acfbfee7..f76127a7105 100644 --- a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs +++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs @@ -72,4 +72,46 @@ mod issue4291 { } } +mod issue6437 { + pub struct Scalar; + + impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar { + fn add_assign(&mut self, _rhs: &Scalar) { + unimplemented!(); + } + } + + impl<'b> Scalar { + pub fn something<'c>() -> Self { + Self + } + } +} + +// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213 +mod first_case { + use serde::de::Visitor; + pub trait Expected { + fn fmt(&self, formatter: &mut std::fmt::Formatter); + } + + impl<'de, T> Expected for T + where + T: Visitor<'de>, + { + fn fmt(&self, formatter: &mut std::fmt::Formatter) {} + } +} + +// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213 +mod second_case { + pub trait Source { + fn hey(); + } + + impl<'a, T: Source + ?Sized + 'a> Source for Box<T> { + fn hey() {} + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr index 9143fb2c208..fcc12d4ce14 100644 --- a/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr +++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr @@ -18,5 +18,23 @@ error: this lifetime isn't used in the function definition LL | fn unused_lt<'a>(x: u8) {} | ^^ -error: aborting due to 3 previous errors +error: this lifetime isn't used in the impl + --> $DIR/extra_unused_lifetimes.rs:78:10 + | +LL | impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar { + | ^^ + +error: this lifetime isn't used in the impl + --> $DIR/extra_unused_lifetimes.rs:84:10 + | +LL | impl<'b> Scalar { + | ^^ + +error: this lifetime isn't used in the function definition + --> $DIR/extra_unused_lifetimes.rs:85:26 + | +LL | pub fn something<'c>() -> Self { + | ^^ + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/format_push_string.rs b/src/tools/clippy/tests/ui/format_push_string.rs new file mode 100644 index 00000000000..4db13d650eb --- /dev/null +++ b/src/tools/clippy/tests/ui/format_push_string.rs @@ -0,0 +1,7 @@ +#![warn(clippy::format_push_string)] + +fn main() { + let mut string = String::new(); + string += &format!("{:?}", 1234); + string.push_str(&format!("{:?}", 5678)); +} diff --git a/src/tools/clippy/tests/ui/format_push_string.stderr b/src/tools/clippy/tests/ui/format_push_string.stderr new file mode 100644 index 00000000000..953784bcc06 --- /dev/null +++ b/src/tools/clippy/tests/ui/format_push_string.stderr @@ -0,0 +1,19 @@ +error: `format!(..)` appended to existing `String` + --> $DIR/format_push_string.rs:5:5 + | +LL | string += &format!("{:?}", 1234); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::format-push-string` implied by `-D warnings` + = help: consider using `write!` to avoid the extra allocation + +error: `format!(..)` appended to existing `String` + --> $DIR/format_push_string.rs:6:5 + | +LL | string.push_str(&format!("{:?}", 5678)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `write!` to avoid the extra allocation + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/identity_op.rs b/src/tools/clippy/tests/ui/identity_op.rs index edc3fe1aec1..fec54d00ccb 100644 --- a/src/tools/clippy/tests/ui/identity_op.rs +++ b/src/tools/clippy/tests/ui/identity_op.rs @@ -1,3 +1,5 @@ +use std::fmt::Write as _; + const ONE: i64 = 1; const NEG_ONE: i64 = -1; const ZERO: i64 = 0; @@ -7,7 +9,7 @@ struct A(String); impl std::ops::Shl<i32> for A { type Output = A; fn shl(mut self, other: i32) -> Self { - self.0.push_str(&format!("{}", other)); + let _ = write!(self.0, "{}", other); self } } @@ -75,4 +77,34 @@ fn main() { (x + 1) % 3; // no error 4 % 3; // no error 4 % -3; // no error + + // See #8724 + let a = 0; + let b = true; + 0 + if b { 1 } else { 2 }; + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error + 0 + match a { 0 => 10, _ => 20 }; + 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error + 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error + 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error + + 0 + if b { 0 + 1 } else { 2 }; + 0 + match a { 0 => 0 + 10, _ => 20 }; + 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + + let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; + + 0 + { a } + 3; // no error + 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error + + fn f(_: i32) { + todo!(); + } + f(1 * a + { 8 * 5 }); + f(0 + if b { 1 } else { 2 } + 3); // no error + const _: i32 = { 2 * 4 } + 0 + 3; + const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error } diff --git a/src/tools/clippy/tests/ui/identity_op.stderr b/src/tools/clippy/tests/ui/identity_op.stderr index 706f01a3dd6..d8cb65839cb 100644 --- a/src/tools/clippy/tests/ui/identity_op.stderr +++ b/src/tools/clippy/tests/ui/identity_op.stderr @@ -1,5 +1,5 @@ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:37:5 + --> $DIR/identity_op.rs:39:5 | LL | x + 0; | ^^^^^ @@ -7,106 +7,196 @@ LL | x + 0; = note: `-D clippy::identity-op` implied by `-D warnings` error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:38:5 + --> $DIR/identity_op.rs:40:5 | LL | x + (1 - 1); | ^^^^^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:40:5 + --> $DIR/identity_op.rs:42:5 | LL | 0 + x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:43:5 + --> $DIR/identity_op.rs:45:5 | LL | x | (0); | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:46:5 + --> $DIR/identity_op.rs:48:5 | LL | x * 1; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:47:5 + --> $DIR/identity_op.rs:49:5 | LL | 1 * x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:53:5 + --> $DIR/identity_op.rs:55:5 | LL | -1 & x; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `u` - --> $DIR/identity_op.rs:56:5 + --> $DIR/identity_op.rs:58:5 | LL | u & 255; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:59:5 + --> $DIR/identity_op.rs:61:5 | LL | 42 << 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:60:5 + --> $DIR/identity_op.rs:62:5 | LL | 1 >> 0; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:61:5 + --> $DIR/identity_op.rs:63:5 | LL | 42 >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `&x` - --> $DIR/identity_op.rs:62:5 + --> $DIR/identity_op.rs:64:5 | LL | &x >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:63:5 + --> $DIR/identity_op.rs:65:5 | LL | x >> &0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:70:5 + --> $DIR/identity_op.rs:72:5 | LL | 2 % 3; | ^^^^^ error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:71:5 + --> $DIR/identity_op.rs:73:5 | LL | -2 % 3; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:72:5 + --> $DIR/identity_op.rs:74:5 | LL | 2 % -3 + x; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:73:5 + --> $DIR/identity_op.rs:75:5 | LL | -2 % -3 + x; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:74:9 + --> $DIR/identity_op.rs:76:9 | LL | x + 1 % 3; | ^^^^^ -error: aborting due to 18 previous errors +error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }` + --> $DIR/identity_op.rs:84:5 + | +LL | 0 + if b { 1 } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }` + --> $DIR/identity_op.rs:86:5 + | +LL | 0 + match a { 0 => 10, _ => 20 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }` + --> $DIR/identity_op.rs:91:5 + | +LL | 0 + if b { 0 + 1 } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:91:16 + | +LL | 0 + if b { 0 + 1 } else { 2 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `match a { 0 => 0 + 10, _ => 20 }` + --> $DIR/identity_op.rs:92:5 + | +LL | 0 + match a { 0 => 0 + 10, _ => 20 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `10` + --> $DIR/identity_op.rs:92:25 + | +LL | 0 + match a { 0 => 0 + 10, _ => 20 }; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:93:16 + | +LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `30` + --> $DIR/identity_op.rs:93:52 + | +LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:95:20 + | +LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:95:52 + | +LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:96:23 + | +LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:96:58 + | +LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` + --> $DIR/identity_op.rs:98:5 + | +LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `a` + --> $DIR/identity_op.rs:106:7 + | +LL | f(1 * a + { 8 * 5 }); + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }` + --> $DIR/identity_op.rs:108:20 + | +LL | const _: i32 = { 2 * 4 } + 0 + 3; + | ^^^^^^^^^^^^^ + +error: aborting due to 33 previous errors diff --git a/src/tools/clippy/tests/ui/impl.rs b/src/tools/clippy/tests/ui/impl.rs index 39443775015..aea52a852f9 100644 --- a/src/tools/clippy/tests/ui/impl.rs +++ b/src/tools/clippy/tests/ui/impl.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] +#![allow(dead_code, clippy::extra_unused_lifetimes)] #![warn(clippy::multiple_inherent_impl)] struct MyStruct; diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr index 83a36f407d5..6ae700753f0 100644 --- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr +++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr @@ -1,4 +1,4 @@ -error[E0080]: evaluation of `main::{constant#3}::<&i32>` failed +error[E0080]: evaluation of `main::{constant#3}` failed --> $DIR/indexing_slicing_index.rs:31:14 | LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. diff --git a/src/tools/clippy/tests/ui/is_digit_ascii_radix.fixed b/src/tools/clippy/tests/ui/is_digit_ascii_radix.fixed new file mode 100644 index 00000000000..c0ba647d707 --- /dev/null +++ b/src/tools/clippy/tests/ui/is_digit_ascii_radix.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::is_digit_ascii_radix)] + +const TEN: u32 = 10; + +fn main() { + let c: char = '6'; + + // Should trigger the lint. + let _ = c.is_ascii_digit(); + let _ = c.is_ascii_hexdigit(); + let _ = c.is_ascii_hexdigit(); + + // Should not trigger the lint. + let _ = c.is_digit(11); + let _ = c.is_digit(TEN); +} diff --git a/src/tools/clippy/tests/ui/is_digit_ascii_radix.rs b/src/tools/clippy/tests/ui/is_digit_ascii_radix.rs new file mode 100644 index 00000000000..68e3f3243d9 --- /dev/null +++ b/src/tools/clippy/tests/ui/is_digit_ascii_radix.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::is_digit_ascii_radix)] + +const TEN: u32 = 10; + +fn main() { + let c: char = '6'; + + // Should trigger the lint. + let _ = c.is_digit(10); + let _ = c.is_digit(16); + let _ = c.is_digit(0x10); + + // Should not trigger the lint. + let _ = c.is_digit(11); + let _ = c.is_digit(TEN); +} diff --git a/src/tools/clippy/tests/ui/is_digit_ascii_radix.stderr b/src/tools/clippy/tests/ui/is_digit_ascii_radix.stderr new file mode 100644 index 00000000000..dc5cb2913ae --- /dev/null +++ b/src/tools/clippy/tests/ui/is_digit_ascii_radix.stderr @@ -0,0 +1,22 @@ +error: use of `char::is_digit` with literal radix of 10 + --> $DIR/is_digit_ascii_radix.rs:11:13 + | +LL | let _ = c.is_digit(10); + | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_digit()` + | + = note: `-D clippy::is-digit-ascii-radix` implied by `-D warnings` + +error: use of `char::is_digit` with literal radix of 16 + --> $DIR/is_digit_ascii_radix.rs:12:13 + | +LL | let _ = c.is_digit(16); + | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()` + +error: use of `char::is_digit` with literal radix of 16 + --> $DIR/is_digit_ascii_radix.rs:13:13 + | +LL | let _ = c.is_digit(0x10); + | ^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed index 56761ebbcb8..7c2b05d837b 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code)] +#![allow(dead_code, clippy::let_unit_value)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs index 98321d889b5..f2d0b155d2c 100644 --- a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs +++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code)] +#![allow(dead_code, clippy::let_unit_value)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/src/tools/clippy/tests/ui/iter_with_drain.fixed b/src/tools/clippy/tests/ui/iter_with_drain.fixed index aea4dba9dd5..0330d554926 100644 --- a/src/tools/clippy/tests/ui/iter_with_drain.fixed +++ b/src/tools/clippy/tests/ui/iter_with_drain.fixed @@ -39,6 +39,15 @@ fn should_not_help() { let _: Vec<_> = b.drain(0..a.len()).collect(); } +fn _closed_range(mut x: Vec<String>) { + let _: Vec<String> = x.drain(0..=x.len()).collect(); +} + +fn _with_mut(x: &mut Vec<String>, y: &mut VecDeque<String>) { + let _: Vec<String> = x.drain(..).collect(); + let _: Vec<String> = y.drain(..).collect(); +} + #[derive(Default)] struct Bomb { fire: Vec<u8>, diff --git a/src/tools/clippy/tests/ui/iter_with_drain.rs b/src/tools/clippy/tests/ui/iter_with_drain.rs index 271878cffb4..993936fb8de 100644 --- a/src/tools/clippy/tests/ui/iter_with_drain.rs +++ b/src/tools/clippy/tests/ui/iter_with_drain.rs @@ -39,6 +39,15 @@ fn should_not_help() { let _: Vec<_> = b.drain(0..a.len()).collect(); } +fn _closed_range(mut x: Vec<String>) { + let _: Vec<String> = x.drain(0..=x.len()).collect(); +} + +fn _with_mut(x: &mut Vec<String>, y: &mut VecDeque<String>) { + let _: Vec<String> = x.drain(..).collect(); + let _: Vec<String> = y.drain(..).collect(); +} + #[derive(Default)] struct Bomb { fire: Vec<u8>, diff --git a/src/tools/clippy/tests/ui/let_underscore_drop.rs b/src/tools/clippy/tests/ui/let_underscore_drop.rs index 50744f81c3c..11b50492ab2 100644 --- a/src/tools/clippy/tests/ui/let_underscore_drop.rs +++ b/src/tools/clippy/tests/ui/let_underscore_drop.rs @@ -1,4 +1,5 @@ #![warn(clippy::let_underscore_drop)] +#![allow(clippy::let_unit_value)] struct Droppable; diff --git a/src/tools/clippy/tests/ui/let_underscore_drop.stderr b/src/tools/clippy/tests/ui/let_underscore_drop.stderr index 66069e0c5e1..ee7bbe995f1 100644 --- a/src/tools/clippy/tests/ui/let_underscore_drop.stderr +++ b/src/tools/clippy/tests/ui/let_underscore_drop.stderr @@ -1,5 +1,5 @@ error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:16:5 + --> $DIR/let_underscore_drop.rs:17:5 | LL | let _ = Box::new(()); | ^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _ = Box::new(()); = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:17:5 + --> $DIR/let_underscore_drop.rs:18:5 | LL | let _ = Droppable; | ^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let _ = Droppable; = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:18:5 + --> $DIR/let_underscore_drop.rs:19:5 | LL | let _ = Some(Droppable); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed index f398edc23cb..e72b7462325 100644 --- a/src/tools/clippy/tests/ui/let_unit.fixed +++ b/src/tools/clippy/tests/ui/let_unit.fixed @@ -61,3 +61,55 @@ fn multiline_sugg() { #[derive(Copy, Clone)] pub struct ContainsUnit(()); // should be fine + +fn _returns_generic() { + fn f<T>() -> T { + unimplemented!() + } + fn f2<T, U>(_: T) -> U { + unimplemented!() + } + fn f3<T>(x: T) -> T { + x + } + fn f4<T>(mut x: Vec<T>) -> T { + x.pop().unwrap() + } + + let _: () = f(); // Ok + let _: () = f(); // Lint. + + let _: () = f2(0i32); // Ok + let _: () = f2(0i32); // Lint. + + f3(()); // Lint + f3(()); // Lint + + f4(vec![()]); // Lint + f4(vec![()]); // Lint + + // Ok + let _: () = { + let x = 5; + f2(x) + }; + + let _: () = if true { f() } else { f2(0) }; // Ok + let _: () = if true { f() } else { f2(0) }; // Lint + + // Ok + let _: () = match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => f2('x'), + }; + + // Lint + match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => (), + }; +} diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs index af5b1fb2ac7..47ee0a76724 100644 --- a/src/tools/clippy/tests/ui/let_unit.rs +++ b/src/tools/clippy/tests/ui/let_unit.rs @@ -61,3 +61,55 @@ fn multiline_sugg() { #[derive(Copy, Clone)] pub struct ContainsUnit(()); // should be fine + +fn _returns_generic() { + fn f<T>() -> T { + unimplemented!() + } + fn f2<T, U>(_: T) -> U { + unimplemented!() + } + fn f3<T>(x: T) -> T { + x + } + fn f4<T>(mut x: Vec<T>) -> T { + x.pop().unwrap() + } + + let _: () = f(); // Ok + let x: () = f(); // Lint. + + let _: () = f2(0i32); // Ok + let x: () = f2(0i32); // Lint. + + let _: () = f3(()); // Lint + let x: () = f3(()); // Lint + + let _: () = f4(vec![()]); // Lint + let x: () = f4(vec![()]); // Lint + + // Ok + let _: () = { + let x = 5; + f2(x) + }; + + let _: () = if true { f() } else { f2(0) }; // Ok + let x: () = if true { f() } else { f2(0) }; // Lint + + // Ok + let _: () = match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => f2('x'), + }; + + // Lint + let _: () = match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => (), + }; +} diff --git a/src/tools/clippy/tests/ui/let_unit.stderr b/src/tools/clippy/tests/ui/let_unit.stderr index f2600c6c228..13ec11a6d33 100644 --- a/src/tools/clippy/tests/ui/let_unit.stderr +++ b/src/tools/clippy/tests/ui/let_unit.stderr @@ -34,5 +34,74 @@ LL + .map(|_| ()) LL + .next() ... -error: aborting due to 3 previous errors +error: this let-binding has unit value + --> $DIR/let_unit.rs:80:5 + | +LL | let x: () = f(); // Lint. + | ^^^^-^^^^^^^^^^^ + | | + | help: use a wild (`_`) binding: `_` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:83:5 + | +LL | let x: () = f2(0i32); // Lint. + | ^^^^-^^^^^^^^^^^^^^^^ + | | + | help: use a wild (`_`) binding: `_` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:85:5 + | +LL | let _: () = f3(()); // Lint + | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:86:5 + | +LL | let x: () = f3(()); // Lint + | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:88:5 + | +LL | let _: () = f4(vec![()]); // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:89:5 + | +LL | let x: () = f4(vec![()]); // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:98:5 + | +LL | let x: () = if true { f() } else { f2(0) }; // Lint + | ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: use a wild (`_`) binding: `_` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:109:5 + | +LL | / let _: () = match Some(0) { +LL | | None => f2(1), +LL | | Some(0) => f(), +LL | | Some(1) => f2(3), +LL | | Some(_) => (), +LL | | }; + | |______^ + | +help: omit the `let` binding + | +LL ~ match Some(0) { +LL + None => f2(1), +LL + Some(0) => f(), +LL + Some(1) => f2(3), +LL + Some(_) => (), +LL + }; + | + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/manual_bits.fixed b/src/tools/clippy/tests/ui/manual_bits.fixed index 4f1b19b75b8..386360dbdcd 100644 --- a/src/tools/clippy/tests/ui/manual_bits.fixed +++ b/src/tools/clippy/tests/ui/manual_bits.fixed @@ -1,38 +1,44 @@ // run-rustfix #![warn(clippy::manual_bits)] -#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] +#![allow( + clippy::no_effect, + clippy::useless_conversion, + path_statements, + unused_must_use, + clippy::unnecessary_operation +)] use std::mem::{size_of, size_of_val}; fn main() { - i8::BITS; - i16::BITS; - i32::BITS; - i64::BITS; - i128::BITS; - isize::BITS; - - u8::BITS; - u16::BITS; - u32::BITS; - u64::BITS; - u128::BITS; - usize::BITS; - - i8::BITS; - i16::BITS; - i32::BITS; - i64::BITS; - i128::BITS; - isize::BITS; - - u8::BITS; - u16::BITS; - u32::BITS; - u64::BITS; - u128::BITS; - usize::BITS; + i8::BITS as usize; + i16::BITS as usize; + i32::BITS as usize; + i64::BITS as usize; + i128::BITS as usize; + isize::BITS as usize; + + u8::BITS as usize; + u16::BITS as usize; + u32::BITS as usize; + u64::BITS as usize; + u128::BITS as usize; + usize::BITS as usize; + + i8::BITS as usize; + i16::BITS as usize; + i32::BITS as usize; + i64::BITS as usize; + i128::BITS as usize; + isize::BITS as usize; + + u8::BITS as usize; + u16::BITS as usize; + u32::BITS as usize; + u64::BITS as usize; + u128::BITS as usize; + usize::BITS as usize; size_of::<usize>() * 4; 4 * size_of::<usize>(); @@ -42,7 +48,12 @@ fn main() { size_of_val(&0u32) * 8; type Word = u32; - Word::BITS; + Word::BITS as usize; type Bool = bool; size_of::<Bool>() * 8; + + let _: u32 = u128::BITS as u32; + let _: u32 = u128::BITS.try_into().unwrap(); + let _ = (u128::BITS as usize).pow(5); + let _ = &(u128::BITS as usize); } diff --git a/src/tools/clippy/tests/ui/manual_bits.rs b/src/tools/clippy/tests/ui/manual_bits.rs index f8a01313e6a..62638f047eb 100644 --- a/src/tools/clippy/tests/ui/manual_bits.rs +++ b/src/tools/clippy/tests/ui/manual_bits.rs @@ -1,7 +1,13 @@ // run-rustfix #![warn(clippy::manual_bits)] -#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] +#![allow( + clippy::no_effect, + clippy::useless_conversion, + path_statements, + unused_must_use, + clippy::unnecessary_operation +)] use std::mem::{size_of, size_of_val}; @@ -45,4 +51,9 @@ fn main() { size_of::<Word>() * 8; type Bool = bool; size_of::<Bool>() * 8; + + let _: u32 = (size_of::<u128>() * 8) as u32; + let _: u32 = (size_of::<u128>() * 8).try_into().unwrap(); + let _ = (size_of::<u128>() * 8).pow(5); + let _ = &(size_of::<u128>() * 8); } diff --git a/src/tools/clippy/tests/ui/manual_bits.stderr b/src/tools/clippy/tests/ui/manual_bits.stderr index c4f5af2dcb0..69c591a203d 100644 --- a/src/tools/clippy/tests/ui/manual_bits.stderr +++ b/src/tools/clippy/tests/ui/manual_bits.stderr @@ -1,154 +1,178 @@ error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:9:5 + --> $DIR/manual_bits.rs:15:5 | LL | size_of::<i8>() * 8; - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` | = note: `-D clippy::manual-bits` implied by `-D warnings` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:10:5 + --> $DIR/manual_bits.rs:16:5 | LL | size_of::<i16>() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:11:5 + --> $DIR/manual_bits.rs:17:5 | LL | size_of::<i32>() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:12:5 + --> $DIR/manual_bits.rs:18:5 | LL | size_of::<i64>() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:13:5 + --> $DIR/manual_bits.rs:19:5 | LL | size_of::<i128>() * 8; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:14:5 + --> $DIR/manual_bits.rs:20:5 | LL | size_of::<isize>() * 8; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:16:5 + --> $DIR/manual_bits.rs:22:5 | LL | size_of::<u8>() * 8; - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:17:5 + --> $DIR/manual_bits.rs:23:5 | LL | size_of::<u16>() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:18:5 + --> $DIR/manual_bits.rs:24:5 | LL | size_of::<u32>() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:19:5 + --> $DIR/manual_bits.rs:25:5 | LL | size_of::<u64>() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:20:5 + --> $DIR/manual_bits.rs:26:5 | LL | size_of::<u128>() * 8; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:21:5 + --> $DIR/manual_bits.rs:27:5 | LL | size_of::<usize>() * 8; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:23:5 + --> $DIR/manual_bits.rs:29:5 | LL | 8 * size_of::<i8>(); - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:24:5 + --> $DIR/manual_bits.rs:30:5 | LL | 8 * size_of::<i16>(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:25:5 + --> $DIR/manual_bits.rs:31:5 | LL | 8 * size_of::<i32>(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:26:5 + --> $DIR/manual_bits.rs:32:5 | LL | 8 * size_of::<i64>(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:27:5 + --> $DIR/manual_bits.rs:33:5 | LL | 8 * size_of::<i128>(); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:28:5 + --> $DIR/manual_bits.rs:34:5 | LL | 8 * size_of::<isize>(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:30:5 + --> $DIR/manual_bits.rs:36:5 | LL | 8 * size_of::<u8>(); - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:31:5 + --> $DIR/manual_bits.rs:37:5 | LL | 8 * size_of::<u16>(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:32:5 + --> $DIR/manual_bits.rs:38:5 | LL | 8 * size_of::<u32>(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:33:5 + --> $DIR/manual_bits.rs:39:5 | LL | 8 * size_of::<u64>(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:34:5 + --> $DIR/manual_bits.rs:40:5 | LL | 8 * size_of::<u128>(); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:35:5 + --> $DIR/manual_bits.rs:41:5 | LL | 8 * size_of::<usize>(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:45:5 + --> $DIR/manual_bits.rs:51:5 | LL | size_of::<Word>() * 8; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:55:18 + | +LL | let _: u32 = (size_of::<u128>() * 8) as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:56:18 + | +LL | let _: u32 = (size_of::<u128>() * 8).try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:57:13 + | +LL | let _ = (size_of::<u128>() * 8).pow(5); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` + +error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:58:14 + | +LL | let _ = &(size_of::<u128>() * 8); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` -error: aborting due to 25 previous errors +error: aborting due to 29 previous errors diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs new file mode 100644 index 00000000000..f23c6d69b4c --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs @@ -0,0 +1,78 @@ +#![warn(clippy::manual_non_exhaustive)] +#![allow(unused)] + +enum E { + A, + B, + #[doc(hidden)] + _C, +} + +// user forgot to remove the marker +#[non_exhaustive] +enum Ep { + A, + B, + #[doc(hidden)] + _C, +} + +// marker variant does not have doc hidden attribute, should be ignored +enum NoDocHidden { + A, + B, + _C, +} + +// name of variant with doc hidden does not start with underscore, should be ignored +enum NoUnderscore { + A, + B, + #[doc(hidden)] + C, +} + +// variant with doc hidden is not unit, should be ignored +enum NotUnit { + A, + B, + #[doc(hidden)] + _C(bool), +} + +// variant with doc hidden is the only one, should be ignored +enum OnlyMarker { + #[doc(hidden)] + _A, +} + +// variant with multiple markers, should be ignored +enum MultipleMarkers { + A, + #[doc(hidden)] + _B, + #[doc(hidden)] + _C, +} + +// already non_exhaustive and no markers, should be ignored +#[non_exhaustive] +enum NonExhaustive { + A, + B, +} + +// marked is used, don't lint +enum UsedHidden { + #[doc(hidden)] + _A, + B, + C, +} +fn foo(x: &mut UsedHidden) { + if matches!(*x, UsedHidden::B) { + *x = UsedHidden::_A; + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr new file mode 100644 index 00000000000..317a45d2cbd --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr @@ -0,0 +1,41 @@ +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive_enum.rs:4:1 + | +LL | enum E { + | ^----- + | | + | _help: add the attribute: `#[non_exhaustive] enum E` + | | +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_^ + | + = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` +help: remove this variant + --> $DIR/manual_non_exhaustive_enum.rs:8:5 + | +LL | _C, + | ^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive_enum.rs:13:1 + | +LL | / enum Ep { +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_^ + | +help: remove this variant + --> $DIR/manual_non_exhaustive_enum.rs:17:5 + | +LL | _C, + | ^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs index 7a788f48520..498eee4447b 100644 --- a/src/tools/clippy/tests/ui/manual_non_exhaustive.rs +++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs @@ -1,69 +1,6 @@ #![warn(clippy::manual_non_exhaustive)] #![allow(unused)] -mod enums { - enum E { - A, - B, - #[doc(hidden)] - _C, - } - - // user forgot to remove the marker - #[non_exhaustive] - enum Ep { - A, - B, - #[doc(hidden)] - _C, - } - - // marker variant does not have doc hidden attribute, should be ignored - enum NoDocHidden { - A, - B, - _C, - } - - // name of variant with doc hidden does not start with underscore, should be ignored - enum NoUnderscore { - A, - B, - #[doc(hidden)] - C, - } - - // variant with doc hidden is not unit, should be ignored - enum NotUnit { - A, - B, - #[doc(hidden)] - _C(bool), - } - - // variant with doc hidden is the only one, should be ignored - enum OnlyMarker { - #[doc(hidden)] - _A, - } - - // variant with multiple markers, should be ignored - enum MultipleMarkers { - A, - #[doc(hidden)] - _B, - #[doc(hidden)] - _C, - } - - // already non_exhaustive and no markers, should be ignored - #[non_exhaustive] - enum NonExhaustive { - A, - B, - } -} - mod structs { struct S { pub a: i32, diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr index 613c5e8ca1d..e0766c17b75 100644 --- a/src/tools/clippy/tests/ui/manual_non_exhaustive.stderr +++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr @@ -1,44 +1,5 @@ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:5:5 - | -LL | enum E { - | ^----- - | | - | _____help: add the attribute: `#[non_exhaustive] enum E` - | | -LL | | A, -LL | | B, -LL | | #[doc(hidden)] -LL | | _C, -LL | | } - | |_____^ - | - = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` -help: remove this variant - --> $DIR/manual_non_exhaustive.rs:9:9 - | -LL | _C, - | ^^ - -error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:14:5 - | -LL | / enum Ep { -LL | | A, -LL | | B, -LL | | #[doc(hidden)] -LL | | _C, -LL | | } - | |_____^ - | -help: remove this variant - --> $DIR/manual_non_exhaustive.rs:18:9 - | -LL | _C, - | ^^ - -error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:68:5 + --> $DIR/manual_non_exhaustive_struct.rs:5:5 | LL | struct S { | ^------- @@ -51,14 +12,15 @@ LL | | _c: (), LL | | } | |_____^ | + = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` help: remove this field - --> $DIR/manual_non_exhaustive.rs:71:9 + --> $DIR/manual_non_exhaustive_struct.rs:8:9 | LL | _c: (), | ^^^^^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:76:5 + --> $DIR/manual_non_exhaustive_struct.rs:13:5 | LL | / struct Sp { LL | | pub a: i32, @@ -68,13 +30,13 @@ LL | | } | |_____^ | help: remove this field - --> $DIR/manual_non_exhaustive.rs:79:9 + --> $DIR/manual_non_exhaustive_struct.rs:16:9 | LL | _c: (), | ^^^^^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:117:5 + --> $DIR/manual_non_exhaustive_struct.rs:54:5 | LL | struct T(pub i32, pub i32, ()); | --------^^^^^^^^^^^^^^^^^^^^^^^ @@ -82,22 +44,22 @@ LL | struct T(pub i32, pub i32, ()); | help: add the attribute: `#[non_exhaustive] struct T` | help: remove this field - --> $DIR/manual_non_exhaustive.rs:117:32 + --> $DIR/manual_non_exhaustive_struct.rs:54:32 | LL | struct T(pub i32, pub i32, ()); | ^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:121:5 + --> $DIR/manual_non_exhaustive_struct.rs:58:5 | LL | struct Tp(pub i32, pub i32, ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove this field - --> $DIR/manual_non_exhaustive.rs:121:33 + --> $DIR/manual_non_exhaustive_struct.rs:58:33 | LL | struct Tp(pub i32, pub i32, ()); | ^^ -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/manual_split_once.fixed b/src/tools/clippy/tests/ui/manual_split_once.fixed index d5113df569a..c7ca770434a 100644 --- a/src/tools/clippy/tests/ui/manual_split_once.fixed +++ b/src/tools/clippy/tests/ui/manual_split_once.fixed @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] -#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)] +#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] extern crate itertools; @@ -10,27 +10,25 @@ extern crate itertools; use itertools::Itertools; fn main() { - let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0)); let _ = "key=value".splitn(2, '=').nth(2); - let _ = "key=value".split_once('=').map_or("key=value", |x| x.0); - let _ = "key=value".split_once('=').map_or("key=value", |x| x.0); let _ = "key=value".split_once('=').unwrap().1; let _ = "key=value".split_once('=').unwrap().1; let (_, _) = "key=value".split_once('=').unwrap(); let s = String::from("key=value"); - let _ = s.split_once('=').map_or(&*s, |x| x.0); + let _ = s.split_once('=').unwrap().1; let s = Box::<str>::from("key=value"); - let _ = s.split_once('=').map_or(&*s, |x| x.0); + let _ = s.split_once('=').unwrap().1; let s = &"key=value"; - let _ = s.split_once('=').map_or(*s, |x| x.0); + let _ = s.split_once('=').unwrap().1; fn _f(s: &str) -> Option<&str> { - let _ = s.split_once("key=value").map_or(s, |x| x.0); - let _ = s.split_once("key=value")?.1; - let _ = s.split_once("key=value")?.1; + let _ = s.split_once('=')?.1; + let _ = s.split_once('=')?.1; + let _ = s.rsplit_once('=')?.0; + let _ = s.rsplit_once('=')?.0; None } @@ -38,19 +36,112 @@ fn main() { let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); // `rsplitn` gives the results in the reverse order of `rsplit_once` - let _ = "key=value".rsplitn(2, '=').next().unwrap(); let _ = "key=value".rsplit_once('=').unwrap().0; - let _ = "key=value".rsplit_once('=').map(|x| x.1); let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap(); + let _ = s.rsplit_once('=').map(|x| x.0); +} + +fn indirect() -> Option<()> { + let (l, r) = "a.b.c".split_once('.').unwrap(); + + + + let (l, r) = "a.b.c".split_once('.')?; + + + + let (l, r) = "a.b.c".rsplit_once('.').unwrap(); + + + + let (l, r) = "a.b.c".rsplit_once('.')?; + + + + // could lint, currently doesn't + + let mut iter = "a.b.c".splitn(2, '.'); + let other = 1; + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let mut mut_binding = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let tuple = (iter.next()?, iter.next()?); + + // should not lint + + let mut missing_unwrap = "a.b.c".splitn(2, '.'); + let l = missing_unwrap.next(); + let r = missing_unwrap.next(); + + let mut mixed_unrap = "a.b.c".splitn(2, '.'); + let unwrap = mixed_unrap.next().unwrap(); + let question_mark = mixed_unrap.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let same_name = iter.next()?; + let same_name = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let shadows_existing = "d"; + let shadows_existing = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let becomes_shadowed = iter.next()?; + let becomes_shadowed = "d"; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + let third_usage = iter.next()?; + + let mut n_three = "a.b.c".splitn(3, '.'); + let l = n_three.next()?; + let r = n_three.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + { + let in_block = iter.next()?; + } + let r = iter.next()?; + + let mut lacks_binding = "a.b.c".splitn(2, '.'); + let _ = lacks_binding.next()?; + let r = lacks_binding.next()?; + + let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~"); + let l = iter.next()?; + let r = iter.next()?; + + let mut assigned = ""; + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + assigned = iter.next()?; + + None } fn _msrv_1_51() { #![clippy::msrv = "1.51"] - // `str::split_once` was stabilized in 1.16. Do not lint this + // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); } fn _msrv_1_52() { #![clippy::msrv = "1.52"] let _ = "key=value".split_once('=').unwrap().1; + + let (a, b) = "a.b.c".split_once('.').unwrap(); + + } diff --git a/src/tools/clippy/tests/ui/manual_split_once.rs b/src/tools/clippy/tests/ui/manual_split_once.rs index 80e02952dbd..ee2848a251e 100644 --- a/src/tools/clippy/tests/ui/manual_split_once.rs +++ b/src/tools/clippy/tests/ui/manual_split_once.rs @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] -#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)] +#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] extern crate itertools; @@ -10,27 +10,25 @@ extern crate itertools; use itertools::Itertools; fn main() { - let _ = "key=value".splitn(2, '=').next(); let _ = "key=value".splitn(2, '=').nth(2); - let _ = "key=value".splitn(2, '=').next().unwrap(); - let _ = "key=value".splitn(2, '=').nth(0).unwrap(); let _ = "key=value".splitn(2, '=').nth(1).unwrap(); let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); let s = String::from("key=value"); - let _ = s.splitn(2, '=').next().unwrap(); + let _ = s.splitn(2, '=').nth(1).unwrap(); let s = Box::<str>::from("key=value"); - let _ = s.splitn(2, '=').nth(0).unwrap(); + let _ = s.splitn(2, '=').nth(1).unwrap(); let s = &"key=value"; - let _ = s.splitn(2, '=').skip(0).next().unwrap(); + let _ = s.splitn(2, '=').skip(1).next().unwrap(); fn _f(s: &str) -> Option<&str> { - let _ = s.splitn(2, "key=value").next()?; - let _ = s.splitn(2, "key=value").nth(1)?; - let _ = s.splitn(2, "key=value").skip(1).next()?; + let _ = s.splitn(2, '=').nth(1)?; + let _ = s.splitn(2, '=').skip(1).next()?; + let _ = s.rsplitn(2, '=').nth(1)?; + let _ = s.rsplitn(2, '=').skip(1).next()?; None } @@ -38,19 +36,112 @@ fn main() { let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); // `rsplitn` gives the results in the reverse order of `rsplit_once` - let _ = "key=value".rsplitn(2, '=').next().unwrap(); let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); - let _ = "key=value".rsplitn(2, '=').nth(0); let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); + let _ = s.rsplitn(2, '=').nth(1); +} + +fn indirect() -> Option<()> { + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next().unwrap(); + let r = iter.next().unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".rsplitn(2, '.'); + let r = iter.next().unwrap(); + let l = iter.next().unwrap(); + + let mut iter = "a.b.c".rsplitn(2, '.'); + let r = iter.next()?; + let l = iter.next()?; + + // could lint, currently doesn't + + let mut iter = "a.b.c".splitn(2, '.'); + let other = 1; + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let mut mut_binding = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let tuple = (iter.next()?, iter.next()?); + + // should not lint + + let mut missing_unwrap = "a.b.c".splitn(2, '.'); + let l = missing_unwrap.next(); + let r = missing_unwrap.next(); + + let mut mixed_unrap = "a.b.c".splitn(2, '.'); + let unwrap = mixed_unrap.next().unwrap(); + let question_mark = mixed_unrap.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let same_name = iter.next()?; + let same_name = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let shadows_existing = "d"; + let shadows_existing = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let becomes_shadowed = iter.next()?; + let becomes_shadowed = "d"; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + let third_usage = iter.next()?; + + let mut n_three = "a.b.c".splitn(3, '.'); + let l = n_three.next()?; + let r = n_three.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + { + let in_block = iter.next()?; + } + let r = iter.next()?; + + let mut lacks_binding = "a.b.c".splitn(2, '.'); + let _ = lacks_binding.next()?; + let r = lacks_binding.next()?; + + let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~"); + let l = iter.next()?; + let r = iter.next()?; + + let mut assigned = ""; + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + assigned = iter.next()?; + + None } fn _msrv_1_51() { #![clippy::msrv = "1.51"] - // `str::split_once` was stabilized in 1.16. Do not lint this + // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); } fn _msrv_1_52() { #![clippy::msrv = "1.52"] let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); } diff --git a/src/tools/clippy/tests/ui/manual_split_once.stderr b/src/tools/clippy/tests/ui/manual_split_once.stderr index af9c7a2d41b..2563a6904b7 100644 --- a/src/tools/clippy/tests/ui/manual_split_once.stderr +++ b/src/tools/clippy/tests/ui/manual_split_once.stderr @@ -1,100 +1,213 @@ error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:13:13 + --> $DIR/manual_split_once.rs:14:13 | -LL | let _ = "key=value".splitn(2, '=').next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))` +LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` | = note: `-D clippy::manual-split-once` implied by `-D warnings` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:15:13 | -LL | let _ = "key=value".splitn(2, '=').next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:16:13 - | -LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:17:13 - | -LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:18:13 - | LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:19:18 + --> $DIR/manual_split_once.rs:16:18 | LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')` error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:19:13 + | +LL | let _ = s.splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` + +error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:22:13 | -LL | let _ = s.splitn(2, '=').next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)` +LL | let _ = s.splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:25:13 | -LL | let _ = s.splitn(2, '=').nth(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)` +LL | let _ = s.splitn(2, '=').skip(1).next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:28:13 + --> $DIR/manual_split_once.rs:28:17 | -LL | let _ = s.splitn(2, '=').skip(0).next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)` +LL | let _ = s.splitn(2, '=').nth(1)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:31:17 + --> $DIR/manual_split_once.rs:29:17 | -LL | let _ = s.splitn(2, "key=value").next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)` +LL | let _ = s.splitn(2, '=').skip(1).next()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:32:17 +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:30:17 | -LL | let _ = s.splitn(2, "key=value").nth(1)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1` +LL | let _ = s.rsplitn(2, '=').nth(1)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:33:17 +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:31:17 | -LL | let _ = s.splitn(2, "key=value").skip(1).next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1` +LL | let _ = s.rsplitn(2, '=').skip(1).next()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:42:13 + --> $DIR/manual_split_once.rs:39:13 | LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:43:13 + --> $DIR/manual_split_once.rs:40:18 | -LL | let _ = "key=value".rsplitn(2, '=').nth(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)` +LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:44:18 + --> $DIR/manual_split_once.rs:41:13 | -LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` +LL | let _ = s.rsplitn(2, '=').nth(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:45:5 + | +LL | let mut iter = "a.b.c".splitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let l = iter.next().unwrap(); + | ----------------------------- first usage here +LL | let r = iter.next().unwrap(); + | ----------------------------- second usage here + | +help: try `split_once` + | +LL | let (l, r) = "a.b.c".split_once('.').unwrap(); + | +help: remove the `iter` usages + | +LL - let l = iter.next().unwrap(); +LL + + | +help: remove the `iter` usages + | +LL - let r = iter.next().unwrap(); +LL + + | error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:55:13 + --> $DIR/manual_split_once.rs:49:5 + | +LL | let mut iter = "a.b.c".splitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let l = iter.next()?; + | --------------------- first usage here +LL | let r = iter.next()?; + | --------------------- second usage here + | +help: try `split_once` + | +LL | let (l, r) = "a.b.c".split_once('.')?; + | +help: remove the `iter` usages + | +LL - let l = iter.next()?; +LL + + | +help: remove the `iter` usages + | +LL - let r = iter.next()?; +LL + + | + +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:53:5 + | +LL | let mut iter = "a.b.c".rsplitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let r = iter.next().unwrap(); + | ----------------------------- first usage here +LL | let l = iter.next().unwrap(); + | ----------------------------- second usage here + | +help: try `rsplit_once` + | +LL | let (l, r) = "a.b.c".rsplit_once('.').unwrap(); + | +help: remove the `iter` usages + | +LL - let r = iter.next().unwrap(); +LL + + | +help: remove the `iter` usages + | +LL - let l = iter.next().unwrap(); +LL + + | + +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:57:5 + | +LL | let mut iter = "a.b.c".rsplitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let r = iter.next()?; + | --------------------- first usage here +LL | let l = iter.next()?; + | --------------------- second usage here + | +help: try `rsplit_once` + | +LL | let (l, r) = "a.b.c".rsplit_once('.')?; + | +help: remove the `iter` usages + | +LL - let r = iter.next()?; +LL + + | +help: remove the `iter` usages + | +LL - let l = iter.next()?; +LL + + | + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:142:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` -error: aborting due to 16 previous errors +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:144:5 + | +LL | let mut iter = "a.b.c".splitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let a = iter.next().unwrap(); + | ----------------------------- first usage here +LL | let b = iter.next().unwrap(); + | ----------------------------- second usage here + | +help: try `split_once` + | +LL | let (a, b) = "a.b.c".split_once('.').unwrap(); + | +help: remove the `iter` usages + | +LL - let a = iter.next().unwrap(); +LL + + | +help: remove the `iter` usages + | +LL - let b = iter.next().unwrap(); +LL + + | + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed index 5d57638af43..a7b36d53cd2 100644 --- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed @@ -5,7 +5,8 @@ unused_variables, overflowing_literals, clippy::excessive_precision, - clippy::inconsistent_digit_grouping + clippy::inconsistent_digit_grouping, + clippy::unusual_byte_groupings )] fn main() { @@ -25,5 +26,18 @@ fn main() { let fail28 = 241_251_235E723_f64; let ok29 = 42279.911_32; + // testing that the suggestion actually fits in its type + let fail30 = 127_i8; // should be i8 + let fail31 = 240_u8; // should be u8 + let ok32 = 360_8; // doesnt fit in either, should be ignored + let fail33 = 0x1234_i16; + let fail34 = 0xABCD_u16; + let ok35 = 0x12345_16; + let fail36 = 0xFFFF_FFFF_FFFF_FFFF_u64; // u64 + + // issue #6129 + let ok37 = 123_32.123; + let ok38 = 124_64.0; + let _ = 1.123_45E1_f32; } diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs index 12171452885..c97b31965c7 100644 --- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs @@ -5,7 +5,8 @@ unused_variables, overflowing_literals, clippy::excessive_precision, - clippy::inconsistent_digit_grouping + clippy::inconsistent_digit_grouping, + clippy::unusual_byte_groupings )] fn main() { @@ -25,5 +26,18 @@ fn main() { let fail28 = 241251235E723_64; let ok29 = 42279.911_32; + // testing that the suggestion actually fits in its type + let fail30 = 127_8; // should be i8 + let fail31 = 240_8; // should be u8 + let ok32 = 360_8; // doesnt fit in either, should be ignored + let fail33 = 0x1234_16; + let fail34 = 0xABCD_16; + let ok35 = 0x12345_16; + let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 + + // issue #6129 + let ok37 = 123_32.123; + let ok38 = 124_64.0; + let _ = 1.12345E1_32; } diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr index d24543c26e4..fb761d9bde4 100644 --- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr @@ -1,5 +1,5 @@ error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:12:18 + --> $DIR/mistyped_literal_suffix.rs:13:18 | LL | let fail14 = 2_32; | ^^^^ help: did you mean to write: `2_i32` @@ -7,64 +7,94 @@ LL | let fail14 = 2_32; = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:13:18 + --> $DIR/mistyped_literal_suffix.rs:14:18 | LL | let fail15 = 4_64; | ^^^^ help: did you mean to write: `4_i64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:14:18 + --> $DIR/mistyped_literal_suffix.rs:15:18 | LL | let fail16 = 7_8; // | ^^^ help: did you mean to write: `7_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:15:18 + --> $DIR/mistyped_literal_suffix.rs:16:18 | LL | let fail17 = 23_16; // | ^^^^^ help: did you mean to write: `23_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:18:18 + --> $DIR/mistyped_literal_suffix.rs:19:18 | LL | let fail20 = 2__8; // | ^^^^ help: did you mean to write: `2_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:19:18 + --> $DIR/mistyped_literal_suffix.rs:20:18 | LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:22:18 + --> $DIR/mistyped_literal_suffix.rs:23:18 | LL | let fail25 = 1E2_32; | ^^^^^^ help: did you mean to write: `1E2_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:23:18 + --> $DIR/mistyped_literal_suffix.rs:24:18 | LL | let fail26 = 43E7_64; | ^^^^^^^ help: did you mean to write: `43E7_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:24:18 + --> $DIR/mistyped_literal_suffix.rs:25:18 | LL | let fail27 = 243E17_32; | ^^^^^^^^^ help: did you mean to write: `243E17_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:25:18 + --> $DIR/mistyped_literal_suffix.rs:26:18 | LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:28:13 + --> $DIR/mistyped_literal_suffix.rs:30:18 + | +LL | let fail30 = 127_8; // should be i8 + | ^^^^^ help: did you mean to write: `127_i8` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:31:18 + | +LL | let fail31 = 240_8; // should be u8 + | ^^^^^ help: did you mean to write: `240_u8` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:33:18 + | +LL | let fail33 = 0x1234_16; + | ^^^^^^^^^ help: did you mean to write: `0x1234_i16` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:34:18 + | +LL | let fail34 = 0xABCD_16; + | ^^^^^^^^^ help: did you mean to write: `0xABCD_u16` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:36:18 + | +LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:42:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` -error: aborting due to 11 previous errors +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/mut_from_ref.rs b/src/tools/clippy/tests/ui/mut_from_ref.rs index a9a04c8f56b..370dbd58821 100644 --- a/src/tools/clippy/tests/ui/mut_from_ref.rs +++ b/src/tools/clippy/tests/ui/mut_from_ref.rs @@ -5,7 +5,7 @@ struct Foo; impl Foo { fn this_wont_hurt_a_bit(&self) -> &mut Foo { - unimplemented!() + unsafe { unimplemented!() } } } @@ -15,29 +15,37 @@ trait Ouch { impl Ouch for Foo { fn ouch(x: &Foo) -> &mut Foo { - unimplemented!() + unsafe { unimplemented!() } } } fn fail(x: &u32) -> &mut u16 { - unimplemented!() + unsafe { unimplemented!() } } fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { - unimplemented!() + unsafe { unimplemented!() } } fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { - unimplemented!() + unsafe { unimplemented!() } } // this is OK, because the result borrows y fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 { - unimplemented!() + unsafe { unimplemented!() } } // this is also OK, because the result could borrow y fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 { + unsafe { unimplemented!() } +} + +unsafe fn also_broken(x: &u32) -> &mut u32 { + unimplemented!() +} + +fn without_unsafe(x: &u32) -> &mut u32 { unimplemented!() } diff --git a/src/tools/clippy/tests/ui/mut_from_ref.stderr b/src/tools/clippy/tests/ui/mut_from_ref.stderr index 4787999920b..b76d6a13ffb 100644 --- a/src/tools/clippy/tests/ui/mut_from_ref.stderr +++ b/src/tools/clippy/tests/ui/mut_from_ref.stderr @@ -59,5 +59,17 @@ note: immutable borrow here LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ -error: aborting due to 5 previous errors +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:44:35 + | +LL | unsafe fn also_broken(x: &u32) -> &mut u32 { + | ^^^^^^^^ + | +note: immutable borrow here + --> $DIR/mut_from_ref.rs:44:26 + | +LL | unsafe fn also_broken(x: &u32) -> &mut u32 { + | ^^^^ + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed b/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed index f00f9ee4c33..c1685f7b6d7 100644 --- a/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed +++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] -#![allow(unused, clippy::needless_return, clippy::match_single_binding)] +#![allow( + unused, + clippy::needless_return, + clippy::match_single_binding, + clippy::let_unit_value +)] use std::collections::HashMap; diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.rs b/src/tools/clippy/tests/ui/needless_for_each_fixable.rs index 1bd400d348b..ad17b0956fa 100644 --- a/src/tools/clippy/tests/ui/needless_for_each_fixable.rs +++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.rs @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] -#![allow(unused, clippy::needless_return, clippy::match_single_binding)] +#![allow( + unused, + clippy::needless_return, + clippy::match_single_binding, + clippy::let_unit_value +)] use std::collections::HashMap; diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr b/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr index 6487e57266c..08e995851d7 100644 --- a/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr +++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr @@ -1,5 +1,5 @@ error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:10:5 + --> $DIR/needless_for_each_fixable.rs:15:5 | LL | / v.iter().for_each(|elem| { LL | | acc += elem; @@ -15,7 +15,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:13:5 + --> $DIR/needless_for_each_fixable.rs:18:5 | LL | / v.into_iter().for_each(|elem| { LL | | acc += elem; @@ -30,7 +30,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:17:5 + --> $DIR/needless_for_each_fixable.rs:22:5 | LL | / [1, 2, 3].iter().for_each(|elem| { LL | | acc += elem; @@ -45,7 +45,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:22:5 + --> $DIR/needless_for_each_fixable.rs:27:5 | LL | / hash_map.iter().for_each(|(k, v)| { LL | | acc += k + v; @@ -60,7 +60,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:25:5 + --> $DIR/needless_for_each_fixable.rs:30:5 | LL | / hash_map.iter_mut().for_each(|(k, v)| { LL | | acc += *k + *v; @@ -75,7 +75,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:28:5 + --> $DIR/needless_for_each_fixable.rs:33:5 | LL | / hash_map.keys().for_each(|k| { LL | | acc += k; @@ -90,7 +90,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:31:5 + --> $DIR/needless_for_each_fixable.rs:36:5 | LL | / hash_map.values().for_each(|v| { LL | | acc += v; @@ -105,7 +105,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:38:5 + --> $DIR/needless_for_each_fixable.rs:43:5 | LL | / my_vec().iter().for_each(|elem| { LL | | acc += elem; diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs index 89e012c066f..54e66b391b8 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.rs +++ b/src/tools/clippy/tests/ui/needless_late_init.rs @@ -1,4 +1,15 @@ -#![allow(unused)] +#![feature(let_chains)] +#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value)] + +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::rc::Rc; + +struct SignificantDrop; +impl std::ops::Drop for SignificantDrop { + fn drop(&mut self) { + println!("dropped"); + } +} fn main() { let a; @@ -17,13 +28,6 @@ fn main() { b = "five" } - let c; - if let Some(n) = Some(5) { - c = n; - } else { - c = -50; - } - let d; if true { let temp = 5; @@ -36,7 +40,7 @@ fn main() { if true { e = format!("{} {}", a, b); } else { - e = format!("{}", c); + e = format!("{}", n); } let f; @@ -52,7 +56,27 @@ fn main() { panic!(); } - println!("{}", a); + // Drop order only matters if both are significant + let x; + let y = SignificantDrop; + x = 1; + + let x; + let y = 1; + x = SignificantDrop; + + let x; + // types that should be considered insignificant + let y = 1; + let y = "2"; + let y = String::new(); + let y = vec![3.0]; + let y = HashMap::<usize, usize>::new(); + let y = BTreeMap::<usize, usize>::new(); + let y = HashSet::<usize>::new(); + let y = BTreeSet::<usize>::new(); + let y = Box::new(4); + x = SignificantDrop; } async fn in_async() -> &'static str { @@ -176,5 +200,32 @@ fn does_not_lint() { } in_macro!(); - println!("{}", x); + // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613 + let x; + if let Some(n) = Some("v") { + x = 1; + } else { + x = 2; + } + + let x; + if true && let Some(n) = Some("let chains too") { + x = 1; + } else { + x = 2; + } + + // ignore mut bindings + // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93 + // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100 + let mut x: usize; + x = 1; + x = 2; + x = 3; + + // should not move the declaration if `x` has a significant drop, and there + // is another binding with a significant drop between it and the first usage + let x; + let y = SignificantDrop; + x = SignificantDrop; } diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr index ef79e635d2a..d33a117b288 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.stderr +++ b/src/tools/clippy/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ -error: unneeded late initalization - --> $DIR/needless_late_init.rs:4:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:15:5 | LL | let a; | ^^^^^^ @@ -20,8 +20,8 @@ help: add a semicolon after the `match` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:13:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:24:5 | LL | let b; | ^^^^^^ @@ -41,29 +41,8 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:20:5 - | -LL | let c; - | ^^^^^^ - | -help: declare `c` here - | -LL | let c = if let Some(n) = Some(5) { - | +++++++ -help: remove the assignments from the branches - | -LL ~ n -LL | } else { -LL ~ -50 - | -help: add a semicolon after the `if` expression - | -LL | }; - | + - -error: unneeded late initalization - --> $DIR/needless_late_init.rs:27:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:31:5 | LL | let d; | ^^^^^^ @@ -83,8 +62,8 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:35:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:39:5 | LL | let e; | ^^^^^^ @@ -97,15 +76,15 @@ help: remove the assignments from the branches | LL ~ format!("{} {}", a, b) LL | } else { -LL ~ format!("{}", c) +LL ~ format!("{}", n) | help: add a semicolon after the `if` expression | LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:42:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:46:5 | LL | let f; | ^^^^^^ @@ -120,8 +99,8 @@ LL - 1 => f = "three", LL + 1 => "three", | -error: unneeded late initalization - --> $DIR/needless_late_init.rs:48:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:52:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -140,8 +119,50 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:63:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:60:5 + | +LL | let x; + | ^^^^^^ created here +LL | let y = SignificantDrop; +LL | x = 1; + | ^^^^^ initialised here + | +help: declare `x` here + | +LL | let x = 1; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:64:5 + | +LL | let x; + | ^^^^^^ created here +LL | let y = 1; +LL | x = SignificantDrop; + | ^^^^^^^^^^^^^^^^^^^ initialised here + | +help: declare `x` here + | +LL | let x = SignificantDrop; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:68:5 + | +LL | let x; + | ^^^^^^ created here +... +LL | x = SignificantDrop; + | ^^^^^^^^^^^^^^^^^^^ initialised here + | +help: declare `x` here + | +LL | let x = SignificantDrop; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:87:5 | LL | let a; | ^^^^^^ @@ -161,8 +182,8 @@ help: add a semicolon after the `match` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:80:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:104:5 | LL | let a; | ^^^^^^ @@ -182,5 +203,5 @@ help: add a semicolon after the `match` expression LL | }; | + -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/needless_late_init_fixable.fixed b/src/tools/clippy/tests/ui/needless_late_init_fixable.fixed index b516f9d86b7..724477e8691 100644 --- a/src/tools/clippy/tests/ui/needless_late_init_fixable.fixed +++ b/src/tools/clippy/tests/ui/needless_late_init_fixable.fixed @@ -15,11 +15,5 @@ fn main() { let d: usize = 1; - let mut e = 1; - e = 2; - - - let h = format!("{}", e); - - println!("{}", a); + let e = format!("{}", d); } diff --git a/src/tools/clippy/tests/ui/needless_late_init_fixable.rs b/src/tools/clippy/tests/ui/needless_late_init_fixable.rs index 75a4bc916de..3e6bd363672 100644 --- a/src/tools/clippy/tests/ui/needless_late_init_fixable.rs +++ b/src/tools/clippy/tests/ui/needless_late_init_fixable.rs @@ -14,12 +14,6 @@ fn main() { let d: usize; d = 1; - let mut e; - e = 1; - e = 2; - - let h; - h = format!("{}", e); - - println!("{}", a); + let e; + e = format!("{}", d); } diff --git a/src/tools/clippy/tests/ui/needless_late_init_fixable.stderr b/src/tools/clippy/tests/ui/needless_late_init_fixable.stderr index 3f3d4f5286b..8c664309e3e 100644 --- a/src/tools/clippy/tests/ui/needless_late_init_fixable.stderr +++ b/src/tools/clippy/tests/ui/needless_late_init_fixable.stderr @@ -1,8 +1,10 @@ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:6:5 | LL | let a; - | ^^^^^^ + | ^^^^^^ created here +LL | a = "zero"; + | ^^^^^^^^^^ initialised here | = note: `-D clippy::needless-late-init` implied by `-D warnings` help: declare `a` here @@ -10,60 +12,59 @@ help: declare `a` here LL | let a = "zero"; | ~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:9:5 | LL | let b; - | ^^^^^^ + | ^^^^^^ created here +LL | let c; +LL | b = 1; + | ^^^^^ initialised here | help: declare `b` here | LL | let b = 1; | ~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:10:5 | LL | let c; - | ^^^^^^ + | ^^^^^^ created here +LL | b = 1; +LL | c = 2; + | ^^^^^ initialised here | help: declare `c` here | LL | let c = 2; | ~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:14:5 | LL | let d: usize; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ created here +LL | d = 1; + | ^^^^^ initialised here | help: declare `d` here | LL | let d: usize = 1; | ~~~~~~~~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:17:5 | -LL | let mut e; - | ^^^^^^^^^^ +LL | let e; + | ^^^^^^ created here +LL | e = format!("{}", d); + | ^^^^^^^^^^^^^^^^^^^^ initialised here | help: declare `e` here | -LL | let mut e = 1; - | ~~~~~~~~~ - -error: unneeded late initalization - --> $DIR/needless_late_init_fixable.rs:21:5 - | -LL | let h; - | ^^^^^^ - | -help: declare `h` here - | -LL | let h = format!("{}", e); +LL | let e = format!("{}", d); | ~~~~~ -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/needless_match.fixed b/src/tools/clippy/tests/ui/needless_match.fixed index 9ccccaa1725..b997e5316cf 100644 --- a/src/tools/clippy/tests/ui/needless_match.fixed +++ b/src/tools/clippy/tests/ui/needless_match.fixed @@ -80,6 +80,18 @@ fn if_let_option() { } else { None }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) }; +} + +fn if_let_option_result() -> Result<(), ()> { + fn f(x: i32) -> Result<Option<i32>, ()> { + Ok(Some(x)) + } + // Don't trigger + let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? }; + Ok(()) } fn if_let_result() { diff --git a/src/tools/clippy/tests/ui/needless_match.rs b/src/tools/clippy/tests/ui/needless_match.rs index d210ecff7f1..90482775a1e 100644 --- a/src/tools/clippy/tests/ui/needless_match.rs +++ b/src/tools/clippy/tests/ui/needless_match.rs @@ -103,6 +103,18 @@ fn if_let_option() { } else { None }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) }; +} + +fn if_let_option_result() -> Result<(), ()> { + fn f(x: i32) -> Result<Option<i32>, ()> { + Ok(Some(x)) + } + // Don't trigger + let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? }; + Ok(()) } fn if_let_result() { diff --git a/src/tools/clippy/tests/ui/needless_match.stderr b/src/tools/clippy/tests/ui/needless_match.stderr index 34c5226f060..2d679631c6f 100644 --- a/src/tools/clippy/tests/ui/needless_match.stderr +++ b/src/tools/clippy/tests/ui/needless_match.stderr @@ -72,19 +72,19 @@ LL | let _ = if let Some(a) = Some(1) { Some(a) } else { None }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:110:31 + --> $DIR/needless_match.rs:122:31 | LL | let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:111:31 + --> $DIR/needless_match.rs:123:31 | LL | let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:117:21 + --> $DIR/needless_match.rs:129:21 | LL | let _: Simple = if let Simple::A = x { | _____________________^ @@ -97,7 +97,7 @@ LL | | }; | |_____^ help: replace it with: `x` error: this match expression is unnecessary - --> $DIR/needless_match.rs:156:26 + --> $DIR/needless_match.rs:168:26 | LL | let _: Complex = match ce { | __________________________^ diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.fixed b/src/tools/clippy/tests/ui/needless_option_as_deref.fixed index c09b07db3dc..acd22c6bb43 100644 --- a/src/tools/clippy/tests/ui/needless_option_as_deref.fixed +++ b/src/tools/clippy/tests/ui/needless_option_as_deref.fixed @@ -16,6 +16,20 @@ fn main() { let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + let mut y = 0; + let mut x = Some(&mut y); + for _ in 0..3 { + let _ = x.as_deref_mut(); + } + + let mut y = 0; + let mut x = Some(&mut y); + let mut closure = || { + let _ = x.as_deref_mut(); + }; + closure(); + closure(); + // #7846 let mut i = 0; let mut opt_vec = vec![Some(&mut i)]; diff --git a/src/tools/clippy/tests/ui/needless_option_as_deref.rs b/src/tools/clippy/tests/ui/needless_option_as_deref.rs index c3ba27ecccf..61eda5052a2 100644 --- a/src/tools/clippy/tests/ui/needless_option_as_deref.rs +++ b/src/tools/clippy/tests/ui/needless_option_as_deref.rs @@ -16,6 +16,20 @@ fn main() { let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + let mut y = 0; + let mut x = Some(&mut y); + for _ in 0..3 { + let _ = x.as_deref_mut(); + } + + let mut y = 0; + let mut x = Some(&mut y); + let mut closure = || { + let _ = x.as_deref_mut(); + }; + closure(); + closure(); + // #7846 let mut i = 0; let mut opt_vec = vec![Some(&mut i)]; diff --git a/src/tools/clippy/tests/ui/needless_option_take.fixed b/src/tools/clippy/tests/ui/needless_option_take.fixed new file mode 100644 index 00000000000..29691e81666 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_option_take.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +fn main() { + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + println!("Testing erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); +} diff --git a/src/tools/clippy/tests/ui/needless_option_take.rs b/src/tools/clippy/tests/ui/needless_option_take.rs new file mode 100644 index 00000000000..9f4109eb463 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_option_take.rs @@ -0,0 +1,15 @@ +// run-rustfix + +fn main() { + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + println!("Testing erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref().take(); +} diff --git a/src/tools/clippy/tests/ui/needless_option_take.stderr b/src/tools/clippy/tests/ui/needless_option_take.stderr new file mode 100644 index 00000000000..cb3bf015b36 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_option_take.stderr @@ -0,0 +1,10 @@ +error: called `Option::take()` on a temporary value + --> $DIR/needless_option_take.rs:14:5 + | +LL | x.as_ref().take(); + | ^^^^^^^^^^^^^^^^^ help: try: `x.as_ref()` + | + = note: `-D clippy::needless-option-take` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/needless_splitn.fixed b/src/tools/clippy/tests/ui/needless_splitn.fixed index f6a4b2f17d3..61f5fc4e679 100644 --- a/src/tools/clippy/tests/ui/needless_splitn.fixed +++ b/src/tools/clippy/tests/ui/needless_splitn.fixed @@ -24,4 +24,24 @@ fn main() { let _ = str.rsplitn(2, '=').nth(1); let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap(); let (_, _) = str.rsplit('=').next_tuple().unwrap(); + + let _ = str.split('=').next(); + let _ = str.split('=').nth(3); + let _ = str.splitn(5, '=').nth(4); + let _ = str.splitn(5, '=').nth(5); +} + +fn _question_mark(s: &str) -> Option<()> { + let _ = s.split('=').next()?; + let _ = s.split('=').nth(0)?; + let _ = s.rsplit('=').next()?; + let _ = s.rsplit('=').nth(0)?; + + Some(()) +} + +fn _test_msrv() { + #![clippy::msrv = "1.51"] + // `manual_split_once` MSRV shouldn't apply to `needless_splitn` + let _ = "key=value".split('=').nth(0).unwrap(); } diff --git a/src/tools/clippy/tests/ui/needless_splitn.rs b/src/tools/clippy/tests/ui/needless_splitn.rs index 6ba32255bb2..71d9a7077fa 100644 --- a/src/tools/clippy/tests/ui/needless_splitn.rs +++ b/src/tools/clippy/tests/ui/needless_splitn.rs @@ -24,4 +24,24 @@ fn main() { let _ = str.rsplitn(2, '=').nth(1); let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap(); let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap(); + + let _ = str.splitn(5, '=').next(); + let _ = str.splitn(5, '=').nth(3); + let _ = str.splitn(5, '=').nth(4); + let _ = str.splitn(5, '=').nth(5); +} + +fn _question_mark(s: &str) -> Option<()> { + let _ = s.splitn(2, '=').next()?; + let _ = s.splitn(2, '=').nth(0)?; + let _ = s.rsplitn(2, '=').next()?; + let _ = s.rsplitn(2, '=').nth(0)?; + + Some(()) +} + +fn _test_msrv() { + #![clippy::msrv = "1.51"] + // `manual_split_once` MSRV shouldn't apply to `needless_splitn` + let _ = "key=value".splitn(2, '=').nth(0).unwrap(); } diff --git a/src/tools/clippy/tests/ui/needless_splitn.stderr b/src/tools/clippy/tests/ui/needless_splitn.stderr index 66de2256554..f112b29e7f2 100644 --- a/src/tools/clippy/tests/ui/needless_splitn.stderr +++ b/src/tools/clippy/tests/ui/needless_splitn.stderr @@ -36,5 +36,47 @@ error: unnecessary use of `rsplitn` LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` -error: aborting due to 6 previous errors +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:28:13 + | +LL | let _ = str.splitn(5, '=').next(); + | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:29:13 + | +LL | let _ = str.splitn(5, '=').nth(3); + | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:35:13 + | +LL | let _ = s.splitn(2, '=').next()?; + | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:36:13 + | +LL | let _ = s.splitn(2, '=').nth(0)?; + | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` + +error: unnecessary use of `rsplitn` + --> $DIR/needless_splitn.rs:37:13 + | +LL | let _ = s.rsplitn(2, '=').next()?; + | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` + +error: unnecessary use of `rsplitn` + --> $DIR/needless_splitn.rs:38:13 + | +LL | let _ = s.rsplitn(2, '=').nth(0)?; + | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:46:13 + | +LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/new_without_default.rs b/src/tools/clippy/tests/ui/new_without_default.rs index e94f99c95f4..538927b1805 100644 --- a/src/tools/clippy/tests/ui/new_without_default.rs +++ b/src/tools/clippy/tests/ui/new_without_default.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, clippy::missing_safety_doc)] +#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)] #![warn(clippy::new_without_default)] pub struct Foo; diff --git a/src/tools/clippy/tests/ui/non_expressive_names.rs b/src/tools/clippy/tests/ui/non_expressive_names.rs index 9937005d68d..583096ac054 100644 --- a/src/tools/clippy/tests/ui/non_expressive_names.rs +++ b/src/tools/clippy/tests/ui/non_expressive_names.rs @@ -1,5 +1,5 @@ #![warn(clippy::all)] -#![allow(unused, clippy::println_empty_string, non_snake_case)] +#![allow(unused, clippy::println_empty_string, non_snake_case, clippy::let_unit_value)] #[derive(Clone, Debug)] enum MaybeInst { diff --git a/src/tools/clippy/tests/ui/numbered_fields.fixed b/src/tools/clippy/tests/ui/numbered_fields.fixed index 1da97e96879..3710b3e9c81 100644 --- a/src/tools/clippy/tests/ui/numbered_fields.fixed +++ b/src/tools/clippy/tests/ui/numbered_fields.fixed @@ -30,4 +30,9 @@ fn main() { // Ok because it's in macro let _ = tuple_struct_init!(); + + type Alias = TupleStruct; + + // Aliases can't be tuple constructed #8638 + let _ = Alias { 0: 0, 1: 1, 2: 2 }; } diff --git a/src/tools/clippy/tests/ui/numbered_fields.rs b/src/tools/clippy/tests/ui/numbered_fields.rs index 08ec405a560..2af84bc0642 100644 --- a/src/tools/clippy/tests/ui/numbered_fields.rs +++ b/src/tools/clippy/tests/ui/numbered_fields.rs @@ -38,4 +38,9 @@ fn main() { // Ok because it's in macro let _ = tuple_struct_init!(); + + type Alias = TupleStruct; + + // Aliases can't be tuple constructed #8638 + let _ = Alias { 0: 0, 1: 1, 2: 2 }; } diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed index 7790c816481..e12e13a57f1 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.fixed +++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::option_if_let_else)] -#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)] +#![allow( + clippy::redundant_closure, + clippy::ref_option_ref, + clippy::equatable_if_let, + clippy::let_unit_value +)] fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs index 3d9f76ee4a6..b5206fc26a9 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.rs +++ b/src/tools/clippy/tests/ui/option_if_let_else.rs @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::option_if_let_else)] -#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)] +#![allow( + clippy::redundant_closure, + clippy::ref_option_ref, + clippy::equatable_if_let, + clippy::let_unit_value +)] fn bad1(string: Option<&str>) -> (bool, &str) { if let Some(x) = string { diff --git a/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr index 546131ceb5b..40aef977b98 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.stderr +++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:6:5 + --> $DIR/option_if_let_else.rs:11:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -11,19 +11,19 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:24:13 + --> $DIR/option_if_let_else.rs:29:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:25:13 + --> $DIR/option_if_let_else.rs:30:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:26:13 + --> $DIR/option_if_let_else.rs:31:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -43,13 +43,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:32:13 + --> $DIR/option_if_let_else.rs:37:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:33:13 + --> $DIR/option_if_let_else.rs:38:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -69,7 +69,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:39:13 + --> $DIR/option_if_let_else.rs:44:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -89,7 +89,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:48:5 + --> $DIR/option_if_let_else.rs:53:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -108,7 +108,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:61:13 + --> $DIR/option_if_let_else.rs:66:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -120,7 +120,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:70:13 + --> $DIR/option_if_let_else.rs:75:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -143,7 +143,7 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:103:13 + --> $DIR/option_if_let_else.rs:108:13 | LL | / if let Some(idx) = s.find('.') { LL | | vec![s[..idx].to_string(), s[idx..].to_string()] @@ -153,13 +153,13 @@ LL | | } | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:127:13 + --> $DIR/option_if_let_else.rs:132:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:136:13 + --> $DIR/option_if_let_else.rs:141:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -181,13 +181,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:164:13 + --> $DIR/option_if_let_else.rs:169:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:168:13 + --> $DIR/option_if_let_else.rs:173:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ diff --git a/src/tools/clippy/tests/ui/option_take_on_temporary.fixed b/src/tools/clippy/tests/ui/option_take_on_temporary.fixed new file mode 100644 index 00000000000..29691e81666 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_take_on_temporary.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +fn main() { + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + println!("Testing erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); +} diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.fixed b/src/tools/clippy/tests/ui/or_then_unwrap.fixed index 6e0d5a87f68..844cc4b7a09 100644 --- a/src/tools/clippy/tests/ui/or_then_unwrap.fixed +++ b/src/tools/clippy/tests/ui/or_then_unwrap.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::or_then_unwrap)] -#![allow(clippy::map_identity)] +#![allow(clippy::map_identity, clippy::let_unit_value)] struct SomeStruct; impl SomeStruct { diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.rs b/src/tools/clippy/tests/ui/or_then_unwrap.rs index e406a71d46d..1528ef9be96 100644 --- a/src/tools/clippy/tests/ui/or_then_unwrap.rs +++ b/src/tools/clippy/tests/ui/or_then_unwrap.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::or_then_unwrap)] -#![allow(clippy::map_identity)] +#![allow(clippy::map_identity, clippy::let_unit_value)] struct SomeStruct; impl SomeStruct { diff --git a/src/tools/clippy/tests/ui/panicking_macros.rs b/src/tools/clippy/tests/ui/panicking_macros.rs index 12a0c776ae2..041ef17fa68 100644 --- a/src/tools/clippy/tests/ui/panicking_macros.rs +++ b/src/tools/clippy/tests/ui/panicking_macros.rs @@ -1,4 +1,4 @@ -#![allow(clippy::assertions_on_constants, clippy::eq_op)] +#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::let_unit_value)] #![feature(inline_const)] #![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] diff --git a/src/tools/clippy/tests/ui/pub_use.rs b/src/tools/clippy/tests/ui/pub_use.rs new file mode 100644 index 00000000000..65542bedec7 --- /dev/null +++ b/src/tools/clippy/tests/ui/pub_use.rs @@ -0,0 +1,14 @@ +#![warn(clippy::pub_use)] +#![allow(unused_imports)] +#![no_main] + +pub mod outer { + mod inner { + pub struct Test {} + } + // should be linted + pub use inner::Test; +} + +// should not be linted +use std::fmt; diff --git a/src/tools/clippy/tests/ui/pub_use.stderr b/src/tools/clippy/tests/ui/pub_use.stderr new file mode 100644 index 00000000000..9ab710df818 --- /dev/null +++ b/src/tools/clippy/tests/ui/pub_use.stderr @@ -0,0 +1,11 @@ +error: using `pub use` + --> $DIR/pub_use.rs:10:5 + | +LL | pub use inner::Test; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::pub-use` implied by `-D warnings` + = help: move the exported item to a public module instead + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed index 25f2fd061b8..106947de68c 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed @@ -104,4 +104,14 @@ mod m4 { pub use m4::*; +mod issue_8732 { + #[allow(unused_macros)] + macro_rules! some_macro { + () => {}; + } + + #[allow(unused_imports)] + pub(crate) use some_macro; // ok: macro exports are exempt +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.rs b/src/tools/clippy/tests/ui/redundant_pub_crate.rs index 616286b4f39..f96cfd31843 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.rs +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.rs @@ -104,4 +104,14 @@ mod m4 { pub use m4::*; +mod issue_8732 { + #[allow(unused_macros)] + macro_rules! some_macro { + () => {}; + } + + #[allow(unused_imports)] + pub(crate) use some_macro; // ok: macro exports are exempt +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 24a0c812291..9c4079ad6d3 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -1,71 +1,70 @@ -//! Test for Clippy lint renames. +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + // run-rustfix -#![allow(dead_code)] -// allow the new lint name here, to test if the new name works -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::new_without_default)] +#![allow(clippy::blocks_in_if_conditions)] +#![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] +#![allow(clippy::disallowed_methods)] +#![allow(clippy::disallowed_types)] +#![allow(clippy::for_loops_over_fallibles)] +#![allow(clippy::useless_conversion)] +#![allow(clippy::match_result_ok)] +#![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] -#![allow(clippy::box_collection)] -#![allow(clippy::blocks_in_if_conditions)] +#![allow(clippy::expect_used)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::unwrap_used)] -#![allow(clippy::expect_used)] -#![allow(clippy::for_loops_over_fallibles)] -#![allow(clippy::useless_conversion)] -#![allow(clippy::invisible_characters)] +#![allow(clippy::needless_borrow)] #![allow(clippy::single_char_add_str)] -#![allow(clippy::match_result_ok)] -#![allow(clippy::disallowed_types)] -#![allow(clippy::disallowed_methods)] +#![allow(clippy::module_name_repetitions)] #![allow(clippy::recursive_format_impl)] -// uplifted lints -#![allow(invalid_value)] -#![allow(array_into_iter)] -#![allow(unused_labels)] +#![allow(clippy::invisible_characters)] #![allow(drop_bounds)] -#![allow(temporary_cstring_as_ptr)] -#![allow(non_fmt_panics)] -#![allow(unknown_lints)] +#![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] +#![allow(invalid_value)] #![allow(enum_intrinsics_non_enums)] -// warn for the old lint name here, to test if the renaming worked -#![warn(clippy::module_name_repetitions)] -#![warn(clippy::new_without_default)] +#![allow(non_fmt_panics)] +#![allow(temporary_cstring_as_ptr)] +#![allow(unknown_lints)] +#![allow(unused_labels)] +#![warn(clippy::blocks_in_if_conditions)] +#![warn(clippy::blocks_in_if_conditions)] +#![warn(clippy::box_collection)] #![warn(clippy::redundant_static_lifetimes)] #![warn(clippy::cognitive_complexity)] +#![warn(clippy::disallowed_methods)] +#![warn(clippy::disallowed_types)] +#![warn(clippy::for_loops_over_fallibles)] +#![warn(clippy::for_loops_over_fallibles)] +#![warn(clippy::useless_conversion)] +#![warn(clippy::match_result_ok)] +#![warn(clippy::new_without_default)] #![warn(clippy::bind_instead_of_map)] -#![warn(clippy::box_collection)] -#![warn(clippy::blocks_in_if_conditions)] -#![warn(clippy::blocks_in_if_conditions)] -#![warn(clippy::map_unwrap_or)] +#![warn(clippy::expect_used)] #![warn(clippy::map_unwrap_or)] #![warn(clippy::map_unwrap_or)] #![warn(clippy::unwrap_used)] -#![warn(clippy::unwrap_used)] -#![warn(clippy::expect_used)] +#![warn(clippy::needless_borrow)] #![warn(clippy::expect_used)] -#![warn(clippy::for_loops_over_fallibles)] -#![warn(clippy::for_loops_over_fallibles)] -#![warn(clippy::useless_conversion)] -#![warn(clippy::invisible_characters)] +#![warn(clippy::map_unwrap_or)] +#![warn(clippy::unwrap_used)] #![warn(clippy::single_char_add_str)] -#![warn(clippy::match_result_ok)] -#![warn(clippy::disallowed_types)] -#![warn(clippy::disallowed_methods)] -#![warn(clippy::needless_borrow)] +#![warn(clippy::module_name_repetitions)] #![warn(clippy::recursive_format_impl)] -// uplifted lints -#![warn(invalid_value)] -#![warn(array_into_iter)] -#![warn(unused_labels)] +#![warn(clippy::invisible_characters)] #![warn(drop_bounds)] -#![warn(temporary_cstring_as_ptr)] -#![warn(non_fmt_panics)] -#![warn(unknown_lints)] +#![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] +#![warn(invalid_value)] #![warn(enum_intrinsics_non_enums)] +#![warn(non_fmt_panics)] +#![warn(temporary_cstring_as_ptr)] +#![warn(unknown_lints)] +#![warn(unused_labels)] fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index ea64234c680..e83e66b7fbd 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -1,71 +1,70 @@ -//! Test for Clippy lint renames. +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + // run-rustfix -#![allow(dead_code)] -// allow the new lint name here, to test if the new name works -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::new_without_default)] +#![allow(clippy::blocks_in_if_conditions)] +#![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] +#![allow(clippy::disallowed_methods)] +#![allow(clippy::disallowed_types)] +#![allow(clippy::for_loops_over_fallibles)] +#![allow(clippy::useless_conversion)] +#![allow(clippy::match_result_ok)] +#![allow(clippy::new_without_default)] #![allow(clippy::bind_instead_of_map)] -#![allow(clippy::box_collection)] -#![allow(clippy::blocks_in_if_conditions)] +#![allow(clippy::expect_used)] #![allow(clippy::map_unwrap_or)] #![allow(clippy::unwrap_used)] -#![allow(clippy::expect_used)] -#![allow(clippy::for_loops_over_fallibles)] -#![allow(clippy::useless_conversion)] -#![allow(clippy::invisible_characters)] +#![allow(clippy::needless_borrow)] #![allow(clippy::single_char_add_str)] -#![allow(clippy::match_result_ok)] -#![allow(clippy::disallowed_types)] -#![allow(clippy::disallowed_methods)] +#![allow(clippy::module_name_repetitions)] #![allow(clippy::recursive_format_impl)] -// uplifted lints -#![allow(invalid_value)] -#![allow(array_into_iter)] -#![allow(unused_labels)] +#![allow(clippy::invisible_characters)] #![allow(drop_bounds)] -#![allow(temporary_cstring_as_ptr)] -#![allow(non_fmt_panics)] -#![allow(unknown_lints)] +#![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] +#![allow(invalid_value)] #![allow(enum_intrinsics_non_enums)] -// warn for the old lint name here, to test if the renaming worked -#![warn(clippy::stutter)] -#![warn(clippy::new_without_default_derive)] +#![allow(non_fmt_panics)] +#![allow(temporary_cstring_as_ptr)] +#![allow(unknown_lints)] +#![allow(unused_labels)] +#![warn(clippy::block_in_if_condition_expr)] +#![warn(clippy::block_in_if_condition_stmt)] +#![warn(clippy::box_vec)] #![warn(clippy::const_static_lifetime)] #![warn(clippy::cyclomatic_complexity)] +#![warn(clippy::disallowed_method)] +#![warn(clippy::disallowed_type)] +#![warn(clippy::for_loop_over_option)] +#![warn(clippy::for_loop_over_result)] +#![warn(clippy::identity_conversion)] +#![warn(clippy::if_let_some_result)] +#![warn(clippy::new_without_default_derive)] #![warn(clippy::option_and_then_some)] -#![warn(clippy::box_vec)] -#![warn(clippy::block_in_if_condition_expr)] -#![warn(clippy::block_in_if_condition_stmt)] +#![warn(clippy::option_expect_used)] #![warn(clippy::option_map_unwrap_or)] #![warn(clippy::option_map_unwrap_or_else)] -#![warn(clippy::result_map_unwrap_or_else)] #![warn(clippy::option_unwrap_used)] -#![warn(clippy::result_unwrap_used)] -#![warn(clippy::option_expect_used)] +#![warn(clippy::ref_in_deref)] #![warn(clippy::result_expect_used)] -#![warn(clippy::for_loop_over_option)] -#![warn(clippy::for_loop_over_result)] -#![warn(clippy::identity_conversion)] -#![warn(clippy::zero_width_space)] +#![warn(clippy::result_map_unwrap_or_else)] +#![warn(clippy::result_unwrap_used)] #![warn(clippy::single_char_push_str)] -#![warn(clippy::if_let_some_result)] -#![warn(clippy::disallowed_type)] -#![warn(clippy::disallowed_method)] -#![warn(clippy::ref_in_deref)] +#![warn(clippy::stutter)] #![warn(clippy::to_string_in_display)] -// uplifted lints -#![warn(clippy::invalid_ref)] -#![warn(clippy::into_iter_on_array)] -#![warn(clippy::unused_label)] +#![warn(clippy::zero_width_space)] #![warn(clippy::drop_bounds)] -#![warn(clippy::temporary_cstring_as_ptr)] -#![warn(clippy::panic_params)] -#![warn(clippy::unknown_clippy_lints)] +#![warn(clippy::into_iter_on_array)] #![warn(clippy::invalid_atomic_ordering)] +#![warn(clippy::invalid_ref)] #![warn(clippy::mem_discriminant_non_enum)] +#![warn(clippy::panic_params)] +#![warn(clippy::temporary_cstring_as_ptr)] +#![warn(clippy::unknown_clippy_lints)] +#![warn(clippy::unused_label)] fn main() {} diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index 8b132a78384..f811b10d017 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,82 +1,82 @@ -error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` +error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` --> $DIR/rename.rs:35:9 | -LL | #![warn(clippy::stutter)] - | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` +LL | #![warn(clippy::block_in_if_condition_expr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` | = note: `-D renamed-and-removed-lints` implied by `-D warnings` -error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` +error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` --> $DIR/rename.rs:36:9 | -LL | #![warn(clippy::new_without_default_derive)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` +LL | #![warn(clippy::block_in_if_condition_stmt)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` -error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` +error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` --> $DIR/rename.rs:37:9 | +LL | #![warn(clippy::box_vec)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` + +error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` + --> $DIR/rename.rs:38:9 + | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:38:9 + --> $DIR/rename.rs:39:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` -error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:39:9 - | -LL | #![warn(clippy::option_and_then_some)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` - -error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` +error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` --> $DIR/rename.rs:40:9 | -LL | #![warn(clippy::box_vec)] - | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` +LL | #![warn(clippy::disallowed_method)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` -error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` +error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` --> $DIR/rename.rs:41:9 | -LL | #![warn(clippy::block_in_if_condition_expr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` +LL | #![warn(clippy::disallowed_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` -error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` +error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` --> $DIR/rename.rs:42:9 | -LL | #![warn(clippy::block_in_if_condition_stmt)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` +LL | #![warn(clippy::for_loop_over_option)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` -error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` --> $DIR/rename.rs:43:9 | -LL | #![warn(clippy::option_map_unwrap_or)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::for_loop_over_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` -error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` --> $DIR/rename.rs:44:9 | -LL | #![warn(clippy::option_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::identity_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` -error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` +error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` --> $DIR/rename.rs:45:9 | -LL | #![warn(clippy::result_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` +LL | #![warn(clippy::if_let_some_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` -error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` +error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` --> $DIR/rename.rs:46:9 | -LL | #![warn(clippy::option_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` +LL | #![warn(clippy::new_without_default_derive)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` -error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` +error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` --> $DIR/rename.rs:47:9 | -LL | #![warn(clippy::result_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` +LL | #![warn(clippy::option_and_then_some)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` --> $DIR/rename.rs:48:9 @@ -84,107 +84,113 @@ error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_use LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` -error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` +error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` --> $DIR/rename.rs:49:9 | -LL | #![warn(clippy::result_expect_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` +LL | #![warn(clippy::option_map_unwrap_or)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` --> $DIR/rename.rs:50:9 | -LL | #![warn(clippy::for_loop_over_option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` +LL | #![warn(clippy::option_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` --> $DIR/rename.rs:51:9 | -LL | #![warn(clippy::for_loop_over_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` +LL | #![warn(clippy::option_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` -error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` +error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` --> $DIR/rename.rs:52:9 | -LL | #![warn(clippy::identity_conversion)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` +LL | #![warn(clippy::ref_in_deref)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` -error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` +error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` --> $DIR/rename.rs:53:9 | -LL | #![warn(clippy::zero_width_space)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` +LL | #![warn(clippy::result_expect_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` -error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` +error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` --> $DIR/rename.rs:54:9 | -LL | #![warn(clippy::single_char_push_str)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` +LL | #![warn(clippy::result_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` +error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` --> $DIR/rename.rs:55:9 | -LL | #![warn(clippy::if_let_some_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` +LL | #![warn(clippy::result_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` -error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` +error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` --> $DIR/rename.rs:56:9 | -LL | #![warn(clippy::disallowed_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` +LL | #![warn(clippy::single_char_push_str)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` -error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` +error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` --> $DIR/rename.rs:57:9 | -LL | #![warn(clippy::disallowed_method)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` +LL | #![warn(clippy::stutter)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` -error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` +error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` --> $DIR/rename.rs:58:9 | -LL | #![warn(clippy::ref_in_deref)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` +LL | #![warn(clippy::to_string_in_display)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` -error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` +error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` --> $DIR/rename.rs:59:9 | -LL | #![warn(clippy::to_string_in_display)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` +LL | #![warn(clippy::zero_width_space)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` -error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:61:9 +error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` + --> $DIR/rename.rs:60:9 | -LL | #![warn(clippy::invalid_ref)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` +LL | #![warn(clippy::drop_bounds)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` -error: lint `clippy::unused_label` has been renamed to `unused_labels` +error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` + --> $DIR/rename.rs:62:9 + | +LL | #![warn(clippy::invalid_atomic_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` + +error: lint `clippy::invalid_ref` has been renamed to `invalid_value` --> $DIR/rename.rs:63:9 | -LL | #![warn(clippy::unused_label)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` +LL | #![warn(clippy::invalid_ref)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` -error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` +error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` --> $DIR/rename.rs:64:9 | -LL | #![warn(clippy::drop_bounds)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` +LL | #![warn(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` -error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` +error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` --> $DIR/rename.rs:65:9 | -LL | #![warn(clippy::temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` +LL | #![warn(clippy::panic_params)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` -error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` --> $DIR/rename.rs:66:9 | -LL | #![warn(clippy::panic_params)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` +LL | #![warn(clippy::temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` --> $DIR/rename.rs:67:9 @@ -192,17 +198,11 @@ error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` -error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` +error: lint `clippy::unused_label` has been renamed to `unused_labels` --> $DIR/rename.rs:68:9 | -LL | #![warn(clippy::invalid_atomic_ordering)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` - -error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:69:9 - | -LL | #![warn(clippy::mem_discriminant_non_enum)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` +LL | #![warn(clippy::unused_label)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: aborting due to 34 previous errors diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs index 38fc9969804..086331af6b5 100644 --- a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs +++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.rs @@ -39,4 +39,19 @@ fn main() { // No lint foo!(a_struct); + + #[non_exhaustive] + struct B { + a: u32, + b: u32, + c: u64, + } + + let b_struct = B { a: 5, b: 42, c: 342 }; + + match b_struct { + B { a: 5, b: 42, .. } => {}, + B { a: 0, b: 0, c: 128, .. } => {}, // No Lint + _ => {}, + } } diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs index 7f28f025790..3d2295912c9 100644 --- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs @@ -1,3 +1,5 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] #![warn(clippy::same_functions_in_if_condition)] #![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`. #![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks @@ -87,4 +89,21 @@ fn main() { "linux" }; println!("{}", os); + + #[derive(PartialEq, Eq)] + enum E { + A, + B, + } + fn generic<const P: E>() -> bool { + match P { + E::A => true, + E::B => false, + } + } + if generic::<{ E::A }>() { + println!("A"); + } else if generic::<{ E::B }>() { + println!("B"); + } } diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr index 363a03846d2..71e82910ef7 100644 --- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr @@ -1,72 +1,72 @@ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:29:15 + --> $DIR/same_functions_in_if_condition.rs:31:15 | LL | } else if function() { | ^^^^^^^^^^ | = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings` note: same as this - --> $DIR/same_functions_in_if_condition.rs:28:8 + --> $DIR/same_functions_in_if_condition.rs:30:8 | LL | if function() { | ^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:34:15 + --> $DIR/same_functions_in_if_condition.rs:36:15 | LL | } else if fn_arg(a) { | ^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:33:8 + --> $DIR/same_functions_in_if_condition.rs:35:8 | LL | if fn_arg(a) { | ^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:39:15 + --> $DIR/same_functions_in_if_condition.rs:41:15 | LL | } else if obj.method() { | ^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:38:8 + --> $DIR/same_functions_in_if_condition.rs:40:8 | LL | if obj.method() { | ^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:44:15 + --> $DIR/same_functions_in_if_condition.rs:46:15 | LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:43:8 + --> $DIR/same_functions_in_if_condition.rs:45:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:51:15 + --> $DIR/same_functions_in_if_condition.rs:53:15 | LL | } else if v.pop() == None { | ^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:49:8 + --> $DIR/same_functions_in_if_condition.rs:51:8 | LL | if v.pop() == None { | ^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:56:15 + --> $DIR/same_functions_in_if_condition.rs:58:15 | LL | } else if v.len() == 42 { | ^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:54:8 + --> $DIR/same_functions_in_if_condition.rs:56:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/shadow.rs b/src/tools/clippy/tests/ui/shadow.rs index 0321f8c4cdf..a394ef8f25c 100644 --- a/src/tools/clippy/tests/ui/shadow.rs +++ b/src/tools/clippy/tests/ui/shadow.rs @@ -1,4 +1,5 @@ #![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)] +#![allow(clippy::let_unit_value)] fn shadow_same() { let x = 1; diff --git a/src/tools/clippy/tests/ui/shadow.stderr b/src/tools/clippy/tests/ui/shadow.stderr index f8b9221d555..3bd41d06260 100644 --- a/src/tools/clippy/tests/ui/shadow.stderr +++ b/src/tools/clippy/tests/ui/shadow.stderr @@ -1,266 +1,266 @@ error: `x` is shadowed by itself in `x` - --> $DIR/shadow.rs:5:9 + --> $DIR/shadow.rs:6:9 | LL | let x = x; | ^ | = note: `-D clippy::shadow-same` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:4:9 + --> $DIR/shadow.rs:5:9 | LL | let x = 1; | ^ error: `mut x` is shadowed by itself in `&x` - --> $DIR/shadow.rs:6:13 + --> $DIR/shadow.rs:7:13 | LL | let mut x = &x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:5:9 + --> $DIR/shadow.rs:6:9 | LL | let x = x; | ^ error: `x` is shadowed by itself in `&mut x` - --> $DIR/shadow.rs:7:9 + --> $DIR/shadow.rs:8:9 | LL | let x = &mut x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:6:9 + --> $DIR/shadow.rs:7:9 | LL | let mut x = &x; | ^^^^^ error: `x` is shadowed by itself in `*x` - --> $DIR/shadow.rs:8:9 + --> $DIR/shadow.rs:9:9 | LL | let x = *x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:7:9 + --> $DIR/shadow.rs:8:9 | LL | let x = &mut x; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:13:9 + --> $DIR/shadow.rs:14:9 | LL | let x = x.0; | ^ | = note: `-D clippy::shadow-reuse` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:12:9 + --> $DIR/shadow.rs:13:9 | LL | let x = ([[0]], ()); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:14:9 + --> $DIR/shadow.rs:15:9 | LL | let x = x[0]; | ^ | note: previous binding is here - --> $DIR/shadow.rs:13:9 + --> $DIR/shadow.rs:14:9 | LL | let x = x.0; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:15:10 + --> $DIR/shadow.rs:16:10 | LL | let [x] = x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:14:9 + --> $DIR/shadow.rs:15:9 | LL | let x = x[0]; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:16:9 + --> $DIR/shadow.rs:17:9 | LL | let x = Some(x); | ^ | note: previous binding is here - --> $DIR/shadow.rs:15:10 + --> $DIR/shadow.rs:16:10 | LL | let [x] = x; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:17:9 + --> $DIR/shadow.rs:18:9 | LL | let x = foo(x); | ^ | note: previous binding is here - --> $DIR/shadow.rs:16:9 + --> $DIR/shadow.rs:17:9 | LL | let x = Some(x); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:18:9 + --> $DIR/shadow.rs:19:9 | LL | let x = || x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:17:9 + --> $DIR/shadow.rs:18:9 | LL | let x = foo(x); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:19:9 + --> $DIR/shadow.rs:20:9 | LL | let x = Some(1).map(|_| x)?; | ^ | note: previous binding is here - --> $DIR/shadow.rs:18:9 + --> $DIR/shadow.rs:19:9 | LL | let x = || x; | ^ error: `y` is shadowed - --> $DIR/shadow.rs:21:9 + --> $DIR/shadow.rs:22:9 | LL | let y = match y { | ^ | note: previous binding is here - --> $DIR/shadow.rs:20:9 + --> $DIR/shadow.rs:21:9 | LL | let y = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:30:9 + --> $DIR/shadow.rs:31:9 | LL | let x = 2; | ^ | = note: `-D clippy::shadow-unrelated` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:29:9 + --> $DIR/shadow.rs:30:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:35:13 + --> $DIR/shadow.rs:36:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:34:10 + --> $DIR/shadow.rs:35:10 | LL | fn f(x: u32) { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:40:14 + --> $DIR/shadow.rs:41:14 | LL | Some(x) => { | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:41:17 + --> $DIR/shadow.rs:42:17 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:40:14 + --> $DIR/shadow.rs:41:14 | LL | Some(x) => { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:45:17 + --> $DIR/shadow.rs:46:17 | LL | if let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:46:20 + --> $DIR/shadow.rs:47:20 | LL | while let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:47:15 + --> $DIR/shadow.rs:48:15 | LL | let _ = |[x]: [u32; 1]| { | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:48:13 + --> $DIR/shadow.rs:49:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:47:15 + --> $DIR/shadow.rs:48:15 | LL | let _ = |[x]: [u32; 1]| { | ^ error: `y` is shadowed - --> $DIR/shadow.rs:51:17 + --> $DIR/shadow.rs:52:17 | LL | if let Some(y) = y {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:50:9 + --> $DIR/shadow.rs:51:9 | LL | let y = Some(1); | ^ error: `_b` shadows a previous, unrelated binding - --> $DIR/shadow.rs:87:9 + --> $DIR/shadow.rs:88:9 | LL | let _b = _a; | ^^ | note: previous binding is here - --> $DIR/shadow.rs:86:28 + --> $DIR/shadow.rs:87:28 | LL | pub async fn foo2(_a: i32, _b: i64) { | ^^ diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs new file mode 100644 index 00000000000..c4a3301e722 --- /dev/null +++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs @@ -0,0 +1,526 @@ +// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934 +// // run-rustfix + +#![warn(clippy::significant_drop_in_scrutinee)] +#![allow(clippy::single_match)] +#![allow(clippy::match_single_binding)] +#![allow(unused_assignments)] +#![allow(dead_code)] + +use std::ops::Deref; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{Mutex, MutexGuard}; + +struct State {} + +impl State { + fn foo(&self) -> bool { + true + } + + fn bar(&self) {} +} + +fn should_not_trigger_lint_with_mutex_guard_outside_match() { + let mutex = Mutex::new(State {}); + + // Should not trigger lint because the temporary should drop at the `;` on line before the match + let is_foo = mutex.lock().unwrap().foo(); + match is_foo { + true => { + mutex.lock().unwrap().bar(); + } + false => {} + }; +} + +fn should_not_trigger_lint_with_mutex_guard_when_taking_ownership_in_match() { + let mutex = Mutex::new(State {}); + + // Should not trigger lint because the scrutinee is explicitly returning the MutexGuard, + // so its lifetime should not be surprising. + match mutex.lock() { + Ok(guard) => { + guard.foo(); + mutex.lock().unwrap().bar(); + } + _ => {} + }; +} + +fn should_trigger_lint_with_mutex_guard_in_match_scrutinee() { + let mutex = Mutex::new(State {}); + + // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it + // is preserved until the end of the match, but there is no clear indication that this is the + // case. + match mutex.lock().unwrap().foo() { + true => { + mutex.lock().unwrap().bar(); + } + false => {} + }; +} + +fn should_not_trigger_lint_for_insignificant_drop() { + // Should not trigger lint because there are no temporaries whose drops have a significant + // side effect. + match 1u64.to_string().is_empty() { + true => { + println!("It was empty") + } + false => { + println!("It was not empty") + } + } +} + +struct StateWithMutex { + m: Mutex<u64>, +} + +struct MutexGuardWrapper<'a> { + mg: MutexGuard<'a, u64>, +} + +impl<'a> MutexGuardWrapper<'a> { + fn get_the_value(&self) -> u64 { + *self.mg.deref() + } +} + +struct MutexGuardWrapperWrapper<'a> { + mg: MutexGuardWrapper<'a>, +} + +impl<'a> MutexGuardWrapperWrapper<'a> { + fn get_the_value(&self) -> u64 { + *self.mg.mg.deref() + } +} + +impl StateWithMutex { + fn lock_m(&self) -> MutexGuardWrapper<'_> { + MutexGuardWrapper { + mg: self.m.lock().unwrap(), + } + } + + fn lock_m_m(&self) -> MutexGuardWrapperWrapper<'_> { + MutexGuardWrapperWrapper { + mg: MutexGuardWrapper { + mg: self.m.lock().unwrap(), + }, + } + } + + fn foo(&self) -> bool { + true + } + + fn bar(&self) {} +} + +fn should_trigger_lint_with_wrapped_mutex() { + let s = StateWithMutex { m: Mutex::new(1) }; + + // Should trigger lint because a temporary contains a type with a significant drop and its + // lifetime is not obvious. Additionally, it is not obvious from looking at the scrutinee that + // the temporary contains such a type, making it potentially even more surprising. + match s.lock_m().get_the_value() { + 1 => { + println!("Got 1. Is it still 1?"); + println!("{}", s.lock_m().get_the_value()); + } + 2 => { + println!("Got 2. Is it still 2?"); + println!("{}", s.lock_m().get_the_value()); + } + _ => {} + } + println!("All done!"); +} + +fn should_trigger_lint_with_double_wrapped_mutex() { + let s = StateWithMutex { m: Mutex::new(1) }; + + // Should trigger lint because a temporary contains a type which further contains a type with a + // significant drop and its lifetime is not obvious. Additionally, it is not obvious from + // looking at the scrutinee that the temporary contains such a type, making it potentially even + // more surprising. + match s.lock_m_m().get_the_value() { + 1 => { + println!("Got 1. Is it still 1?"); + println!("{}", s.lock_m().get_the_value()); + } + 2 => { + println!("Got 2. Is it still 2?"); + println!("{}", s.lock_m().get_the_value()); + } + _ => {} + } + println!("All done!"); +} + +struct Counter { + i: AtomicU64, +} + +#[clippy::has_significant_drop] +struct CounterWrapper<'a> { + counter: &'a Counter, +} + +impl<'a> CounterWrapper<'a> { + fn new(counter: &Counter) -> CounterWrapper { + counter.i.fetch_add(1, Ordering::Relaxed); + CounterWrapper { counter } + } +} + +impl<'a> Drop for CounterWrapper<'a> { + fn drop(&mut self) { + self.counter.i.fetch_sub(1, Ordering::Relaxed); + } +} + +impl Counter { + fn temp_increment(&self) -> Vec<CounterWrapper> { + vec![CounterWrapper::new(self), CounterWrapper::new(self)] + } +} + +fn should_trigger_lint_for_vec() { + let counter = Counter { i: AtomicU64::new(0) }; + + // Should trigger lint because the temporary in the scrutinee returns a collection of types + // which have significant drops. The types with significant drops are also non-obvious when + // reading the expression in the scrutinee. + match counter.temp_increment().len() { + 2 => { + let current_count = counter.i.load(Ordering::Relaxed); + println!("Current count {}", current_count); + assert_eq!(current_count, 0); + } + 1 => {} + 3 => {} + _ => {} + }; +} + +struct StateWithField { + s: String, +} + +// Should trigger lint only on the type in the tuple which is created using a temporary +// with a significant drop. Additionally, this test ensures that the format of the tuple +// is preserved correctly in the suggestion. +fn should_trigger_lint_for_tuple_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + + { + match (mutex1.lock().unwrap().s.len(), true) { + (3, _) => { + println!("started"); + mutex1.lock().unwrap().s.len(); + println!("done"); + } + (_, _) => {} + }; + + match (true, mutex1.lock().unwrap().s.len(), true) { + (_, 3, _) => { + println!("started"); + mutex1.lock().unwrap().s.len(); + println!("done"); + } + (_, _, _) => {} + }; + + let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() }); + match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { + (3, _, 3) => { + println!("started"); + mutex1.lock().unwrap().s.len(); + mutex2.lock().unwrap().s.len(); + println!("done"); + } + (_, _, _) => {} + }; + + let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() }); + match mutex3.lock().unwrap().s.as_str() { + "three" => { + println!("started"); + mutex1.lock().unwrap().s.len(); + mutex2.lock().unwrap().s.len(); + println!("done"); + } + _ => {} + }; + + + match (true, mutex3.lock().unwrap().s.as_str()) { + (_, "three") => { + println!("started"); + mutex1.lock().unwrap().s.len(); + mutex2.lock().unwrap().s.len(); + println!("done"); + } + (_, _) => {} + }; + } +} + +// Should trigger lint when either side of a binary operation creates a temporary with a +// significant drop. +// To avoid potential unnecessary copies or creating references that would trigger the significant +// drop problem, the lint recommends moving the entire binary operation. +fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() { + let mutex = Mutex::new(StateWithField { s: "state".to_owned() }); + + match mutex.lock().unwrap().s.len() > 1 { + true => { + mutex.lock().unwrap().s.len(); + } + false => {} + }; + + match 1 < mutex.lock().unwrap().s.len() { + true => { + mutex.lock().unwrap().s.len(); + } + false => {} + }; +} + +// Should trigger lint when both sides of a binary operation creates a temporary with a +// significant drop. +// To avoid potential unnecessary copies or creating references that would trigger the significant +// drop problem, the lint recommends moving the entire binary operation. +fn should_trigger_lint_for_accessing_fields_in_mutex_in_both_sides_of_binary_op() { + let mutex1 = Mutex::new(StateWithField { s: "state".to_owned() }); + let mutex2 = Mutex::new(StateWithField { s: "statewithfield".to_owned() }); + + match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { + true => { + println!("{} < {}", mutex1.lock().unwrap().s.len(), mutex2.lock().unwrap().s.len()); + } + false => {} + }; + + match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { + true => { + println!("{} >= {}", mutex1.lock().unwrap().s.len(), mutex2.lock().unwrap().s.len()); + } + false => {} + }; +} + +fn should_not_trigger_lint_for_closure_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + + let get_mutex_guard = || mutex1.lock().unwrap().s.len(); + + // Should not trigger lint because the temporary with a significant drop will be dropped + // at the end of the closure, so the MutexGuard will be unlocked and not have a potentially + // surprising lifetime. + match get_mutex_guard() > 1 { + true => { + mutex1.lock().unwrap().s.len(); + } + false => {} + }; +} + +fn should_trigger_lint_for_return_from_closure_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + + let get_mutex_guard = || mutex1.lock().unwrap(); + + // Should trigger lint because the temporary with a significant drop is returned from the + // closure but not used directly in any match arms, so it has a potentially surprising lifetime. + match get_mutex_guard().s.len() > 1 { + true => { + mutex1.lock().unwrap().s.len(); + } + false => {} + }; +} + +fn should_trigger_lint_for_return_from_match_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() }); + + let i = 100; + + // Should trigger lint because the nested match within the scrutinee returns a temporary with a + // significant drop is but not used directly in any match arms, so it has a potentially + // surprising lifetime. + match match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1 { + true => { + mutex1.lock().unwrap().s.len(); + } + false => { + println!("nothing to do here"); + } + }; +} + +fn should_trigger_lint_for_return_from_if_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() }); + + let i = 100; + + // Should trigger lint because the nested if-expression within the scrutinee returns a temporary + // with a significant drop is but not used directly in any match arms, so it has a potentially + // surprising lifetime. + match if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1 { + true => { + mutex1.lock().unwrap().s.len(); + } + false => {} + }; +} + +fn should_not_trigger_lint_for_if_in_scrutinee() { + let mutex = Mutex::new(StateWithField { s: "state".to_owned() }); + + let i = 100; + + // Should not trigger the lint because the temporary with a significant drop *is* dropped within + // the body of the if-expression nested within the match scrutinee, and therefore does not have + // a potentially surprising lifetime. + match if i > 1 { mutex.lock().unwrap().s.len() > 1 } else { false } { + true => { + mutex.lock().unwrap().s.len(); + } + false => {} + }; +} + +struct StateWithBoxedMutexGuard { + u: Mutex<u64>, +} + +impl StateWithBoxedMutexGuard { + fn new() -> StateWithBoxedMutexGuard { + StateWithBoxedMutexGuard { u: Mutex::new(42) } + } + fn lock(&self) -> Box<MutexGuard<u64>> { + Box::new(self.u.lock().unwrap()) + } +} + +fn should_trigger_lint_for_boxed_mutex_guard() { + let s = StateWithBoxedMutexGuard::new(); + + // Should trigger lint because a temporary Box holding a type with a significant drop in a match + // scrutinee may have a potentially surprising lifetime. + match s.lock().deref().deref() { + 0 | 1 => println!("Value was less than 2"), + _ => println!("Value is {}", s.lock().deref()), + }; +} + +struct StateStringWithBoxedMutexGuard { + s: Mutex<String>, +} + +impl StateStringWithBoxedMutexGuard { + fn new() -> StateStringWithBoxedMutexGuard { + StateStringWithBoxedMutexGuard { s: Mutex::new("A String".to_owned()) } + } + fn lock(&self) -> Box<MutexGuard<String>> { + Box::new(self.s.lock().unwrap()) + } +} + +fn should_trigger_lint_for_boxed_mutex_guard_holding_string() { + let s = StateStringWithBoxedMutexGuard::new(); + + let matcher = String::from("A String"); + + // Should trigger lint because a temporary Box holding a type with a significant drop in a match + // scrutinee may have a potentially surprising lifetime. + match s.lock().deref().deref() { + matcher => println!("Value is {}", s.lock().deref()), + _ => println!("Value was not a match"), + }; +} + + +struct StateWithIntField { + i: u64, +} + +// Should trigger lint when either side of an assign expression contains a temporary with a +// significant drop, because the temporary's lifetime will be extended to the end of the match. +// To avoid potential unnecessary copies or creating references that would trigger the significant +// drop problem, the lint recommends moving the entire binary operation. +fn should_trigger_lint_in_assign_expr() { + let mutex = Mutex::new(StateWithIntField { i: 10 }); + + let mut i = 100; + + match mutex.lock().unwrap().i = i { + _ => { + println!("{}", mutex.lock().unwrap().i); + } + }; + + match i = mutex.lock().unwrap().i { + _ => { + println!("{}", mutex.lock().unwrap().i); + } + }; + + match mutex.lock().unwrap().i += 1 { + _ => { + println!("{}", mutex.lock().unwrap().i); + } + }; + + match i += mutex.lock().unwrap().i { + _ => { + println!("{}", mutex.lock().unwrap().i); + } + }; +} + +#[derive(Debug)] +enum RecursiveEnum { + Foo(Option<Box<RecursiveEnum>>) +} + +#[derive(Debug)] +enum GenericRecursiveEnum<T> { + Foo(T, Option<Box<GenericRecursiveEnum<T>>>) +} + +fn should_not_cause_stack_overflow() { + // Test that when a type recursively contains itself, a stack overflow does not occur when + // checking sub-types for significant drops. + let f = RecursiveEnum::Foo(Some(Box::new(RecursiveEnum::Foo(None)))); + match f { + RecursiveEnum::Foo(Some(f)) => { + println!("{:?}", f) + } + RecursiveEnum::Foo(f) => { + println!("{:?}", f) + } + } + + let f = GenericRecursiveEnum::Foo(1u64, Some(Box::new(GenericRecursiveEnum::Foo(2u64, None)))); + match f { + GenericRecursiveEnum::Foo(i, Some(f)) => { + println!("{} {:?}", i, f) + } + GenericRecursiveEnum::Foo(i, f) => { + println!("{} {:?}", i, f) + } + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr new file mode 100644 index 00000000000..c442e93f539 --- /dev/null +++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr @@ -0,0 +1,261 @@ +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:57:11 + | +LL | match mutex.lock().unwrap().foo() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::significant-drop-in-scrutinee` implied by `-D warnings` +help: try moving the temporary above the match + | +LL ~ let value = mutex.lock().unwrap().foo(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:130:11 + | +LL | match s.lock_m().get_the_value() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = s.lock_m().get_the_value(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:151:11 + | +LL | match s.lock_m_m().get_the_value() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = s.lock_m_m().get_the_value(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:199:11 + | +LL | match counter.temp_increment().len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = counter.temp_increment().len(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:222:16 + | +LL | match (mutex1.lock().unwrap().s.len(), true) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len(); +LL ~ match (value, true) { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:231:22 + | +LL | match (true, mutex1.lock().unwrap().s.len(), true) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len(); +LL ~ match (true, value, true) { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:241:16 + | +LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len(); +LL ~ match (value, true, mutex2.lock().unwrap().s.len()) { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:241:54 + | +LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex2.lock().unwrap().s.len(); +LL ~ match (mutex1.lock().unwrap().s.len(), true, value) { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:252:15 + | +LL | match mutex3.lock().unwrap().s.as_str() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:263:22 + | +LL | match (true, mutex3.lock().unwrap().s.as_str()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:282:11 + | +LL | match mutex.lock().unwrap().s.len() > 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex.lock().unwrap().s.len() > 1; +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:289:11 + | +LL | match 1 < mutex.lock().unwrap().s.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = 1 < mutex.lock().unwrap().s.len(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:305:11 + | +LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:312:11 + | +LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:343:11 + | +LL | match get_mutex_guard().s.len() > 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = get_mutex_guard().s.len() > 1; +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:360:11 + | +LL | match match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1; +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:379:11 + | +LL | match if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1; +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:421:11 + | +LL | match s.lock().deref().deref() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match and create a copy + | +LL ~ let value = *s.lock().deref().deref(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:447:11 + | +LL | match s.lock().deref().deref() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:467:11 + | +LL | match mutex.lock().unwrap().i = i { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ mutex.lock().unwrap().i = i; +LL ~ match () { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:473:11 + | +LL | match i = mutex.lock().unwrap().i { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ i = mutex.lock().unwrap().i; +LL ~ match () { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:479:11 + | +LL | match mutex.lock().unwrap().i += 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ mutex.lock().unwrap().i += 1; +LL ~ match () { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:485:11 + | +LL | match i += mutex.lock().unwrap().i { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ i += mutex.lock().unwrap().i; +LL ~ match () { + | + +error: aborting due to 23 previous errors + diff --git a/src/tools/clippy/tests/ui/similar_names.rs b/src/tools/clippy/tests/ui/similar_names.rs index 76f6ce9ee6b..c21225d153b 100644 --- a/src/tools/clippy/tests/ui/similar_names.rs +++ b/src/tools/clippy/tests/ui/similar_names.rs @@ -3,7 +3,8 @@ unused, clippy::println_empty_string, clippy::empty_loop, - clippy::diverging_sub_expression + clippy::diverging_sub_expression, + clippy::let_unit_value )] struct Foo { diff --git a/src/tools/clippy/tests/ui/similar_names.stderr b/src/tools/clippy/tests/ui/similar_names.stderr index faf572b0c6b..6e772693897 100644 --- a/src/tools/clippy/tests/ui/similar_names.stderr +++ b/src/tools/clippy/tests/ui/similar_names.stderr @@ -1,84 +1,84 @@ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:20:9 + --> $DIR/similar_names.rs:21:9 | LL | let bpple: i32; | ^^^^^ | = note: `-D clippy::similar-names` implied by `-D warnings` note: existing binding defined here - --> $DIR/similar_names.rs:18:9 + --> $DIR/similar_names.rs:19:9 | LL | let apple: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:22:9 + --> $DIR/similar_names.rs:23:9 | LL | let cpple: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:18:9 + --> $DIR/similar_names.rs:19:9 | LL | let apple: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:46:9 + --> $DIR/similar_names.rs:47:9 | LL | let bluby: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:45:9 + --> $DIR/similar_names.rs:46:9 | LL | let blubx: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:50:9 + --> $DIR/similar_names.rs:51:9 | LL | let coke: i32; | ^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:48:9 + --> $DIR/similar_names.rs:49:9 | LL | let cake: i32; | ^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:68:9 + --> $DIR/similar_names.rs:69:9 | LL | let xyzeabc: i32; | ^^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:66:9 + --> $DIR/similar_names.rs:67:9 | LL | let xyz1abc: i32; | ^^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:72:9 + --> $DIR/similar_names.rs:73:9 | LL | let parsee: i32; | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:70:9 + --> $DIR/similar_names.rs:71:9 | LL | let parser: i32; | ^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:93:16 + --> $DIR/similar_names.rs:94:16 | LL | bpple: sprang, | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:92:16 + --> $DIR/similar_names.rs:93:16 | LL | apple: spring, | ^^^^^^ diff --git a/src/tools/clippy/tests/ui/single_char_lifetime_names.rs b/src/tools/clippy/tests/ui/single_char_lifetime_names.rs index 261d8bc7260..69c5b236f7c 100644 --- a/src/tools/clippy/tests/ui/single_char_lifetime_names.rs +++ b/src/tools/clippy/tests/ui/single_char_lifetime_names.rs @@ -1,4 +1,5 @@ #![warn(clippy::single_char_lifetime_names)] +#![allow(clippy::let_unit_value)] // Lifetimes should only be linted when they're introduced struct DiagnosticCtx<'a, 'b> diff --git a/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr b/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr index 013b64f46a8..1438b3999db 100644 --- a/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr +++ b/src/tools/clippy/tests/ui/single_char_lifetime_names.stderr @@ -1,5 +1,5 @@ error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:4:22 + --> $DIR/single_char_lifetime_names.rs:5:22 | LL | struct DiagnosticCtx<'a, 'b> | ^^ @@ -8,7 +8,7 @@ LL | struct DiagnosticCtx<'a, 'b> = help: use a more informative name error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:4:26 + --> $DIR/single_char_lifetime_names.rs:5:26 | LL | struct DiagnosticCtx<'a, 'b> | ^^ @@ -16,7 +16,7 @@ LL | struct DiagnosticCtx<'a, 'b> = help: use a more informative name error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:13:6 + --> $DIR/single_char_lifetime_names.rs:14:6 | LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { | ^^ @@ -24,7 +24,7 @@ LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { = help: use a more informative name error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:13:10 + --> $DIR/single_char_lifetime_names.rs:14:10 | LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { | ^^ @@ -32,7 +32,7 @@ LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { = help: use a more informative name error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:33:15 + --> $DIR/single_char_lifetime_names.rs:34:15 | LL | fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) { | ^^ diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs index b624a41a29b..82387f3d80b 100644 --- a/src/tools/clippy/tests/ui/single_match_else.rs +++ b/src/tools/clippy/tests/ui/single_match_else.rs @@ -1,7 +1,12 @@ +// aux-build: proc_macro_with_span.rs + #![warn(clippy::single_match_else)] #![allow(clippy::needless_return)] #![allow(clippy::no_effect)] +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + enum ExprNode { ExprAddrOf, Butterflies, @@ -11,13 +16,22 @@ enum ExprNode { static NODE: ExprNode = ExprNode::Unicorns; fn unwrap_addr() -> Option<&'static ExprNode> { - match ExprNode::Butterflies { + let _ = match ExprNode::Butterflies { ExprNode::ExprAddrOf => Some(&NODE), _ => { let x = 5; None }, - } + }; + + // Don't lint + with_span!(span match ExprNode::Butterflies { + ExprNode::ExprAddrOf => Some(&NODE), + _ => { + let x = 5; + None + }, + }) } macro_rules! unwrap_addr { diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr index 21ea704b62a..7756c6f204e 100644 --- a/src/tools/clippy/tests/ui/single_match_else.stderr +++ b/src/tools/clippy/tests/ui/single_match_else.stderr @@ -1,22 +1,23 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:14:5 + --> $DIR/single_match_else.rs:19:13 | -LL | / match ExprNode::Butterflies { +LL | let _ = match ExprNode::Butterflies { + | _____________^ LL | | ExprNode::ExprAddrOf => Some(&NODE), LL | | _ => { LL | | let x = 5; LL | | None LL | | }, -LL | | } +LL | | }; | |_____^ | = note: `-D clippy::single-match-else` implied by `-D warnings` help: try this | -LL ~ if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { +LL ~ let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { LL + let x = 5; LL + None -LL + } +LL ~ }; | error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/stable_sort_primitive.stderr b/src/tools/clippy/tests/ui/stable_sort_primitive.stderr index b8d22ed2504..c35e0c22ae8 100644 --- a/src/tools/clippy/tests/ui/stable_sort_primitive.stderr +++ b/src/tools/clippy/tests/ui/stable_sort_primitive.stderr @@ -5,7 +5,7 @@ LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `bool` --> $DIR/stable_sort_primitive.rs:9:5 @@ -13,7 +13,7 @@ error: used `sort` on primitive type `bool` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `char` --> $DIR/stable_sort_primitive.rs:11:5 @@ -21,7 +21,7 @@ error: used `sort` on primitive type `char` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `str` --> $DIR/stable_sort_primitive.rs:13:5 @@ -29,7 +29,7 @@ error: used `sort` on primitive type `str` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `tuple` --> $DIR/stable_sort_primitive.rs:15:5 @@ -37,7 +37,7 @@ error: used `sort` on primitive type `tuple` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `array` --> $DIR/stable_sort_primitive.rs:17:5 @@ -45,7 +45,7 @@ error: used `sort` on primitive type `array` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `i32` --> $DIR/stable_sort_primitive.rs:19:5 @@ -53,7 +53,7 @@ error: used `sort` on primitive type `i32` LL | arr.sort(); | ^^^^^^^^^^ help: try: `arr.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs index fcd827a91c7..21753e5dc6a 100644 --- a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs +++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs @@ -1,7 +1,7 @@ // aux-build:proc_macro_suspicious_else_formatting.rs #![warn(clippy::suspicious_else_formatting)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::let_unit_value)] extern crate proc_macro_suspicious_else_formatting; use proc_macro_suspicious_else_formatting::DeriveBadSpan; diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.fixed b/src/tools/clippy/tests/ui/to_digit_is_some.fixed index 19184df0bec..3c5e9642714 100644 --- a/src/tools/clippy/tests/ui/to_digit_is_some.fixed +++ b/src/tools/clippy/tests/ui/to_digit_is_some.fixed @@ -6,6 +6,6 @@ fn main() { let c = 'x'; let d = &c; - let _ = d.is_digit(10); - let _ = char::is_digit(c, 10); + let _ = d.is_digit(8); + let _ = char::is_digit(c, 8); } diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.rs b/src/tools/clippy/tests/ui/to_digit_is_some.rs index 45a6728ebf5..4f247c06cee 100644 --- a/src/tools/clippy/tests/ui/to_digit_is_some.rs +++ b/src/tools/clippy/tests/ui/to_digit_is_some.rs @@ -6,6 +6,6 @@ fn main() { let c = 'x'; let d = &c; - let _ = d.to_digit(10).is_some(); - let _ = char::to_digit(c, 10).is_some(); + let _ = d.to_digit(8).is_some(); + let _ = char::to_digit(c, 8).is_some(); } diff --git a/src/tools/clippy/tests/ui/to_digit_is_some.stderr b/src/tools/clippy/tests/ui/to_digit_is_some.stderr index 177d3ccd3e2..10a1b393a39 100644 --- a/src/tools/clippy/tests/ui/to_digit_is_some.stderr +++ b/src/tools/clippy/tests/ui/to_digit_is_some.stderr @@ -1,16 +1,16 @@ error: use of `.to_digit(..).is_some()` --> $DIR/to_digit_is_some.rs:9:13 | -LL | let _ = d.to_digit(10).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(10)` +LL | let _ = d.to_digit(8).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(8)` | = note: `-D clippy::to-digit-is-some` implied by `-D warnings` error: use of `.to_digit(..).is_some()` --> $DIR/to_digit_is_some.rs:10:13 | -LL | let _ = char::to_digit(c, 10).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 10)` +LL | let _ = char::to_digit(c, 8).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 8)` error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs index f5ca91143af..a21d4c5d637 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs @@ -95,4 +95,7 @@ trait FooIter: Iterator<Item = Foo> { } } +// This should not lint +fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {} + fn main() {} diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr index 6f8c8e47dfb..d0a4cfb8837 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr @@ -67,5 +67,13 @@ LL | Self: Iterator<Item = Foo>, | = help: consider removing this trait bound -error: aborting due to 8 previous errors +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:99:23 + | +LL | fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {} + | ^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/transmute_collection.rs b/src/tools/clippy/tests/ui/transmute_collection.rs index cd5a7127791..5a431bee04a 100644 --- a/src/tools/clippy/tests/ui/transmute_collection.rs +++ b/src/tools/clippy/tests/ui/transmute_collection.rs @@ -1,7 +1,7 @@ #![warn(clippy::unsound_collection_transmute)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; -use std::mem::transmute; +use std::mem::{transmute, MaybeUninit}; fn main() { unsafe { @@ -43,5 +43,8 @@ fn main() { // wrong layout let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new()); let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new()); + + let _ = transmute::<_, Vec<u8>>(Vec::<MaybeUninit<u8>>::new()); + let _ = transmute::<_, Vec<*mut u32>>(Vec::<Box<u32>>::new()); } } diff --git a/src/tools/clippy/tests/ui/trim_split_whitespace.fixed b/src/tools/clippy/tests/ui/trim_split_whitespace.fixed new file mode 100644 index 00000000000..e4d352f7367 --- /dev/null +++ b/src/tools/clippy/tests/ui/trim_split_whitespace.fixed @@ -0,0 +1,91 @@ +// run-rustfix +#![warn(clippy::trim_split_whitespace)] +#![allow(clippy::let_unit_value)] + +struct Custom; +impl Custom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStr(&'static str); +impl std::ops::Deref for DerefStr { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +struct DerefStrAndCustom(&'static str); +impl std::ops::Deref for DerefStrAndCustom { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomSplit(&'static str); +impl std::ops::Deref for DerefStrAndCustomSplit { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomSplit { + #[allow(dead_code)] + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomTrim(&'static str); +impl std::ops::Deref for DerefStrAndCustomTrim { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomTrim { + fn trim(self) -> Self { + self + } +} + +fn main() { + // &str + let _ = " A B C ".split_whitespace(); // should trigger lint + let _ = " A B C ".split_whitespace(); // should trigger lint + let _ = " A B C ".split_whitespace(); // should trigger lint + + // String + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + + // Custom + let _ = Custom.trim().split_whitespace(); // should not trigger lint + + // Deref<Target=str> + let s = DerefStr(" A B C "); + let _ = s.split_whitespace(); // should trigger lint + + // Deref<Target=str> + custom impl + let s = DerefStrAndCustom(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint + + // Deref<Target=str> + only custom split_ws() impl + let s = DerefStrAndCustomSplit(" A B C "); + let _ = s.split_whitespace(); // should trigger lint + // Expl: trim() is called on str (deref) and returns &str. + // Thus split_ws() is called on str as well and the custom impl on S is unused + + // Deref<Target=str> + only custom trim() impl + let s = DerefStrAndCustomTrim(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint +} diff --git a/src/tools/clippy/tests/ui/trim_split_whitespace.rs b/src/tools/clippy/tests/ui/trim_split_whitespace.rs new file mode 100644 index 00000000000..f98451a9837 --- /dev/null +++ b/src/tools/clippy/tests/ui/trim_split_whitespace.rs @@ -0,0 +1,91 @@ +// run-rustfix +#![warn(clippy::trim_split_whitespace)] +#![allow(clippy::let_unit_value)] + +struct Custom; +impl Custom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStr(&'static str); +impl std::ops::Deref for DerefStr { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +struct DerefStrAndCustom(&'static str); +impl std::ops::Deref for DerefStrAndCustom { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomSplit(&'static str); +impl std::ops::Deref for DerefStrAndCustomSplit { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomSplit { + #[allow(dead_code)] + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomTrim(&'static str); +impl std::ops::Deref for DerefStrAndCustomTrim { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomTrim { + fn trim(self) -> Self { + self + } +} + +fn main() { + // &str + let _ = " A B C ".trim().split_whitespace(); // should trigger lint + let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint + let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint + + // String + let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint + + // Custom + let _ = Custom.trim().split_whitespace(); // should not trigger lint + + // Deref<Target=str> + let s = DerefStr(" A B C "); + let _ = s.trim().split_whitespace(); // should trigger lint + + // Deref<Target=str> + custom impl + let s = DerefStrAndCustom(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint + + // Deref<Target=str> + only custom split_ws() impl + let s = DerefStrAndCustomSplit(" A B C "); + let _ = s.trim().split_whitespace(); // should trigger lint + // Expl: trim() is called on str (deref) and returns &str. + // Thus split_ws() is called on str as well and the custom impl on S is unused + + // Deref<Target=str> + only custom trim() impl + let s = DerefStrAndCustomTrim(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint +} diff --git a/src/tools/clippy/tests/ui/trim_split_whitespace.stderr b/src/tools/clippy/tests/ui/trim_split_whitespace.stderr new file mode 100644 index 00000000000..5ae7849e27d --- /dev/null +++ b/src/tools/clippy/tests/ui/trim_split_whitespace.stderr @@ -0,0 +1,52 @@ +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:62:23 + | +LL | let _ = " A B C ".trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + | + = note: `-D clippy::trim-split-whitespace` implied by `-D warnings` + +error: found call to `str::trim_start` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:63:23 + | +LL | let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^^^ help: remove `trim_start()` + +error: found call to `str::trim_end` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:64:23 + | +LL | let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^ help: remove `trim_end()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:67:37 + | +LL | let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: found call to `str::trim_start` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:68:37 + | +LL | let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^^^ help: remove `trim_start()` + +error: found call to `str::trim_end` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:69:37 + | +LL | let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^ help: remove `trim_end()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:76:15 + | +LL | let _ = s.trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:84:15 + | +LL | let _ = s.trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs index fc740ee11d6..d11432f9046 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs @@ -79,4 +79,7 @@ where u: U, } +// This should not lint +fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {} + fn main() {} diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr index 148c19c7d07..abc25e59496 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr @@ -19,5 +19,13 @@ LL | Self: Copy + Default + Ord, | = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` -error: aborting due to 2 previous errors +error: this type has already been used as a bound predicate + --> $DIR/type_repetition_in_bounds.rs:83:43 + | +LL | fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {} + | ^^^^^^^^^^ + | + = help: consider combining the bounds: `impl AsRef<str>: AsRef<str> + AsRef<str>` + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs index afa337c45f4..7be15b0b2dd 100644 --- a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs @@ -1,6 +1,7 @@ // aux-build:proc_macro_unsafe.rs #![warn(clippy::undocumented_unsafe_blocks)] +#![allow(clippy::let_unit_value)] extern crate proc_macro_unsafe; diff --git a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr index 856a07fd316..87d445bd7b8 100644 --- a/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr +++ b/src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:256:19 + --> $DIR/undocumented_unsafe_blocks.rs:257:19 | LL | /* Safety: */ unsafe {} | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | /* Safety: */ unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:260:5 + --> $DIR/undocumented_unsafe_blocks.rs:261:5 | LL | unsafe {} | ^^^^^^^^^ @@ -16,7 +16,7 @@ LL | unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:14 + --> $DIR/undocumented_unsafe_blocks.rs:265:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:29 + --> $DIR/undocumented_unsafe_blocks.rs:265:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:48 + --> $DIR/undocumented_unsafe_blocks.rs:265:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:268:18 + --> $DIR/undocumented_unsafe_blocks.rs:269:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:268:37 + --> $DIR/undocumented_unsafe_blocks.rs:269:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:272:14 + --> $DIR/undocumented_unsafe_blocks.rs:273:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = *unsafe { &42 }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:277:19 + --> $DIR/undocumented_unsafe_blocks.rs:278:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = match unsafe {} { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:283:14 + --> $DIR/undocumented_unsafe_blocks.rs:284:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:287:14 + --> $DIR/undocumented_unsafe_blocks.rs:288:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = [unsafe {}; 5]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:291:13 + --> $DIR/undocumented_unsafe_blocks.rs:292:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:301:8 + --> $DIR/undocumented_unsafe_blocks.rs:302:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -104,7 +104,7 @@ LL | t!(unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:307:13 + --> $DIR/undocumented_unsafe_blocks.rs:308:13 | LL | unsafe {} | ^^^^^^^^^ @@ -116,7 +116,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:315:5 + --> $DIR/undocumented_unsafe_blocks.rs:316:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -124,7 +124,7 @@ LL | unsafe {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:319:5 + --> $DIR/undocumented_unsafe_blocks.rs:320:5 | LL | unsafe { | ^^^^^^^^ @@ -132,7 +132,7 @@ LL | unsafe { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:329:5 + --> $DIR/undocumented_unsafe_blocks.rs:330:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -140,7 +140,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:333:20 + --> $DIR/undocumented_unsafe_blocks.rs:334:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/uninit.rs b/src/tools/clippy/tests/ui/uninit.rs index 1ed3883c1f0..dac5ce272c0 100644 --- a/src/tools/clippy/tests/ui/uninit.rs +++ b/src/tools/clippy/tests/ui/uninit.rs @@ -1,4 +1,5 @@ #![feature(stmt_expr_attributes)] +#![allow(clippy::let_unit_value)] use std::mem::{self, MaybeUninit}; diff --git a/src/tools/clippy/tests/ui/uninit.stderr b/src/tools/clippy/tests/ui/uninit.stderr index 85b64a8419a..15ef2349489 100644 --- a/src/tools/clippy/tests/ui/uninit.stderr +++ b/src/tools/clippy/tests/ui/uninit.stderr @@ -1,5 +1,5 @@ error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:6:29 + --> $DIR/uninit.rs:7:29 | LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; = note: `#[deny(clippy::uninit_assumed_init)]` on by default error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:9:31 + --> $DIR/uninit.rs:10:31 | LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:24:29 + --> $DIR/uninit.rs:25:29 | LL | let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unit_arg.rs b/src/tools/clippy/tests/ui/unit_arg.rs index 535683729f6..38be87bddf1 100644 --- a/src/tools/clippy/tests/ui/unit_arg.rs +++ b/src/tools/clippy/tests/ui/unit_arg.rs @@ -7,7 +7,8 @@ clippy::unnecessary_wraps, clippy::or_fun_call, clippy::needless_question_mark, - clippy::self_named_constructors + clippy::self_named_constructors, + clippy::let_unit_value )] use std::fmt::Debug; diff --git a/src/tools/clippy/tests/ui/unit_arg.stderr b/src/tools/clippy/tests/ui/unit_arg.stderr index 5cfb367a7be..394dee29dc9 100644 --- a/src/tools/clippy/tests/ui/unit_arg.stderr +++ b/src/tools/clippy/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:56:5 + --> $DIR/unit_arg.rs:57:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:59:5 + --> $DIR/unit_arg.rs:60:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:60:5 + --> $DIR/unit_arg.rs:61:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:65:5 + --> $DIR/unit_arg.rs:66:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL ~ b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:68:5 + --> $DIR/unit_arg.rs:69:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:69:5 + --> $DIR/unit_arg.rs:70:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:73:5 + --> $DIR/unit_arg.rs:74:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL + foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:84:13 + --> $DIR/unit_arg.rs:85:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL ~ }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:87:5 + --> $DIR/unit_arg.rs:88:5 | LL | foo(foo(())); | ^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:124:5 + --> $DIR/unit_arg.rs:125:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unit_hash.rs b/src/tools/clippy/tests/ui/unit_hash.rs index 989916c239b..43eb54eff47 100644 --- a/src/tools/clippy/tests/ui/unit_hash.rs +++ b/src/tools/clippy/tests/ui/unit_hash.rs @@ -1,4 +1,5 @@ #![warn(clippy::unit_hash)] +#![allow(clippy::let_unit_value)] use std::collections::hash_map::DefaultHasher; use std::hash::Hash; diff --git a/src/tools/clippy/tests/ui/unit_hash.stderr b/src/tools/clippy/tests/ui/unit_hash.stderr index da276296e02..050fa55a12b 100644 --- a/src/tools/clippy/tests/ui/unit_hash.stderr +++ b/src/tools/clippy/tests/ui/unit_hash.stderr @@ -1,5 +1,5 @@ error: this call to `hash` on the unit type will do nothing - --> $DIR/unit_hash.rs:18:23 + --> $DIR/unit_hash.rs:19:23 | LL | Foo::Empty => ().hash(&mut state), | ^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)` @@ -8,7 +8,7 @@ LL | Foo::Empty => ().hash(&mut state), = note: the implementation of `Hash` for `()` is a no-op error: this call to `hash` on the unit type will do nothing - --> $DIR/unit_hash.rs:23:5 + --> $DIR/unit_hash.rs:24:5 | LL | res.hash(&mut state); | ^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)` @@ -16,7 +16,7 @@ LL | res.hash(&mut state); = note: the implementation of `Hash` for `()` is a no-op error: this call to `hash` on the unit type will do nothing - --> $DIR/unit_hash.rs:26:5 + --> $DIR/unit_hash.rs:27:5 | LL | do_nothing().hash(&mut state); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)` diff --git a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed new file mode 100644 index 00000000000..f95f91329a2 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +#![warn(clippy::unnecessary_owned_empty_strings)] + +fn ref_str_argument(_value: &str) {} + +#[allow(clippy::ptr_arg)] +fn ref_string_argument(_value: &String) {} + +fn main() { + // should be linted + ref_str_argument(""); + + // should be linted + ref_str_argument(""); + + // should not be linted + ref_str_argument(""); + + // should not be linted + ref_string_argument(&String::new()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs new file mode 100644 index 00000000000..0cbdc151ed9 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs @@ -0,0 +1,22 @@ +// run-rustfix + +#![warn(clippy::unnecessary_owned_empty_strings)] + +fn ref_str_argument(_value: &str) {} + +#[allow(clippy::ptr_arg)] +fn ref_string_argument(_value: &String) {} + +fn main() { + // should be linted + ref_str_argument(&String::new()); + + // should be linted + ref_str_argument(&String::from("")); + + // should not be linted + ref_str_argument(""); + + // should not be linted + ref_string_argument(&String::new()); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr new file mode 100644 index 00000000000..46bc4597b33 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr @@ -0,0 +1,16 @@ +error: usage of `&String::new()` for a function expecting a `&str` argument + --> $DIR/unnecessary_owned_empty_strings.rs:12:22 + | +LL | ref_str_argument(&String::new()); + | ^^^^^^^^^^^^^^ help: try: `""` + | + = note: `-D clippy::unnecessary-owned-empty-strings` implied by `-D warnings` + +error: usage of `&String::from("")` for a function expecting a `&str` argument + --> $DIR/unnecessary_owned_empty_strings.rs:15:22 + | +LL | ref_str_argument(&String::from("")); + | ^^^^^^^^^^^^^^^^^ help: try: `""` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed index 38ba41ac54e..7455e22d49b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed @@ -2,6 +2,7 @@ #![allow(clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] +#![feature(custom_inner_attributes)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; @@ -213,6 +214,17 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E fn require_string(_: &String) {} +fn _msrv_1_35() { + #![clippy::msrv = "1.35"] + // `copied` was stabilized in 1.36, so clippy should use `cloned`. + let _ = &["x"][..].iter().cloned(); +} + +fn _msrv_1_36() { + #![clippy::msrv = "1.36"] + let _ = &["x"][..].iter().copied(); +} + // https://github.com/rust-lang/rust-clippy/issues/8507 mod issue_8507 { #![allow(dead_code)] diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index 15fb7ee83e3..bbcd00ad220 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -2,6 +2,7 @@ #![allow(clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] +#![feature(custom_inner_attributes)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; @@ -213,6 +214,17 @@ fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::E fn require_string(_: &String) {} +fn _msrv_1_35() { + #![clippy::msrv = "1.35"] + // `copied` was stabilized in 1.36, so clippy should use `cloned`. + let _ = &["x"][..].to_vec().into_iter(); +} + +fn _msrv_1_36() { + #![clippy::msrv = "1.36"] + let _ = &["x"][..].to_vec().into_iter(); +} + // https://github.com/rust-lang/rust-clippy/issues/8507 mod issue_8507 { #![allow(dead_code)] diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr index c53ce32be77..f9713559e4f 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr @@ -1,54 +1,54 @@ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:150:64 + --> $DIR/unnecessary_to_owned.rs:151:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | = note: `-D clippy::redundant-clone` implied by `-D warnings` note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:150:20 + --> $DIR/unnecessary_to_owned.rs:151:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:151:40 + --> $DIR/unnecessary_to_owned.rs:152:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:151:21 + --> $DIR/unnecessary_to_owned.rs:152:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:152:48 + --> $DIR/unnecessary_to_owned.rs:153:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:152:19 + --> $DIR/unnecessary_to_owned.rs:153:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:153:35 + --> $DIR/unnecessary_to_owned.rs:154:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:153:18 + --> $DIR/unnecessary_to_owned.rs:154:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:59:36 + --> $DIR/unnecessary_to_owned.rs:60:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -56,427 +56,427 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:60:19 + --> $DIR/unnecessary_to_owned.rs:61:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> $DIR/unnecessary_to_owned.rs:62:20 + --> $DIR/unnecessary_to_owned.rs:63:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:63:38 + --> $DIR/unnecessary_to_owned.rs:64:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:64:20 + --> $DIR/unnecessary_to_owned.rs:65:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> $DIR/unnecessary_to_owned.rs:66:18 + --> $DIR/unnecessary_to_owned.rs:67:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:67:34 + --> $DIR/unnecessary_to_owned.rs:68:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:68:18 + --> $DIR/unnecessary_to_owned.rs:69:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:70:17 + --> $DIR/unnecessary_to_owned.rs:71:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:71:30 + --> $DIR/unnecessary_to_owned.rs:72:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:72:17 + --> $DIR/unnecessary_to_owned.rs:73:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:73:17 + --> $DIR/unnecessary_to_owned.rs:74:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:75:19 + --> $DIR/unnecessary_to_owned.rs:76:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:76:36 + --> $DIR/unnecessary_to_owned.rs:77:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:77:19 + --> $DIR/unnecessary_to_owned.rs:78:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:78:19 + --> $DIR/unnecessary_to_owned.rs:79:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:79:19 + --> $DIR/unnecessary_to_owned.rs:80:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:80:19 + --> $DIR/unnecessary_to_owned.rs:81:19 | LL | require_slice(&x_ref.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:82:42 + --> $DIR/unnecessary_to_owned.rs:83:42 | LL | require_x(&Cow::<X>::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:83:15 + --> $DIR/unnecessary_to_owned.rs:84:15 | LL | require_x(&x_ref.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:85:25 + --> $DIR/unnecessary_to_owned.rs:86:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:86:26 + --> $DIR/unnecessary_to_owned.rs:87:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:87:24 + --> $DIR/unnecessary_to_owned.rs:88:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:88:23 + --> $DIR/unnecessary_to_owned.rs:89:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:89:25 + --> $DIR/unnecessary_to_owned.rs:90:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:91:30 + --> $DIR/unnecessary_to_owned.rs:92:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:92:31 + --> $DIR/unnecessary_to_owned.rs:93:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:93:29 + --> $DIR/unnecessary_to_owned.rs:94:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:94:28 + --> $DIR/unnecessary_to_owned.rs:95:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:95:30 + --> $DIR/unnecessary_to_owned.rs:96:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:97:29 + --> $DIR/unnecessary_to_owned.rs:98:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:97:43 + --> $DIR/unnecessary_to_owned.rs:98:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:29 + --> $DIR/unnecessary_to_owned.rs:99:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:47 + --> $DIR/unnecessary_to_owned.rs:99:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:100:26 + --> $DIR/unnecessary_to_owned.rs:101:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:101:27 + --> $DIR/unnecessary_to_owned.rs:102:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:102:25 + --> $DIR/unnecessary_to_owned.rs:103:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:103:24 + --> $DIR/unnecessary_to_owned.rs:104:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:104:24 + --> $DIR/unnecessary_to_owned.rs:105:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:105:26 + --> $DIR/unnecessary_to_owned.rs:106:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:106:26 + --> $DIR/unnecessary_to_owned.rs:107:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:107:26 + --> $DIR/unnecessary_to_owned.rs:108:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:109:31 + --> $DIR/unnecessary_to_owned.rs:110:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:110:32 + --> $DIR/unnecessary_to_owned.rs:111:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:111:30 + --> $DIR/unnecessary_to_owned.rs:112:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:112:29 + --> $DIR/unnecessary_to_owned.rs:113:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:113:29 + --> $DIR/unnecessary_to_owned.rs:114:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:114:31 + --> $DIR/unnecessary_to_owned.rs:115:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:115:31 + --> $DIR/unnecessary_to_owned.rs:116:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:116:31 + --> $DIR/unnecessary_to_owned.rs:117:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:118:30 + --> $DIR/unnecessary_to_owned.rs:119:30 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:118:44 + --> $DIR/unnecessary_to_owned.rs:119:44 | LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:30 + --> $DIR/unnecessary_to_owned.rs:120:30 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:119:44 + --> $DIR/unnecessary_to_owned.rs:120:44 | LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:30 + --> $DIR/unnecessary_to_owned.rs:121:30 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:120:44 + --> $DIR/unnecessary_to_owned.rs:121:44 | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:30 + --> $DIR/unnecessary_to_owned.rs:122:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:48 + --> $DIR/unnecessary_to_owned.rs:122:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:30 + --> $DIR/unnecessary_to_owned.rs:123:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:52 + --> $DIR/unnecessary_to_owned.rs:123:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:30 + --> $DIR/unnecessary_to_owned.rs:124:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:48 + --> $DIR/unnecessary_to_owned.rs:124:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:125:20 + --> $DIR/unnecessary_to_owned.rs:126:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:127:13 + --> $DIR/unnecessary_to_owned.rs:128:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:128:13 + --> $DIR/unnecessary_to_owned.rs:129:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:129:13 + --> $DIR/unnecessary_to_owned.rs:130:13 | LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:130:13 + --> $DIR/unnecessary_to_owned.rs:131:13 | LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:132:13 + --> $DIR/unnecessary_to_owned.rs:133:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:133:13 + --> $DIR/unnecessary_to_owned.rs:134:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:134:13 + --> $DIR/unnecessary_to_owned.rs:135:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:135:13 + --> $DIR/unnecessary_to_owned.rs:136:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:196:14 + --> $DIR/unnecessary_to_owned.rs:197:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -491,11 +491,23 @@ LL - let path = match get_file_path(&t) { LL + let path = match get_file_path(t) { | +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:220:14 + | +LL | let _ = &["x"][..].to_vec().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` + +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:225:14 + | +LL | let _ = &["x"][..].to_vec().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` + error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:260:24 + --> $DIR/unnecessary_to_owned.rs:272:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` -error: aborting due to 77 previous errors +error: aborting due to 79 previous errors diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed index 46463a29e9b..c223b5bc711 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed @@ -6,10 +6,13 @@ #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] fn main() { + // Should be ignored by this lint, as nesting requires more characters. + if let &0 | &2 = &0 {} + if let box (0 | 2) = Box::new(0) {} if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} - const C0: &u8 = &1; - if let &(0 | 2) | C0 = &0 {} + const C0: Option<u8> = Some(1); + if let Some(1 | 2) | C0 = None {} if let &mut (0 | 2) = &mut 0 {} if let x @ (0 | 2) = 0 {} if let (0, 1 | 2 | 3) = (0, 0) {} diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.rs b/src/tools/clippy/tests/ui/unnested_or_patterns.rs index 8ce0738bfc2..04cd11036e4 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.rs +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.rs @@ -6,10 +6,13 @@ #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] fn main() { + // Should be ignored by this lint, as nesting requires more characters. + if let &0 | &2 = &0 {} + if let box 0 | box 2 = Box::new(0) {} if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} - const C0: &u8 = &1; - if let &0 | C0 | &2 = &0 {} + const C0: Option<u8> = Some(1); + if let Some(1) | C0 | Some(2) = None {} if let &mut 0 | &mut 2 = &mut 0 {} if let x @ 0 | x @ 2 = 0 {} if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr index de424c3fdb8..453c66cbba8 100644 --- a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr +++ b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr @@ -1,5 +1,5 @@ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:9:12 + --> $DIR/unnested_or_patterns.rs:12:12 | LL | if let box 0 | box 2 = Box::new(0) {} | ^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | if let box (0 | 2) = Box::new(0) {} | ~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:10:12 + --> $DIR/unnested_or_patterns.rs:13:12 | LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,18 +22,18 @@ LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} | ~~~~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:12:12 + --> $DIR/unnested_or_patterns.rs:15:12 | -LL | if let &0 | C0 | &2 = &0 {} - | ^^^^^^^^^^^^ +LL | if let Some(1) | C0 | Some(2) = None {} + | ^^^^^^^^^^^^^^^^^^^^^^ | help: nest the patterns | -LL | if let &(0 | 2) | C0 = &0 {} - | ~~~~~~~~~~~~~ +LL | if let Some(1 | 2) | C0 = None {} + | ~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:13:12 + --> $DIR/unnested_or_patterns.rs:16:12 | LL | if let &mut 0 | &mut 2 = &mut 0 {} | ^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | if let &mut (0 | 2) = &mut 0 {} | ~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:14:12 + --> $DIR/unnested_or_patterns.rs:17:12 | LL | if let x @ 0 | x @ 2 = 0 {} | ^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | if let x @ (0 | 2) = 0 {} | ~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:15:12 + --> $DIR/unnested_or_patterns.rs:18:12 | LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | if let (0, 1 | 2 | 3) = (0, 0) {} | ~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:16:12 + --> $DIR/unnested_or_patterns.rs:19:12 | LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | if let (1 | 2 | 3, 0) = (0, 0) {} | ~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:17:12 + --> $DIR/unnested_or_patterns.rs:20:12 | LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | if let (x, ..) | (x, 1 | 2) = (0, 1) {} | ~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:18:12 + --> $DIR/unnested_or_patterns.rs:21:12 | LL | if let [0] | [1] = [0] {} | ^^^^^^^^^ @@ -99,7 +99,7 @@ LL | if let [0 | 1] = [0] {} | ~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:19:12 + --> $DIR/unnested_or_patterns.rs:22:12 | LL | if let [x, 0] | [x, 1] = [0, 1] {} | ^^^^^^^^^^^^^^^ @@ -110,7 +110,7 @@ LL | if let [x, 0 | 1] = [0, 1] {} | ~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:20:12 + --> $DIR/unnested_or_patterns.rs:23:12 | LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | if let [x, 0 | 1 | 2] = [0, 1] {} | ~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:21:12 + --> $DIR/unnested_or_patterns.rs:24:12 | LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL | if let [x, ..] | [x, 1 | 2] = [0, 1] {} | ~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:23:12 + --> $DIR/unnested_or_patterns.rs:26:12 | LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^ @@ -143,7 +143,7 @@ LL | if let TS(0 | 1, x) = TS(0, 0) {} | ~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:24:12 + --> $DIR/unnested_or_patterns.rs:27:12 | LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | if let TS(1 | 2 | 3, 0) = TS(0, 0) {} | ~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:25:12 + --> $DIR/unnested_or_patterns.rs:28:12 | LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} | ~~~~~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:30:12 + --> $DIR/unnested_or_patterns.rs:33:12 | LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed index ce58a80347b..c23231a99e9 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.fixed +++ b/src/tools/clippy/tests/ui/useless_attribute.fixed @@ -57,6 +57,12 @@ pub use std::io::prelude::*; #[allow(clippy::enum_glob_use)] pub use std::cmp::Ordering::*; +// don't lint on clippy::redundant_pub_crate +mod c { + #[allow(clippy::redundant_pub_crate)] + pub(crate) struct S; +} + fn test_indented_attr() { #![allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs index c82bb9ba07f..7a7b198ea60 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.rs +++ b/src/tools/clippy/tests/ui/useless_attribute.rs @@ -57,6 +57,12 @@ pub use std::io::prelude::*; #[allow(clippy::enum_glob_use)] pub use std::cmp::Ordering::*; +// don't lint on clippy::redundant_pub_crate +mod c { + #[allow(clippy::redundant_pub_crate)] + pub(crate) struct S; +} + fn test_indented_attr() { #[allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/src/tools/clippy/tests/ui/useless_attribute.stderr b/src/tools/clippy/tests/ui/useless_attribute.stderr index d0194e4bbbe..255d2876355 100644 --- a/src/tools/clippy/tests/ui/useless_attribute.stderr +++ b/src/tools/clippy/tests/ui/useless_attribute.stderr @@ -13,7 +13,7 @@ LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` error: useless lint attribute - --> $DIR/useless_attribute.rs:61:5 + --> $DIR/useless_attribute.rs:67:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed index 8402c33a4cd..b6f47ae906b 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed @@ -8,8 +8,7 @@ // FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] -#![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/src/tools/clippy/tests/ui/wildcard_imports.rs b/src/tools/clippy/tests/ui/wildcard_imports.rs index faaeaade9b0..eb404b7a3de 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.rs +++ b/src/tools/clippy/tests/ui/wildcard_imports.rs @@ -8,8 +8,7 @@ // FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] -#![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr index 7534a65ec9b..626c1754fc8 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> $DIR/wildcard_imports.rs:17:5 + --> $DIR/wildcard_imports.rs:16:5 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` @@ -7,85 +7,85 @@ LL | use crate::fn_mod::*; = note: `-D clippy::wildcard-imports` implied by `-D warnings` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:18:5 + --> $DIR/wildcard_imports.rs:17:5 | LL | use crate::mod_mod::*; | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:19:5 + --> $DIR/wildcard_imports.rs:18:5 | LL | use crate::multi_fn_mod::*; | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:21:5 + --> $DIR/wildcard_imports.rs:20:5 | LL | use crate::struct_mod::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:25:5 + --> $DIR/wildcard_imports.rs:24:5 | LL | use wildcard_imports_helper::inner::inner_for_self_import::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:26:5 + --> $DIR/wildcard_imports.rs:25:5 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:97:13 + --> $DIR/wildcard_imports.rs:96:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:103:75 + --> $DIR/wildcard_imports.rs:102:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:104:13 + --> $DIR/wildcard_imports.rs:103:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:115:20 + --> $DIR/wildcard_imports.rs:114:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:115:30 + --> $DIR/wildcard_imports.rs:114:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:122:13 + --> $DIR/wildcard_imports.rs:121:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:151:9 + --> $DIR/wildcard_imports.rs:150:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:160:9 + --> $DIR/wildcard_imports.rs:159:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:161:9 + --> $DIR/wildcard_imports.rs:160:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:172:13 + --> $DIR/wildcard_imports.rs:171:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:207:17 + --> $DIR/wildcard_imports.rs:206:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:215:13 + --> $DIR/wildcard_imports.rs:214:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:224:17 + --> $DIR/wildcard_imports.rs:223:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:233:13 + --> $DIR/wildcard_imports.rs:232:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:241:13 + --> $DIR/wildcard_imports.rs:240:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.rs b/src/tools/clippy/tests/ui/wrong_self_convention.rs index f8fee4b3ab2..e3cc90ee222 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.rs +++ b/src/tools/clippy/tests/ui/wrong_self_convention.rs @@ -193,11 +193,6 @@ pub mod issue8142 { struct S; impl S { - // Should lint: is_ methods should only take &self, or no self at all. - fn is_still_buggy(&mut self) -> bool { - false - } - // Should not lint: "no self at all" is allowed. fn is_forty_two(x: u32) -> bool { x == 42 diff --git a/src/tools/clippy/tests/ui/wrong_self_convention.stderr b/src/tools/clippy/tests/ui/wrong_self_convention.stderr index 5493a99572e..2e7ee51d7e1 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention.stderr +++ b/src/tools/clippy/tests/ui/wrong_self_convention.stderr @@ -31,7 +31,7 @@ LL | fn into_i32(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:38:15 | LL | fn is_i32(self) {} @@ -71,7 +71,7 @@ LL | pub fn into_i64(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn is_i64(self) {} @@ -111,7 +111,7 @@ LL | fn into_i32_ref(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:98:19 | LL | fn is_i32(self) {} @@ -143,7 +143,7 @@ LL | fn into_i32_ref(&self); | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:122:19 | LL | fn is_i32(self); @@ -191,13 +191,5 @@ LL | fn to_u64(self) -> u64 { | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` - --> $DIR/wrong_self_convention.rs:197:27 - | -LL | fn is_still_buggy(&mut self) -> bool { - | ^^^^^^^^^ - | - = help: consider choosing a less ambiguous name - -error: aborting due to 25 previous errors +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/wrong_self_convention2.rs b/src/tools/clippy/tests/ui/wrong_self_convention2.rs index a8fe8331133..0dcf4743e8b 100644 --- a/src/tools/clippy/tests/ui/wrong_self_convention2.rs +++ b/src/tools/clippy/tests/ui/wrong_self_convention2.rs @@ -104,3 +104,13 @@ mod issue4546 { pub fn to_other_thingy(self: Pin<&Self>) {} } } + +mod issue_8480_8513 { + struct Cat(String); + + impl Cat { + fn is_animal(&mut self) -> bool { + todo!(); + } + } +} diff --git a/src/tools/miri b/src/tools/miri -Subproject a71a0083937671d79e16bfac4c7b8cab9c8ab9b +Subproject 3b8b6aa8b689971d3b8776cefe3d809501e1b8f diff --git a/src/tools/rust-analyzer b/src/tools/rust-analyzer -Subproject 24cf957627d5ede1b395f92ff871fd7a281d49a +Subproject 5dce1ff0212e467271c9e895478670c74d847ee |
