diff --git a/Cargo.lock b/Cargo.lock index 7c8f06a02396..89eb514917d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4064,6 +4064,7 @@ name = "rustc_lexer" version = "0.0.0" dependencies = [ "expect-test", + "memchr", "unicode-properties", "unicode-xid", ] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 08621c1c56a1..e75334b19c89 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -19,6 +19,7 @@ //! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators. use std::borrow::Cow; +use std::sync::Arc; use std::{cmp, fmt}; pub use GenericArgs::*; @@ -27,7 +28,6 @@ pub use rustc_ast_ir::{Movability, Mutability, Pinnedness}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::Lrc; use rustc_data_structures::tagged_ptr::Tag; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::AttrId; @@ -1611,7 +1611,7 @@ pub enum ExprKind { /// Added for optimization purposes to avoid the need to escape /// large binary blobs - should always behave like [`ExprKind::Lit`] /// with a `ByteStr` literal. - IncludedBytes(Lrc<[u8]>), + IncludedBytes(Arc<[u8]>), /// A `format_args!()` expression. FormatArgs(P), @@ -1904,9 +1904,9 @@ pub enum LitKind { Str(Symbol, StrStyle), /// A byte string (`b"foo"`). Not stored as a symbol because it might be /// non-utf8, and symbols only allow utf8 strings. - ByteStr(Lrc<[u8]>, StrStyle), + ByteStr(Arc<[u8]>, StrStyle), /// A C String (`c"foo"`). Guaranteed to only have `\0` at the end. - CStr(Lrc<[u8]>, StrStyle), + CStr(Arc<[u8]>, StrStyle), /// A byte char (`b'f'`). Byte(u8), /// A character literal (`'a'`). diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 70616fe87691..a9961cca5833 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -9,10 +9,10 @@ use std::ops::DerefMut; use std::panic; +use std::sync::Arc; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::Lrc; use rustc_span::source_map::Spanned; use rustc_span::{Ident, Span}; use smallvec::{Array, SmallVec, smallvec}; @@ -793,14 +793,14 @@ fn visit_tt(vis: &mut T, tt: &mut TokenTree) { // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. fn visit_tts(vis: &mut T, TokenStream(tts): &mut TokenStream) { if T::VISIT_TOKENS && !tts.is_empty() { - let tts = Lrc::make_mut(tts); + let tts = Arc::make_mut(tts); visit_vec(tts, |tree| visit_tt(vis, tree)); } } fn visit_attr_tts(vis: &mut T, AttrTokenStream(tts): &mut AttrTokenStream) { if T::VISIT_TOKENS && !tts.is_empty() { - let tts = Lrc::make_mut(tts); + let tts = Arc::make_mut(tts); visit_vec(tts, |tree| visit_attr_tt(vis, tree)); } } @@ -840,7 +840,7 @@ pub fn visit_token(vis: &mut T, t: &mut Token) { vis.visit_ident(ident); } token::Interpolated(nt) => { - let nt = Lrc::make_mut(nt); + let nt = Arc::make_mut(nt); visit_nonterminal(vis, nt); } _ => {} diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 100f664a89fd..36a7b7d87892 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::fmt; +use std::sync::Arc; pub use BinOpToken::*; pub use LitKind::*; @@ -8,7 +9,6 @@ pub use NtExprKind::*; pub use NtPatKind::*; pub use TokenKind::*; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::Lrc; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::Edition; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym}; @@ -451,7 +451,7 @@ pub enum TokenKind { /// The span in the surrounding `Token` is that of the metavariable in the /// macro's RHS. The span within the Nonterminal is that of the fragment /// passed to the macro at the call site. - Interpolated(Lrc), + Interpolated(Arc), /// A doc comment token. /// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc) @@ -469,7 +469,7 @@ impl Clone for TokenKind { // a copy. This is faster than the `derive(Clone)` version which has a // separate path for every variant. match self { - Interpolated(nt) => Interpolated(Lrc::clone(nt)), + Interpolated(nt) => Interpolated(Arc::clone(nt)), _ => unsafe { std::ptr::read(self) }, } } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index e7b393d869d2..50f10d083a04 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -14,10 +14,11 @@ //! ownership of the original. use std::borrow::Cow; +use std::sync::Arc; use std::{cmp, fmt, iter}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{self, Lrc}; +use rustc_data_structures::sync; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::{Decodable, Encodable}; use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym}; @@ -119,11 +120,11 @@ impl ToAttrTokenStream for AttrTokenStream { /// of an actual `TokenStream` until it is needed. /// `Box` is here only to reduce the structure size. #[derive(Clone)] -pub struct LazyAttrTokenStream(Lrc>); +pub struct LazyAttrTokenStream(Arc>); impl LazyAttrTokenStream { pub fn new(inner: impl ToAttrTokenStream + 'static) -> LazyAttrTokenStream { - LazyAttrTokenStream(Lrc::new(Box::new(inner))) + LazyAttrTokenStream(Arc::new(Box::new(inner))) } pub fn to_attr_token_stream(&self) -> AttrTokenStream { @@ -160,7 +161,7 @@ impl HashStable for LazyAttrTokenStream { /// during expansion to perform early cfg-expansion, and to process attributes /// during proc-macro invocations. #[derive(Clone, Debug, Default, Encodable, Decodable)] -pub struct AttrTokenStream(pub Lrc>); +pub struct AttrTokenStream(pub Arc>); /// Like `TokenTree`, but for `AttrTokenStream`. #[derive(Clone, Debug, Encodable, Decodable)] @@ -175,7 +176,7 @@ pub enum AttrTokenTree { impl AttrTokenStream { pub fn new(tokens: Vec) -> AttrTokenStream { - AttrTokenStream(Lrc::new(tokens)) + AttrTokenStream(Arc::new(tokens)) } /// Converts this `AttrTokenStream` to a plain `Vec`. During @@ -293,7 +294,7 @@ pub struct AttrsTarget { /// Today's `TokenTree`s can still contain AST via `token::Interpolated` for /// backwards compatibility. #[derive(Clone, Debug, Default, Encodable, Decodable)] -pub struct TokenStream(pub(crate) Lrc>); +pub struct TokenStream(pub(crate) Arc>); /// Indicates whether a token can join with the following token to form a /// compound token. Used for conversions to `proc_macro::Spacing`. Also used to @@ -412,7 +413,7 @@ impl PartialEq for TokenStream { impl TokenStream { pub fn new(tts: Vec) -> TokenStream { - TokenStream(Lrc::new(tts)) + TokenStream(Arc::new(tts)) } pub fn is_empty(&self) -> bool { @@ -544,7 +545,7 @@ impl TokenStream { /// Push `tt` onto the end of the stream, possibly gluing it to the last /// token. Uses `make_mut` to maximize efficiency. pub fn push_tree(&mut self, tt: TokenTree) { - let vec_mut = Lrc::make_mut(&mut self.0); + let vec_mut = Arc::make_mut(&mut self.0); if Self::try_glue_to_last(vec_mut, &tt) { // nothing else to do @@ -557,7 +558,7 @@ impl TokenStream { /// token tree to the last token. (No other token trees will be glued.) /// Uses `make_mut` to maximize efficiency. pub fn push_stream(&mut self, stream: TokenStream) { - let vec_mut = Lrc::make_mut(&mut self.0); + let vec_mut = Arc::make_mut(&mut self.0); let stream_iter = stream.0.iter().cloned(); @@ -577,7 +578,7 @@ impl TokenStream { } /// Desugar doc comments like `/// foo` in the stream into `#[doc = - /// r"foo"]`. Modifies the `TokenStream` via `Lrc::make_mut`, but as little + /// r"foo"]`. Modifies the `TokenStream` via `Arc::make_mut`, but as little /// as possible. pub fn desugar_doc_comments(&mut self) { if let Some(desugared_stream) = desugar_inner(self.clone()) { @@ -596,7 +597,7 @@ impl TokenStream { ) => { let desugared = desugared_tts(attr_style, data, span); let desugared_len = desugared.len(); - Lrc::make_mut(&mut stream.0).splice(i..i + 1, desugared); + Arc::make_mut(&mut stream.0).splice(i..i + 1, desugared); modified = true; i += desugared_len; } @@ -607,7 +608,7 @@ impl TokenStream { if let Some(desugared_delim_stream) = desugar_inner(delim_stream.clone()) { let new_tt = TokenTree::Delimited(sp, spacing, delim, desugared_delim_stream); - Lrc::make_mut(&mut stream.0)[i] = new_tt; + Arc::make_mut(&mut stream.0)[i] = new_tt; modified = true; } i += 1; diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 4459cb962e8e..6896ac723fa5 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -121,7 +121,7 @@ impl LitKind { } token::ByteStrRaw(n) => { // Raw strings have no escapes so we can convert the symbol - // directly to a `Lrc`. + // directly to a `Arc`. let buf = symbol.as_str().to_owned().into_bytes(); LitKind::ByteStr(buf.into(), StrStyle::Raw(n)) } @@ -142,7 +142,7 @@ impl LitKind { } token::CStrRaw(n) => { // Raw strings have no escapes so we can convert the symbol - // directly to a `Lrc` after appending the terminating NUL + // directly to a `Arc` after appending the terminating NUL // char. let mut buf = symbol.as_str().to_owned().into_bytes(); buf.push(0); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 89d5b5a75a24..dded18fce0a7 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1,11 +1,11 @@ use std::assert_matches::assert_matches; use std::ops::ControlFlow; +use std::sync::Arc; use rustc_ast::ptr::P as AstP; use rustc_ast::*; use rustc_ast_pretty::pprust::expr_to_string; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::HirId; use rustc_hir::def::{DefKind, Res}; @@ -147,7 +147,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::IncludedBytes(bytes) => { let lit = self.arena.alloc(respan( self.lower_span(e.span), - LitKind::ByteStr(Lrc::clone(bytes), StrStyle::Cooked), + LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked), )); hir::ExprKind::Lit(lit) } @@ -625,7 +625,7 @@ impl<'hir> LoweringContext<'_, 'hir> { this.mark_span_with_reason( DesugaringKind::TryBlock, expr.span, - Some(Lrc::clone(&this.allow_try_trait)), + Some(Arc::clone(&this.allow_try_trait)), ), expr, ) @@ -633,7 +633,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let try_span = this.mark_span_with_reason( DesugaringKind::TryBlock, this.tcx.sess.source_map().end_point(body.span), - Some(Lrc::clone(&this.allow_try_trait)), + Some(Arc::clone(&this.allow_try_trait)), ); (try_span, this.expr_unit(try_span)) @@ -742,7 +742,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::Async, self.lower_span(span), - Some(Lrc::clone(&self.allow_gen_future)), + Some(Arc::clone(&self.allow_gen_future)), ); let resume_ty = self.make_lang_item_qpath(hir::LangItem::ResumeTy, unstable_span, None); @@ -826,7 +826,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::Async, span, - Some(Lrc::clone(&self.allow_gen_future)), + Some(Arc::clone(&self.allow_gen_future)), ); self.lower_attrs(inner_hir_id, &[Attribute { kind: AttrKind::Normal(ptr::P(NormalAttr::from_ident(Ident::new( @@ -902,13 +902,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let features = match await_kind { FutureKind::Future => None, - FutureKind::AsyncIterator => Some(Lrc::clone(&self.allow_for_await)), + FutureKind::AsyncIterator => Some(Arc::clone(&self.allow_for_await)), }; let span = self.mark_span_with_reason(DesugaringKind::Await, await_kw_span, features); let gen_future_span = self.mark_span_with_reason( DesugaringKind::Await, full_span, - Some(Lrc::clone(&self.allow_gen_future)), + Some(Arc::clone(&self.allow_gen_future)), ); let expr_hir_id = expr.hir_id; @@ -1952,13 +1952,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::QuestionMark, span, - Some(Lrc::clone(&self.allow_try_trait)), + Some(Arc::clone(&self.allow_try_trait)), ); let try_span = self.tcx.sess.source_map().end_point(span); let try_span = self.mark_span_with_reason( DesugaringKind::QuestionMark, try_span, - Some(Lrc::clone(&self.allow_try_trait)), + Some(Arc::clone(&self.allow_try_trait)), ); // `Try::branch()` @@ -2053,7 +2053,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::YeetExpr, span, - Some(Lrc::clone(&self.allow_try_trait)), + Some(Arc::clone(&self.allow_try_trait)), ); let from_yeet_expr = self.wrap_in_try_constructor( diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index bb7c66f60940..127b7e3684e9 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -41,13 +41,14 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end +use std::sync::Arc; + use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, *}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::Lrc; use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; @@ -144,11 +145,11 @@ struct LoweringContext<'a, 'hir> { #[cfg(debug_assertions)] node_id_to_local_id: NodeMap, - allow_try_trait: Lrc<[Symbol]>, - allow_gen_future: Lrc<[Symbol]>, - allow_async_iterator: Lrc<[Symbol]>, - allow_for_await: Lrc<[Symbol]>, - allow_async_fn_traits: Lrc<[Symbol]>, + allow_try_trait: Arc<[Symbol]>, + allow_gen_future: Arc<[Symbol]>, + allow_async_iterator: Arc<[Symbol]>, + allow_for_await: Arc<[Symbol]>, + allow_async_fn_traits: Arc<[Symbol]>, } impl<'a, 'hir> LoweringContext<'a, 'hir> { @@ -738,7 +739,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &self, reason: DesugaringKind, span: Span, - allow_internal_unstable: Option>, + allow_internal_unstable: Option>, ) -> Span { self.tcx.with_stable_hashing_context(|hcx| { span.mark_with_reason(allow_internal_unstable, reason, span.edition(), hcx) @@ -1686,7 +1687,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { CoroutineKind::Async { return_impl_trait_id, .. } => (return_impl_trait_id, None), CoroutineKind::Gen { return_impl_trait_id, .. } => (return_impl_trait_id, None), CoroutineKind::AsyncGen { return_impl_trait_id, .. } => { - (return_impl_trait_id, Some(Lrc::clone(&self.allow_async_iterator))) + (return_impl_trait_id, Some(Arc::clone(&self.allow_async_iterator))) } }; diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 75abff7461b3..55d064773132 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -1,5 +1,6 @@ +use std::sync::Arc; + use rustc_ast::{self as ast, *}; -use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::GenericArg; use rustc_hir::def::{DefKind, PartialRes, Res}; @@ -72,7 +73,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let bound_modifier_allowed_features = if let Res::Def(DefKind::Trait, async_def_id) = res && self.tcx.async_fn_trait_kind_from_def_id(async_def_id).is_some() { - Some(Lrc::clone(&self.allow_async_fn_traits)) + Some(Arc::clone(&self.allow_async_fn_traits)) } else { None }; @@ -257,7 +258,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Additional features ungated with a bound modifier like `async`. // This is passed down to the implicit associated type binding in // parenthesized bounds. - bound_modifier_allowed_features: Option>, + bound_modifier_allowed_features: Option>, ) -> hir::PathSegment<'hir> { debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment); let (mut generic_args, infer_args) = if let Some(generic_args) = segment.args.as_deref() { @@ -490,7 +491,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, data: &ParenthesizedArgs, itctx: ImplTraitContext, - bound_modifier_allowed_features: Option>, + bound_modifier_allowed_features: Option>, ) -> (GenericArgsCtor<'hir>, bool) { // Switch to `PassThrough` mode for anonymous lifetimes; this // means that we permit things like `&Ref`, where `Ref` has diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 9b958ed6b0d6..eeec24e5ea4e 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -7,6 +7,7 @@ mod fixup; mod item; use std::borrow::Cow; +use std::sync::Arc; use rustc_ast::attr::AttrIdGenerator; use rustc_ast::ptr::P; @@ -21,7 +22,6 @@ use rustc_ast::{ InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass, InlineAsmTemplatePiece, PatKind, RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr, }; -use rustc_data_structures::sync::Lrc; use rustc_span::edition::Edition; use rustc_span::source_map::{SourceMap, Spanned}; use rustc_span::symbol::IdentPrinter; @@ -106,7 +106,7 @@ fn split_block_comment_into_lines(text: &str, col: CharPos) -> Vec { fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec { let sm = SourceMap::new(sm.path_mapping().clone()); let source_file = sm.new_source_file(path, src); - let text = Lrc::clone(&(*source_file.src.as_ref().unwrap())); + let text = Arc::clone(&(*source_file.src.as_ref().unwrap())); let text: &str = text.as_str(); let start_bpos = source_file.start_pos; diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index d163da3ddea3..8142f1518dd8 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -1,12 +1,12 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; +use std::sync::Arc; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; -use rustc_data_structures::sync::Lrc; use rustc_expand::base::{ DummyResult, ExpandResult, ExtCtxt, MacEager, MacResult, MacroExpanderResult, resolve_path, }; @@ -249,7 +249,7 @@ fn load_binary_file( original_path: &Path, macro_span: Span, path_span: Span, -) -> Result<(Lrc<[u8]>, Span), Box> { +) -> Result<(Arc<[u8]>, Span), Box> { let resolved_path = match resolve_path(&cx.sess, original_path, macro_span) { Ok(path) => path, Err(err) => { diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 3f6fdea9fb8c..55e01687400a 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -1,10 +1,10 @@ use std::ops::Range; +use std::sync::Arc; use gccjit::{Location, RValue}; use rustc_abi::Size; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoCodegenMethods}; -use rustc_data_structures::sync::Lrc; use rustc_index::bit_set::DenseBitSet; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::{self, Body, SourceScope}; @@ -172,7 +172,7 @@ fn make_mir_scope<'gcc, 'tcx>( // `lookup_char_pos` return the right information instead. pub struct DebugLoc { /// Information about the original source file. - pub file: Lrc, + pub file: Arc, /// The (1-based) line number. pub line: u32, /// The (1-based) column number. diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 471cdc17148c..6a3e7403b651 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -2,6 +2,7 @@ use std::cell::{OnceCell, RefCell}; use std::ops::Range; +use std::sync::Arc; use std::{iter, ptr}; use libc::c_uint; @@ -10,7 +11,6 @@ use rustc_codegen_ssa::debuginfo::type_names; use rustc_codegen_ssa::mir::debuginfo::VariableKind::*; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; use rustc_codegen_ssa::traits::*; -use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::UnordMap; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_index::IndexVec; @@ -244,7 +244,7 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { // `lookup_char_pos` return the right information instead. struct DebugLoc { /// Information about the original source file. - file: Lrc, + file: Arc, /// The (1-based) line number. line: u32, /// The (1-based) column number. diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 4a84fd29e447..14346795fda6 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -71,14 +71,9 @@ mod debuginfo; mod declare; mod errors; mod intrinsic; - -// The following is a workaround that replaces `pub mod llvm;` and that fixes issue 53912. -#[path = "llvm/mod.rs"] -mod llvm_; -pub mod llvm { - pub use super::llvm_::*; -} - +// FIXME(Zalathar): Fix all the unreachable-pub warnings that would occur if +// this isn't pub, then make it not pub. +pub mod llvm; mod llvm_util; mod mono_item; mod type_; diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 2592a7df95c2..707aeba22ccf 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -10,13 +10,9 @@ use libc::c_uint; use rustc_abi::{Align, Size, WrappingRange}; use rustc_llvm::RustString; -pub use self::AtomicRmwBinOp::*; pub use self::CallConv::*; pub use self::CodeGenOptSize::*; -pub use self::IntPredicate::*; -pub use self::Linkage::*; pub use self::MetadataType::*; -pub use self::RealPredicate::*; pub use self::ffi::*; use crate::common::AsCCharPtr; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index df945920ee8d..d9fbf539fd3d 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -1,5 +1,6 @@ use std::cmp; use std::collections::BTreeSet; +use std::sync::Arc; use std::time::{Duration, Instant}; use itertools::Itertools; @@ -7,7 +8,7 @@ use rustc_abi::FIRST_VARIANT; use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name}; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; -use rustc_data_structures::sync::{Lrc, par_map}; +use rustc_data_structures::sync::par_map; use rustc_data_structures::unord::UnordMap; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; @@ -932,7 +933,7 @@ impl CrateInfo { crate_name: UnordMap::with_capacity(n_crates), used_crates, used_crate_source: UnordMap::with_capacity(n_crates), - dependency_formats: Lrc::clone(tcx.dependency_formats(())), + dependency_formats: Arc::clone(tcx.dependency_formats(())), windows_subsystem, natvis_debugger_visualizers: Default::default(), lint_levels: CodegenLintLevels::from_tcx(tcx), @@ -946,7 +947,7 @@ impl CrateInfo { info.crate_name.insert(cnum, tcx.crate_name(cnum)); let used_crate_source = tcx.used_crate_source(cnum); - info.used_crate_source.insert(cnum, Lrc::clone(used_crate_source)); + info.used_crate_source.insert(cnum, Arc::clone(used_crate_source)); if tcx.is_profiler_runtime(cnum) { info.profiler_runtime = Some(cnum); } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 40299ae26308..428a45975f1e 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -24,10 +24,10 @@ use std::collections::BTreeSet; use std::io; use std::path::{Path, PathBuf}; +use std::sync::Arc; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::UnordMap; use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::CrateNum; @@ -200,9 +200,9 @@ pub struct CrateInfo { pub native_libraries: FxIndexMap>, pub crate_name: UnordMap, pub used_libraries: Vec, - pub used_crate_source: UnordMap>, + pub used_crate_source: UnordMap>, pub used_crates: Vec, - pub dependency_formats: Lrc, + pub dependency_formats: Arc, pub windows_subsystem: Option, pub natvis_debugger_visualizers: BTreeSet, pub lint_levels: CodegenLintLevels, diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index b44d2a01d57c..ed5489652fba 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -51,6 +51,8 @@ pub fn provide(providers: &mut Providers) { providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| { util::check_validity_requirement(tcx, init_kind, param_env_and_ty) }; + providers.hooks.validate_scalar_in_layout = + |tcx, scalar, layout| util::validate_scalar_in_layout(tcx, scalar, layout); } /// `rustc_driver::main` installs a handler that will set this to `true` if diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index a729d9325c84..79baf91c3ce6 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -1,9 +1,10 @@ use rustc_abi::{BackendRepr, FieldsShape, Scalar, Variants}; -use rustc_middle::bug; use rustc_middle::ty::layout::{ HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement, }; -use rustc_middle::ty::{PseudoCanonicalInput, Ty, TyCtxt}; +use rustc_middle::ty::{PseudoCanonicalInput, ScalarInt, Ty, TyCtxt}; +use rustc_middle::{bug, ty}; +use rustc_span::DUMMY_SP; use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine}; use crate::interpret::{InterpCx, MemoryKind}; @@ -34,7 +35,7 @@ pub fn check_validity_requirement<'tcx>( let layout_cx = LayoutCx::new(tcx, input.typing_env); if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks { - check_validity_requirement_strict(layout, &layout_cx, kind) + Ok(check_validity_requirement_strict(layout, &layout_cx, kind)) } else { check_validity_requirement_lax(layout, &layout_cx, kind) } @@ -46,10 +47,10 @@ fn check_validity_requirement_strict<'tcx>( ty: TyAndLayout<'tcx>, cx: &LayoutCx<'tcx>, kind: ValidityRequirement, -) -> Result> { +) -> bool { let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error); - let mut cx = InterpCx::new(cx.tcx(), rustc_span::DUMMY_SP, cx.typing_env, machine); + let mut cx = InterpCx::new(cx.tcx(), DUMMY_SP, cx.typing_env, machine); let allocated = cx .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) @@ -69,14 +70,13 @@ fn check_validity_requirement_strict<'tcx>( // due to this. // The value we are validating is temporary and discarded at the end of this function, so // there is no point in reseting provenance and padding. - Ok(cx - .validate_operand( - &allocated.into(), - /*recursive*/ false, - /*reset_provenance_and_padding*/ false, - ) - .discard_err() - .is_some()) + cx.validate_operand( + &allocated.into(), + /*recursive*/ false, + /*reset_provenance_and_padding*/ false, + ) + .discard_err() + .is_some() } /// Implements the 'lax' (default) version of the [`check_validity_requirement`] checks; see that @@ -168,3 +168,31 @@ fn check_validity_requirement_lax<'tcx>( Ok(true) } + +pub(crate) fn validate_scalar_in_layout<'tcx>( + tcx: TyCtxt<'tcx>, + scalar: ScalarInt, + ty: Ty<'tcx>, +) -> bool { + let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error); + + let typing_env = ty::TypingEnv::fully_monomorphized(); + let mut cx = InterpCx::new(tcx, DUMMY_SP, typing_env, machine); + + let Ok(layout) = cx.layout_of(ty) else { + bug!("could not compute layout of {scalar:?}:{ty:?}") + }; + let allocated = cx + .allocate(layout, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) + .expect("OOM: failed to allocate for uninit check"); + + cx.write_scalar(scalar, &allocated).unwrap(); + + cx.validate_operand( + &allocated.into(), + /*recursive*/ false, + /*reset_provenance_and_padding*/ false, + ) + .discard_err() + .is_some() +} diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 25a9dbb2c111..5be5ee8d1ae9 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -8,6 +8,7 @@ mod type_name; pub use self::alignment::{is_disaligned, is_within_packed}; pub use self::check_validity_requirement::check_validity_requirement; +pub(crate) use self::check_validity_requirement::validate_scalar_in_layout; pub use self::compare_types::{relate_types, sub_types}; pub use self::type_name::type_name; diff --git a/compiler/rustc_data_structures/src/memmap.rs b/compiler/rustc_data_structures/src/memmap.rs index c7f66b2fee80..d64a5862f4e7 100644 --- a/compiler/rustc_data_structures/src/memmap.rs +++ b/compiler/rustc_data_structures/src/memmap.rs @@ -3,13 +3,13 @@ use std::io; use std::ops::{Deref, DerefMut}; /// A trivial wrapper for [`memmap2::Mmap`] (or `Vec` on WASM). -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(miri, target_arch = "wasm32")))] pub struct Mmap(memmap2::Mmap); -#[cfg(target_arch = "wasm32")] +#[cfg(any(miri, target_arch = "wasm32"))] pub struct Mmap(Vec); -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(miri, target_arch = "wasm32")))] impl Mmap { /// # Safety /// @@ -29,7 +29,7 @@ impl Mmap { } } -#[cfg(target_arch = "wasm32")] +#[cfg(any(miri, target_arch = "wasm32"))] impl Mmap { #[inline] pub unsafe fn map(mut file: File) -> io::Result { @@ -56,13 +56,13 @@ impl AsRef<[u8]> for Mmap { } } -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(miri, target_arch = "wasm32")))] pub struct MmapMut(memmap2::MmapMut); -#[cfg(target_arch = "wasm32")] +#[cfg(any(miri, target_arch = "wasm32"))] pub struct MmapMut(Vec); -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(miri, target_arch = "wasm32")))] impl MmapMut { #[inline] pub fn map_anon(len: usize) -> io::Result { @@ -82,7 +82,7 @@ impl MmapMut { } } -#[cfg(target_arch = "wasm32")] +#[cfg(any(miri, target_arch = "wasm32"))] impl MmapMut { #[inline] pub fn map_anon(len: usize) -> io::Result { diff --git a/compiler/rustc_data_structures/src/owned_slice.rs b/compiler/rustc_data_structures/src/owned_slice.rs index c8be0ab52e9d..17c48aee6fa3 100644 --- a/compiler/rustc_data_structures/src/owned_slice.rs +++ b/compiler/rustc_data_structures/src/owned_slice.rs @@ -1,15 +1,15 @@ use std::borrow::Borrow; use std::ops::Deref; +use std::sync::Arc; // Use our fake Send/Sync traits when on not parallel compiler, // so that `OwnedSlice` only implements/requires Send/Sync // for parallel compiler builds. use crate::sync; -use crate::sync::Lrc; /// An owned slice. /// -/// This is similar to `Lrc<[u8]>` but allows slicing and using anything as the +/// This is similar to `Arc<[u8]>` but allows slicing and using anything as the /// backing buffer. /// /// See [`slice_owned`] for `OwnedSlice` construction and examples. @@ -34,7 +34,7 @@ pub struct OwnedSlice { // \/ // ⊂(´・◡・⊂ )∘˚˳° (I am the phantom remnant of #97770) #[expect(dead_code)] - owner: Lrc, + owner: Arc, } /// Makes an [`OwnedSlice`] out of an `owner` and a `slicer` function. @@ -86,7 +86,7 @@ where // N.B. the HRTB on the `slicer` is important — without it the caller could provide // a short lived slice, unrelated to the owner. - let owner = Lrc::new(owner); + let owner = Arc::new(owner); let bytes = slicer(&*owner)?; Ok(OwnedSlice { bytes, owner }) diff --git a/compiler/rustc_data_structures/src/stack.rs b/compiler/rustc_data_structures/src/stack.rs index 3d6d00034832..102b36409115 100644 --- a/compiler/rustc_data_structures/src/stack.rs +++ b/compiler/rustc_data_structures/src/stack.rs @@ -17,6 +17,18 @@ const STACK_PER_RECURSION: usize = 16 * 1024 * 1024; // 16MB /// /// Should not be sprinkled around carelessly, as it causes a little bit of overhead. #[inline] +#[cfg(not(miri))] pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { stacker::maybe_grow(RED_ZONE, STACK_PER_RECURSION, f) } + +/// Grows the stack on demand to prevent stack overflow. Call this in strategic locations +/// to "break up" recursive calls. E.g. almost any call to `visit_expr` or equivalent can benefit +/// from this. +/// +/// Should not be sprinkled around carelessly, as it causes a little bit of overhead. +#[cfg(miri)] +#[inline] +pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { + f() +} diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 7a9533031f4b..bea87a6685d8 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -18,7 +18,6 @@ //! //! | Type | Serial version | Parallel version | //! | ----------------------- | ------------------- | ------------------------------- | -//! | `Lrc` | `rc::Rc` | `sync::Arc` | //! |` Weak` | `rc::Weak` | `sync::Weak` | //! | `LRef<'a, T>` [^2] | `&'a mut T` | `&'a T` | //! | | | | @@ -109,7 +108,7 @@ pub use std::marker::{Send, Sync}; #[cfg(target_has_atomic = "64")] pub use std::sync::atomic::AtomicU64; pub use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize}; -pub use std::sync::{Arc as Lrc, OnceLock, Weak}; +pub use std::sync::{OnceLock, Weak}; pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; pub use parking_lot::{ diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 6ea14d15c144..4c47ce93dd56 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -89,10 +89,10 @@ pub mod pretty; #[macro_use] mod print; mod session_diagnostics; -#[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))] +#[cfg(all(not(miri), unix, any(target_env = "gnu", target_os = "macos")))] mod signal_handler; -#[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))] +#[cfg(not(all(not(miri), unix, any(target_env = "gnu", target_os = "macos"))))] mod signal_handler { /// On platforms which don't support our signal handler's requirements, /// simply use the default signal handler provided by std. @@ -1024,7 +1024,7 @@ pub fn describe_flag_categories(early_dcx: &EarlyDiagCtxt, matches: &Matches) -> let wall = matches.opt_strs("W"); if wall.iter().any(|x| *x == "all") { print_wall_help(); - rustc_errors::FatalError.raise(); + return true; } // Don't handle -W help here, because we might first load additional lints. @@ -1474,7 +1474,7 @@ pub fn init_logger(early_dcx: &EarlyDiagCtxt, cfg: rustc_log::LoggerConfig) { /// Install our usual `ctrlc` handler, which sets [`rustc_const_eval::CTRL_C_RECEIVED`]. /// Making this handler optional lets tools can install a different handler, if they wish. pub fn install_ctrlc_handler() { - #[cfg(not(target_family = "wasm"))] + #[cfg(all(not(miri), not(target_family = "wasm")))] ctrlc::set_handler(move || { // Indicate that we have been signaled to stop, then give the rest of the compiler a bit of // time to check CTRL_C_RECEIVED and run its own shutdown logic, but after a short amount diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index a51c4140e176..ba1c3e185c2d 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -10,7 +10,7 @@ use std::borrow::Cow; use std::error::Error; use std::path::{Path, PathBuf}; -use std::sync::LazyLock; +use std::sync::{Arc, LazyLock}; use std::{fmt, fs, io}; use fluent_bundle::FluentResource; @@ -19,7 +19,7 @@ pub use fluent_bundle::{self, FluentArgs, FluentError, FluentValue}; use fluent_syntax::parser::ParserError; use icu_provider_adapters::fallback::{LocaleFallbackProvider, LocaleFallbacker}; use intl_memoizer::concurrent::IntlLangMemoizer; -use rustc_data_structures::sync::{IntoDynSyncSend, Lrc}; +use rustc_data_structures::sync::IntoDynSyncSend; use rustc_macros::{Decodable, Encodable}; use rustc_span::Span; use tracing::{instrument, trace}; @@ -112,7 +112,7 @@ pub fn fluent_bundle( requested_locale: Option, additional_ftl_path: Option<&Path>, with_directionality_markers: bool, -) -> Result>, TranslationBundleError> { +) -> Result>, TranslationBundleError> { if requested_locale.is_none() && additional_ftl_path.is_none() { return Ok(None); } @@ -190,7 +190,7 @@ pub fn fluent_bundle( bundle.add_resource_overriding(resource); } - let bundle = Lrc::new(bundle); + let bundle = Arc::new(bundle); Ok(Some(bundle)) } @@ -205,7 +205,7 @@ fn register_functions(bundle: &mut FluentBundle) { /// Type alias for the result of `fallback_fluent_bundle` - a reference-counted pointer to a lazily /// evaluated fluent bundle. -pub type LazyFallbackBundle = Lrc FluentBundle>>; +pub type LazyFallbackBundle = Arc FluentBundle>>; /// Return the default `FluentBundle` with standard "en-US" diagnostic messages. #[instrument(level = "trace", skip(resources))] @@ -213,7 +213,7 @@ pub fn fallback_fluent_bundle( resources: Vec<&'static str>, with_directionality_markers: bool, ) -> LazyFallbackBundle { - Lrc::new(LazyLock::new(move || { + Arc::new(LazyLock::new(move || { let mut fallback_bundle = new_bundle(vec![langid!("en-US")]); register_functions(&mut fallback_bundle); diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index b337e2794002..f0636b600b70 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -5,8 +5,9 @@ //! //! [annotate_snippets]: https://docs.rs/crate/annotate-snippets/ +use std::sync::Arc; + use annotate_snippets::{Renderer, Snippet}; -use rustc_data_structures::sync::Lrc; use rustc_error_messages::FluentArgs; use rustc_span::SourceFile; use rustc_span::source_map::SourceMap; @@ -22,8 +23,8 @@ use crate::{ /// Generates diagnostics using annotate-snippet pub struct AnnotateSnippetEmitter { - source_map: Option>, - fluent_bundle: Option>, + source_map: Option>, + fluent_bundle: Option>, fallback_bundle: LazyFallbackBundle, /// If true, hides the longer explanation text @@ -80,7 +81,7 @@ impl Emitter for AnnotateSnippetEmitter { } /// Provides the source string for the given `line` of `file` -fn source_string(file: Lrc, line: &Line) -> String { +fn source_string(file: Arc, line: &Line) -> String { file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or_default() } @@ -102,8 +103,8 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::Level { impl AnnotateSnippetEmitter { pub fn new( - source_map: Option>, - fluent_bundle: Option>, + source_map: Option>, + fluent_bundle: Option>, fallback_bundle: LazyFallbackBundle, short_message: bool, macro_backtrace: bool, @@ -174,7 +175,7 @@ impl AnnotateSnippetEmitter { source_map.ensure_source_file_source_present(&file); ( format!("{}", source_map.filename_for_diagnostics(&file.name)), - source_string(Lrc::clone(&file), &line), + source_string(Arc::clone(&file), &line), line.line_index, line.annotations, ) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index d0b4211c351c..15cf285e7ffc 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -14,10 +14,11 @@ use std::io::prelude::*; use std::io::{self, IsTerminal}; use std::iter; use std::path::Path; +use std::sync::Arc; use derive_setters::Setters; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; -use rustc_data_structures::sync::{DynSend, IntoDynSyncSend, Lrc}; +use rustc_data_structures::sync::{DynSend, IntoDynSyncSend}; use rustc_error_messages::{FluentArgs, SpanLabel}; use rustc_lexer; use rustc_lint_defs::pluralize; @@ -610,8 +611,8 @@ pub enum OutputTheme { pub struct HumanEmitter { #[setters(skip)] dst: IntoDynSyncSend, - sm: Option>, - fluent_bundle: Option>, + sm: Option>, + fluent_bundle: Option>, #[setters(skip)] fallback_bundle: LazyFallbackBundle, short_message: bool, @@ -628,7 +629,7 @@ pub struct HumanEmitter { #[derive(Debug)] pub(crate) struct FileWithAnnotatedLines { - pub(crate) file: Lrc, + pub(crate) file: Arc, pub(crate) lines: Vec, multiline_depth: usize, } @@ -712,7 +713,7 @@ impl HumanEmitter { fn render_source_line( &self, buffer: &mut StyledBuffer, - file: Lrc, + file: Arc, line: &Line, width_offset: usize, code_offset: usize, @@ -1691,7 +1692,7 @@ impl HumanEmitter { // Get the left-side margin to remove it let mut whitespace_margin = usize::MAX; for line_idx in 0..annotated_file.lines.len() { - let file = Lrc::clone(&annotated_file.file); + let file = Arc::clone(&annotated_file.file); let line = &annotated_file.lines[line_idx]; if let Some(source_string) = line.line_index.checked_sub(1).and_then(|l| file.get_line(l)) @@ -1764,7 +1765,7 @@ impl HumanEmitter { let column_width = if let Some(width) = self.diagnostic_width { width.saturating_sub(code_offset) - } else if self.ui_testing { + } else if self.ui_testing || cfg!(miri) { DEFAULT_COLUMN_WIDTH } else { termize::dimensions() @@ -1787,7 +1788,7 @@ impl HumanEmitter { let depths = self.render_source_line( &mut buffer, - Lrc::clone(&annotated_file.file), + Arc::clone(&annotated_file.file), &annotated_file.lines[line_idx], width_offset, code_offset, @@ -2976,7 +2977,7 @@ impl FileWithAnnotatedLines { ) -> Vec { fn add_annotation_to_file( file_vec: &mut Vec, - file: Lrc, + file: Arc, line_index: usize, ann: Annotation, ) { @@ -3113,7 +3114,7 @@ impl FileWithAnnotatedLines { // | baz add_annotation_to_file( &mut output, - Lrc::clone(&file), + Arc::clone(&file), ann.line_start, ann.as_start(), ); @@ -3140,12 +3141,12 @@ impl FileWithAnnotatedLines { .unwrap_or(ann.line_start); for line in ann.line_start + 1..until { // Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`). - add_annotation_to_file(&mut output, Lrc::clone(&file), line, ann.as_line()); + add_annotation_to_file(&mut output, Arc::clone(&file), line, ann.as_line()); } let line_end = ann.line_end - 1; let end_is_empty = file.get_line(line_end - 1).is_some_and(|s| !filter(&s)); if middle < line_end && !end_is_empty { - add_annotation_to_file(&mut output, Lrc::clone(&file), line_end, ann.as_line()); + add_annotation_to_file(&mut output, Arc::clone(&file), line_end, ann.as_line()); } } else { end_ann.annotation_type = AnnotationType::Singleline; diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 95c81fc5f448..7d7f364fec23 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -16,7 +16,7 @@ use std::sync::{Arc, Mutex}; use std::vec; use derive_setters::Setters; -use rustc_data_structures::sync::{IntoDynSyncSend, Lrc}; +use rustc_data_structures::sync::IntoDynSyncSend; use rustc_error_messages::FluentArgs; use rustc_lint_defs::Applicability; use rustc_span::Span; @@ -45,8 +45,8 @@ pub struct JsonEmitter { #[setters(skip)] dst: IntoDynSyncSend>, #[setters(skip)] - sm: Option>, - fluent_bundle: Option>, + sm: Option>, + fluent_bundle: Option>, #[setters(skip)] fallback_bundle: LazyFallbackBundle, #[setters(skip)] @@ -65,7 +65,7 @@ pub struct JsonEmitter { impl JsonEmitter { pub fn new( dst: Box, - sm: Option>, + sm: Option>, fallback_bundle: LazyFallbackBundle, pretty: bool, json_rendered: HumanReadableErrorType, @@ -369,7 +369,7 @@ impl Diagnostic { ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)), ColorConfig::Never => {} } - HumanEmitter::new(dst, Lrc::clone(&je.fallback_bundle)) + HumanEmitter::new(dst, Arc::clone(&je.fallback_bundle)) .short_message(short) .sm(je.sm.clone()) .fluent_bundle(je.fluent_bundle.clone()) diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index cebaf7c1cfe4..f7892db29610 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -39,7 +39,7 @@ impl Write for Shared { /// Test the span yields correct positions in JSON. fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { rustc_span::create_default_session_globals_then(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned()); let fallback_bundle = crate::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 7a02e0dd2f09..9af17db9a6e6 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1048,8 +1048,8 @@ impl<'a> DiagCtxtHandle<'a> { /// bad results, such as spurious/uninteresting additional errors -- when /// returning an error `Result` is difficult. pub fn abort_if_errors(&self) { - if self.has_errors().is_some() { - FatalError.raise(); + if let Some(guar) = self.has_errors() { + guar.raise_fatal(); } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 43feab94c01d..819694d1cdc1 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -3,6 +3,7 @@ use std::iter; use std::path::Component::Prefix; use std::path::{Path, PathBuf}; use std::rc::Rc; +use std::sync::Arc; use rustc_ast::attr::{AttributeExt, MarkedAttrs}; use rustc_ast::ptr::P; @@ -12,7 +13,7 @@ use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; use rustc_attr_parsing::{self as attr, Deprecation, Stability}; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::sync::{self, Lrc}; +use rustc_data_structures::sync; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; use rustc_feature::Features; use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools}; @@ -727,7 +728,7 @@ pub struct SyntaxExtension { /// Span of the macro definition. pub span: Span, /// List of unstable features that are treated as stable inside this macro. - pub allow_internal_unstable: Option>, + pub allow_internal_unstable: Option>, /// The macro's stability info. pub stability: Option, /// The macro's deprecation info. @@ -986,7 +987,7 @@ pub struct Indeterminate; pub struct DeriveResolution { pub path: ast::Path, pub item: Annotatable, - pub exts: Option>, + pub exts: Option>, pub is_const: bool, } @@ -1017,7 +1018,7 @@ pub trait ResolverExpand { invoc: &Invocation, eager_expansion_root: LocalExpnId, force: bool, - ) -> Result, Indeterminate>; + ) -> Result, Indeterminate>; fn record_macro_rule_usage(&mut self, mac_id: NodeId, rule_index: usize); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index ec497f6f8f1f..e3f31ebeca3f 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,6 +1,7 @@ use std::ops::Deref; use std::path::PathBuf; use std::rc::Rc; +use std::sync::Arc; use std::{iter, mem}; use rustc_ast as ast; @@ -16,7 +17,6 @@ use rustc_ast::{ }; use rustc_ast_pretty::pprust; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; -use rustc_data_structures::sync::Lrc; use rustc_errors::PResult; use rustc_feature::Features; use rustc_parse::parser::{ @@ -579,7 +579,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { &mut self, mut fragment: AstFragment, extra_placeholders: &[NodeId], - ) -> (AstFragment, Vec<(Invocation, Option>)>) { + ) -> (AstFragment, Vec<(Invocation, Option>)>) { // Resolve `$crate`s in the fragment for pretty-printing. self.cx.resolver.resolve_dollar_crates(); @@ -1774,7 +1774,7 @@ fn build_single_delegations<'a, Node: InvocationCollectorNode>( struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, - invocations: Vec<(Invocation, Option>)>, + invocations: Vec<(Invocation, Option>)>, monotonic: bool, } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 21688521ade8..595c8c3279f4 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -1,11 +1,11 @@ use std::mem; +use std::sync::Arc; use rustc_ast::ExprKind; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{self, Delimiter, IdentIsRaw, Lit, LitKind, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; use rustc_errors::{Diag, DiagCtxtHandle, PResult, pluralize}; use rustc_parse::lexer::nfc_normalize; use rustc_parse::parser::ParseNtResult; @@ -299,7 +299,7 @@ pub(super) fn transcribe<'a>( marker.visit_span(&mut sp); let use_span = nt.use_span(); with_metavar_spans(|mspans| mspans.insert(use_span, sp)); - TokenTree::token_alone(token::Interpolated(Lrc::clone(nt)), sp) + TokenTree::token_alone(token::Interpolated(Arc::clone(nt)), sp) } MatchedSeq(..) => { // We were unable to descend far enough. This is an error. diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 7eb09a64e96a..5581a6e65089 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -1,4 +1,5 @@ use std::ops::{Bound, Range}; +use std::sync::Arc; use ast::token::IdentIsRaw; use pm::bridge::{ @@ -11,7 +12,6 @@ use rustc_ast::tokenstream::{self, DelimSpacing, Spacing, TokenStream}; use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult}; use rustc_parse::lexer::nfc_normalize; use rustc_parse::parser::Parser; @@ -446,7 +446,7 @@ impl<'a, 'b> Rustc<'a, 'b> { impl server::Types for Rustc<'_, '_> { type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; - type SourceFile = Lrc; + type SourceFile = Arc; type Span = Span; type Symbol = Symbol; } @@ -657,7 +657,7 @@ impl server::TokenStream for Rustc<'_, '_> { impl server::SourceFile for Rustc<'_, '_> { fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { - Lrc::ptr_eq(file1, file2) + Arc::ptr_eq(file1, file2) } fn path(&mut self, file: &Self::SourceFile) -> String { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 1c87a0eb1a78..e7b6a97c9d96 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -206,7 +206,7 @@ pub type UsePath<'hir> = Path<'hir, SmallVec<[Res; 3]>>; impl Path<'_> { pub fn is_global(&self) -> bool { - !self.segments.is_empty() && self.segments[0].ident.name == kw::PathRoot + self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot) } } @@ -1061,10 +1061,7 @@ impl Attribute { pub fn value_lit(&self) -> Option<&MetaItemLit> { match &self.kind { - AttrKind::Normal(n) => match n.as_ref() { - AttrItem { args: AttrArgs::Eq { expr, .. }, .. } => Some(expr), - _ => None, - }, + AttrKind::Normal(box AttrItem { args: AttrArgs::Eq { expr, .. }, .. }) => Some(expr), _ => None, } } @@ -1077,12 +1074,9 @@ impl AttributeExt for Attribute { fn meta_item_list(&self) -> Option> { match &self.kind { - AttrKind::Normal(n) => match n.as_ref() { - AttrItem { args: AttrArgs::Delimited(d), .. } => { - ast::MetaItemKind::list_from_tokens(d.tokens.clone()) - } - _ => None, - }, + AttrKind::Normal(box AttrItem { args: AttrArgs::Delimited(d), .. }) => { + ast::MetaItemKind::list_from_tokens(d.tokens.clone()) + } _ => None, } } @@ -1098,23 +1092,16 @@ impl AttributeExt for Attribute { /// For a single-segment attribute, returns its name; otherwise, returns `None`. fn ident(&self) -> Option { match &self.kind { - AttrKind::Normal(n) => { - if let [ident] = n.path.segments.as_ref() { - Some(*ident) - } else { - None - } - } - AttrKind::DocComment(..) => None, + AttrKind::Normal(box AttrItem { + path: AttrPath { segments: box [ident], .. }, .. + }) => Some(*ident), + _ => None, } } fn path_matches(&self, name: &[Symbol]) -> bool { match &self.kind { - AttrKind::Normal(n) => { - n.path.segments.len() == name.len() - && n.path.segments.iter().zip(name).all(|(s, n)| s.name == *n) - } + AttrKind::Normal(n) => n.path.segments.iter().map(|segment| &segment.name).eq(name), AttrKind::DocComment(..) => false, } } @@ -1128,12 +1115,7 @@ impl AttributeExt for Attribute { } fn is_word(&self) -> bool { - match &self.kind { - AttrKind::Normal(n) => { - matches!(n.args, AttrArgs::Empty) - } - AttrKind::DocComment(..) => false, - } + matches!(self.kind, AttrKind::Normal(box AttrItem { args: AttrArgs::Empty, .. })) } fn ident_path(&self) -> Option> { @@ -1990,7 +1972,7 @@ impl fmt::Display for ConstContext { } // NOTE: `IntoDiagArg` impl for `ConstContext` lives in `rustc_errors` -// due to a cyclical dependency between hir that crate. +// due to a cyclical dependency between hir and that crate. /// A literal. pub type Lit = Spanned; @@ -3604,10 +3586,10 @@ impl<'hir> FnRetTy<'hir> { } pub fn is_suggestable_infer_ty(&self) -> Option<&'hir Ty<'hir>> { - if let Self::Return(ty) = self { - if ty.is_suggestable_infer_ty() { - return Some(*ty); - } + if let Self::Return(ty) = self + && ty.is_suggestable_infer_ty() + { + return Some(*ty); } None } @@ -3976,11 +3958,11 @@ pub struct FnHeader { impl FnHeader { pub fn is_async(&self) -> bool { - matches!(&self.asyncness, IsAsync::Async(_)) + matches!(self.asyncness, IsAsync::Async(_)) } pub fn is_const(&self) -> bool { - matches!(&self.constness, Constness::Const) + matches!(self.constness, Constness::Const) } pub fn is_unsafe(&self) -> bool { @@ -4076,16 +4058,16 @@ pub struct Impl<'hir> { impl ItemKind<'_> { pub fn generics(&self) -> Option<&Generics<'_>> { - Some(match *self { - ItemKind::Fn { ref generics, .. } - | ItemKind::TyAlias(_, ref generics) - | ItemKind::Const(_, ref generics, _) - | ItemKind::Enum(_, ref generics) - | ItemKind::Struct(_, ref generics) - | ItemKind::Union(_, ref generics) - | ItemKind::Trait(_, _, ref generics, _, _) - | ItemKind::TraitAlias(ref generics, _) - | ItemKind::Impl(Impl { ref generics, .. }) => generics, + Some(match self { + ItemKind::Fn { generics, .. } + | ItemKind::TyAlias(_, generics) + | ItemKind::Const(_, generics, _) + | ItemKind::Enum(_, generics) + | ItemKind::Struct(_, generics) + | ItemKind::Union(_, generics) + | ItemKind::Trait(_, _, generics, _, _) + | ItemKind::TraitAlias(generics, _) + | ItemKind::Impl(Impl { generics, .. }) => generics, _ => return None, }) } @@ -4484,16 +4466,14 @@ impl<'hir> Node<'hir> { /// Get a `hir::Impl` if the node is an impl block for the given `trait_def_id`. pub fn impl_block_of_trait(self, trait_def_id: DefId) -> Option<&'hir Impl<'hir>> { - match self { - Node::Item(Item { kind: ItemKind::Impl(impl_block), .. }) - if impl_block - .of_trait - .and_then(|trait_ref| trait_ref.trait_def_id()) - .is_some_and(|trait_id| trait_id == trait_def_id) => - { - Some(impl_block) - } - _ => None, + if let Node::Item(Item { kind: ItemKind::Impl(impl_block), .. }) = self + && let Some(trait_ref) = impl_block.of_trait + && let Some(trait_id) = trait_ref.trait_def_id() + && trait_id == trait_def_id + { + Some(impl_block) + } else { + None } } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 6519552d6046..f632041dba9f 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -345,6 +345,9 @@ pub trait Visitor<'v>: Sized { fn visit_pat_expr(&mut self, expr: &'v PatExpr<'v>) -> Self::Result { walk_pat_expr(self, expr) } + fn visit_lit(&mut self, _hir_id: HirId, _lit: &'v Lit, _negated: bool) -> Self::Result { + Self::Result::output() + } fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result { walk_anon_const(self, c) } @@ -764,7 +767,7 @@ pub fn walk_pat_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v PatField<' pub fn walk_pat_expr<'v, V: Visitor<'v>>(visitor: &mut V, expr: &'v PatExpr<'v>) -> V::Result { try_visit!(visitor.visit_id(expr.hir_id)); match &expr.kind { - PatExprKind::Lit { .. } => V::Result::output(), + PatExprKind::Lit { lit, negated } => visitor.visit_lit(expr.hir_id, lit, *negated), PatExprKind::ConstBlock(c) => visitor.visit_inline_const(c), PatExprKind::Path(qpath) => visitor.visit_qpath(qpath, expr.hir_id, expr.span), } @@ -912,7 +915,8 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) try_visit!(visitor.visit_expr(expr)); visit_opt!(visitor, visit_ty_unambig, ty); } - ExprKind::Lit(_) | ExprKind::Err(_) => {} + ExprKind::Lit(lit) => try_visit!(visitor.visit_lit(expression.hir_id, lit, false)), + ExprKind::Err(_) => {} } V::Result::output() } diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 705c167e258c..270d4fbec30f 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -5,6 +5,7 @@ // tidy-alphabetical-start #![allow(internal_features)] #![feature(associated_type_defaults)] +#![feature(box_patterns)] #![feature(closure_track_caller)] #![feature(debug_closure_helpers)] #![feature(exhaustive_patterns)] diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 6c534d58a7da..9ed56d7bde51 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -19,7 +19,7 @@ use rustc_middle::span_bug; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::fold::{BottomUpFolder, fold_regions}; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; -use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt}; +use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{ AdtDef, GenericArgKind, RegionKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; @@ -257,30 +257,9 @@ pub(super) fn check_opaque_for_cycles<'tcx>( // First, try to look at any opaque expansion cycles, considering coroutine fields // (even though these aren't necessarily true errors). - if tcx - .try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::Yes) - .is_err() - { - // Look for true opaque expansion cycles, but ignore coroutines. - // This will give us any true errors. Coroutines are only problematic - // if they cause layout computation errors. - if tcx - .try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::No) - .is_err() - { - let reported = opaque_type_cycle_error(tcx, def_id); - return Err(reported); - } - - // And also look for cycle errors in the layout of coroutines. - if let Err(&LayoutError::Cycle(guar)) = - tcx.layout_of( - ty::TypingEnv::post_analysis(tcx, def_id.to_def_id()) - .as_query_input(Ty::new_opaque(tcx, def_id.to_def_id(), args)), - ) - { - return Err(guar); - } + if tcx.try_expand_impl_trait_type(def_id.to_def_id(), args).is_err() { + let reported = opaque_type_cycle_error(tcx, def_id); + return Err(reported); } Ok(()) diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index 4a508fc0cf6a..41f8465ae91f 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -11,6 +11,15 @@ pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) { } for id in tcx.hir_crate_items(()).opaques() { + if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. } = + tcx.hir().expect_opaque_ty(id).origin + && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id) + && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn() + { + continue; + } + let ty = tcx.type_of(id).instantiate_identity(); let span = tcx.def_span(id); tcx.dcx().emit_err(crate::errors::TypeOf { span, ty }); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 3b163d0bc205..8efef0c612b0 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -321,6 +321,10 @@ pub fn pat_to_string(ann: &dyn PpAnn, pat: &hir::Pat<'_>) -> String { to_string(ann, |s| s.print_pat(pat)) } +pub fn expr_to_string(ann: &dyn PpAnn, pat: &hir::Expr<'_>) -> String { + to_string(ann, |s| s.print_expr(pat)) +} + impl<'a> State<'a> { fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) { self.maybe_print_comment(span.hi()); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index e90474cabb42..5ce9e0556b4c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1,4 +1,4 @@ -use std::{iter, mem}; +use std::{fmt, iter, mem}; use itertools::Itertools; use rustc_data_structures::fx::FxIndexSet; @@ -13,7 +13,7 @@ use rustc_hir::{ExprKind, HirId, Node, QPath}; use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; -use rustc_index::{Idx, IndexVec}; +use rustc_index::IndexVec; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TypeTrace}; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::TypeError; @@ -25,6 +25,7 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext}; +use smallvec::SmallVec; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; @@ -44,6 +45,12 @@ use crate::{ struct_span_code_err, }; +rustc_index::newtype_index! { + #[orderable] + #[debug_format = "GenericIdx({})"] + pub(crate) struct GenericIdx {} +} + #[derive(Clone, Copy, Default)] pub(crate) enum DivergingBlockBehavior { /// This is the current stable behavior: @@ -1619,6 +1626,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { ty::Int(_) | ty::Uint(_) => Some(ty), + // These exist to direct casts like `0x61 as char` to use + // the right integer type to cast from, instead of falling back to + // i32 due to no further constraints. ty::Char => Some(tcx.types.u8), ty::RawPtr(..) => Some(tcx.types.usize), ty::FnDef(..) | ty::FnPtr(..) => Some(tcx.types.usize), @@ -2288,7 +2298,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we're calling a method of a Fn/FnMut/FnOnce trait object implicitly // (eg invoking a closure) we want to point at the underlying callable, // not the method implicitly invoked (eg call_once). - if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) + // TupleArguments is set only when this is an implicit call (my_closure(...)) rather than explicit (my_closure.call(...)) + if tuple_arguments == TupleArguments + && let Some(assoc_item) = self.tcx.opt_associated_item(def_id) // Since this is an associated item, it might point at either an impl or a trait item. // We want it to always point to the trait item. // If we're pointing at an inherent function, we don't need to do anything, @@ -2298,8 +2310,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" && let Some(call_kind) = self.tcx.fn_trait_kind_from_def_id(maybe_trait_def_id) && let Some(callee_ty) = callee_ty - // TupleArguments is set only when this is an implicit call (my_closure(...)) rather than explicit (my_closure.call(...)) - && tuple_arguments == TupleArguments { let callee_ty = callee_ty.peel_refs(); match *callee_ty.kind() { @@ -2368,174 +2378,136 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && !def_span.is_dummy() { let mut spans: MultiSpan = def_span.into(); - - if let Some(params_with_generics) = self.get_hir_params_with_generics(def_id, is_method) + if let Some((params_with_generics, hir_generics)) = + self.get_hir_param_info(def_id, is_method) { + struct MismatchedParam<'a> { + idx: ExpectedIdx, + generic: GenericIdx, + param: &'a FnParam<'a>, + deps: SmallVec<[ExpectedIdx; 4]>, + } + debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); - - let mut generics_with_unmatched_params = Vec::new(); - - let check_for_matched_generics = || { - if matched_inputs.iter().any(|x| x.is_some()) - && params_with_generics.iter().any(|(x, _)| x.is_some()) - { - for (idx, (generic, _)) in params_with_generics.iter_enumerated() { - // Param has to have a generic and be matched to be relevant - if matched_inputs[idx].is_none() { - continue; - } - - let Some(generic) = generic else { - continue; - }; - - for unmatching_idx in - idx.plus(1)..ExpectedIdx::from_usize(params_with_generics.len()) - { - if matched_inputs[unmatching_idx].is_none() - && let Some(unmatched_idx_param_generic) = - params_with_generics[unmatching_idx].0 - && unmatched_idx_param_generic.name.ident() - == generic.name.ident() - { - // We found a parameter that didn't match that needed to - return true; + // Gather all mismatched parameters with generics. + let mut mismatched_params = Vec::>::new(); + if let Some(expected_idx) = expected_idx { + let expected_idx = ExpectedIdx::from_usize(expected_idx); + let &(expected_generic, ref expected_param) = + ¶ms_with_generics[expected_idx]; + if let Some(expected_generic) = expected_generic { + mismatched_params.push(MismatchedParam { + idx: expected_idx, + generic: expected_generic, + param: expected_param, + deps: SmallVec::new(), + }); + } else { + // Still mark the mismatched parameter + spans.push_span_label(expected_param.span(), ""); + } + } else { + mismatched_params.extend( + params_with_generics.iter_enumerated().zip(matched_inputs).filter_map( + |((idx, &(generic, ref param)), matched_idx)| { + if matched_idx.is_some() { + None + } else if let Some(generic) = generic { + Some(MismatchedParam { + idx, + generic, + param, + deps: SmallVec::new(), + }) + } else { + // Still mark mismatched parameters + spans.push_span_label(param.span(), ""); + None } + }, + ), + ); + } + + if !mismatched_params.is_empty() { + // For each mismatched paramter, create a two-way link to each matched parameter + // of the same type. + let mut dependants = IndexVec::::from_fn_n( + |_| SmallVec::<[u32; 4]>::new(), + params_with_generics.len(), + ); + let mut generic_uses = IndexVec::::from_fn_n( + |_| SmallVec::<[ExpectedIdx; 4]>::new(), + hir_generics.params.len(), + ); + for (idx, param) in mismatched_params.iter_mut().enumerate() { + for ((other_idx, &(other_generic, _)), &other_matched_idx) in + params_with_generics.iter_enumerated().zip(matched_inputs) + { + if other_generic == Some(param.generic) && other_matched_idx.is_some() { + generic_uses[param.generic].extend([param.idx, other_idx]); + dependants[other_idx].push(idx as u32); + param.deps.push(other_idx); } } } - false - }; - let check_for_matched_generics = check_for_matched_generics(); - - for (idx, &(generic_param, param)) in - params_with_generics.iter_enumerated().filter(|&(idx, _)| { - check_for_matched_generics - || expected_idx - .is_none_or(|expected_idx| expected_idx == idx.as_usize()) - }) - { - let Some(generic_param) = generic_param else { - spans.push_span_label(param.span(), ""); - continue; - }; - - let other_params_matched: Vec<(ExpectedIdx, FnParam<'_>)> = - params_with_generics - .iter_enumerated() - .filter(|&(other_idx, &(other_generic_param, _))| { - if other_idx == idx { - return false; - } - let Some(other_generic_param) = other_generic_param else { - return false; - }; - if matched_inputs[idx].is_none() - && matched_inputs[other_idx].is_none() - { - return false; - } - if matched_inputs[idx].is_some() - && matched_inputs[other_idx].is_some() - { - return false; - } - other_generic_param.name.ident() == generic_param.name.ident() - }) - .map(|(other_idx, &(_, other_param))| (other_idx, other_param)) - .collect(); - - if !other_params_matched.is_empty() { - let other_param_matched_names: Vec = other_params_matched - .iter() - .map(|(idx, other_param)| { - if let Some(name) = other_param.name() { - format!("`{name}`") - } else { - format!("parameter #{}", idx.as_u32() + 1) - } - }) - .collect(); - - let matched_ty = self - .resolve_vars_if_possible(formal_and_expected_inputs[idx].1) - .sort_string(self.tcx); - - if matched_inputs[idx].is_some() { + // Highlight each mismatched type along with a note about which other parameters + // the type depends on (if any). + for param in &mismatched_params { + if let Some(deps_list) = listify(¶m.deps, |&dep| { + params_with_generics[dep].1.display(dep.as_usize()).to_string() + }) { spans.push_span_label( - param.span(), + param.param.span(), format!( - "{} need{} to match the {} type of this parameter", - listify(&other_param_matched_names, |n| n.to_string()) - .unwrap_or_default(), - pluralize!(if other_param_matched_names.len() == 1 { - 0 - } else { - 1 - }), - matched_ty, + "this parameter needs to match the {} type of {deps_list}", + self.resolve_vars_if_possible( + formal_and_expected_inputs[param.deps[0]].1 + ) + .sort_string(self.tcx), ), ); } else { + // Still mark mismatched parameters + spans.push_span_label(param.param.span(), ""); + } + } + // Highligh each parameter being depended on for a generic type. + for ((&(_, param), deps), &(_, expected_ty)) in + params_with_generics.iter().zip(&dependants).zip(formal_and_expected_inputs) + { + if let Some(deps_list) = listify(deps, |&dep| { + let param = &mismatched_params[dep as usize]; + param.param.display(param.idx.as_usize()).to_string() + }) { spans.push_span_label( param.span(), format!( - "this parameter needs to match the {} type of {}", - matched_ty, - listify(&other_param_matched_names, |n| n.to_string()) - .unwrap_or_default(), + "{deps_list} need{} to match the {} type of this parameter", + pluralize!((deps.len() != 1) as u32), + self.resolve_vars_if_possible(expected_ty) + .sort_string(self.tcx), ), ); } - generics_with_unmatched_params.push(generic_param); - } else { - spans.push_span_label(param.span(), ""); } - } - - for generic_param in self - .tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.generics()) - .into_iter() - .flat_map(|x| x.params) - .filter(|x| { - generics_with_unmatched_params - .iter() - .any(|y| x.name.ident() == y.name.ident()) - }) - { - let param_idents_matching: Vec = params_with_generics - .iter_enumerated() - .filter(|&(_, &(generic, _))| { - if let Some(generic) = generic { - generic.name.ident() == generic_param.name.ident() - } else { - false - } - }) - .map(|(idx, &(_, param))| { - if let Some(name) = param.name() { - format!("`{name}`") - } else { - format!("parameter #{}", idx.as_u32() + 1) - } - }) - .collect(); - - if !param_idents_matching.is_empty() { - spans.push_span_label( - generic_param.span, - format!( - "{} {} reference this parameter `{}`", - listify(¶m_idents_matching, |n| n.to_string()) - .unwrap_or_default(), - if param_idents_matching.len() == 2 { "both" } else { "all" }, - generic_param.name.ident().name, - ), - ); + // Highlight each generic parameter in use. + for (param, uses) in hir_generics.params.iter().zip(&mut generic_uses) { + uses.sort(); + uses.dedup(); + if let Some(param_list) = listify(uses, |&idx| { + params_with_generics[idx].1.display(idx.as_usize()).to_string() + }) { + spans.push_span_label( + param.span, + format!( + "{param_list} {} reference this parameter `{}`", + if uses.len() == 2 { "both" } else { "all" }, + param.name.ident().name, + ), + ); + } } } } @@ -2611,7 +2583,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; }; - if let Some(params_with_generics) = self.get_hir_params_with_generics(def_id, is_method) { + if let Some((params_with_generics, _)) = self.get_hir_param_info(def_id, is_method) { debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); for (idx, (generic_param, _)) in params_with_generics.iter_enumerated() { if matched_inputs[idx].is_none() { @@ -2639,7 +2611,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if matched_inputs[other_idx].is_some() { return false; } - other_generic_param.name.ident() == generic_param.name.ident() + other_generic_param == generic_param }) .count(); @@ -2671,11 +2643,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns the parameters of a function, with their generic parameters if those are the full /// type of that parameter. Returns `None` if the function has no generics or the body is /// unavailable (eg is an instrinsic). - fn get_hir_params_with_generics( + fn get_hir_param_info( &self, def_id: DefId, is_method: bool, - ) -> Option>, FnParam<'_>)>> { + ) -> Option<(IndexVec, FnParam<'_>)>, &hir::Generics<'_>)> + { let (sig, generics, body_id, param_names) = match self.tcx.hir().get_if_local(def_id)? { hir::Node::TraitItem(&hir::TraitItem { generics, @@ -2705,7 +2678,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &hir::Path { res: Res::Def(_, res_def_id), .. }, )) = param.kind { - generics.params.iter().find(|param| param.def_id.to_def_id() == res_def_id) + generics + .params + .iter() + .position(|param| param.def_id.to_def_id() == res_def_id) + .map(GenericIdx::from_usize) } else { None } @@ -2717,12 +2694,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let params = params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?; debug_assert_eq!(params.len(), fn_inputs.len()); - Some(fn_inputs.zip(params.iter().map(|param| FnParam::Param(param))).collect()) + Some(( + fn_inputs.zip(params.iter().map(|param| FnParam::Param(param))).collect(), + generics, + )) } (None, Some(params)) => { let params = params.get(is_method as usize..)?; debug_assert_eq!(params.len(), fn_inputs.len()); - Some(fn_inputs.zip(params.iter().map(|param| FnParam::Name(param))).collect()) + Some(( + fn_inputs.zip(params.iter().map(|param| FnParam::Name(param))).collect(), + generics, + )) } } } @@ -2770,4 +2753,18 @@ impl FnParam<'_> { _ => None, } } + + fn display(&self, idx: usize) -> impl '_ + fmt::Display { + struct D<'a>(FnParam<'a>, usize); + impl fmt::Display for D<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(name) = self.0.name() { + write!(f, "`{name}`") + } else { + write!(f, "parameter #{}", self.1 + 1) + } + } + } + D(*self, idx) + } } diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 805079afdb00..47c1f7844484 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -306,6 +306,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span); let missing_trait = trait_def_id .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id))); + let mut path = None; + let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path); + let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path); let (mut err, output_def_id) = match is_assign { IsAssign::Yes => { let mut err = struct_span_code_err!( @@ -314,11 +317,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { E0368, "binary assignment operation `{}=` cannot be applied to type `{}`", op.node.as_str(), - lhs_ty, + lhs_ty_str, ); err.span_label( lhs_expr.span, - format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty), + format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty_str), ); self.note_unmet_impls_on_type(&mut err, errors, false); (err, None) @@ -326,41 +329,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsAssign::No => { let message = match op.node { hir::BinOpKind::Add => { - format!("cannot add `{rhs_ty}` to `{lhs_ty}`") + format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`") } hir::BinOpKind::Sub => { - format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`") + format!("cannot subtract `{rhs_ty_str}` from `{lhs_ty_str}`") } hir::BinOpKind::Mul => { - format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`") + format!("cannot multiply `{lhs_ty_str}` by `{rhs_ty_str}`") } hir::BinOpKind::Div => { - format!("cannot divide `{lhs_ty}` by `{rhs_ty}`") + format!("cannot divide `{lhs_ty_str}` by `{rhs_ty_str}`") } hir::BinOpKind::Rem => { format!( - "cannot calculate the remainder of `{lhs_ty}` divided by `{rhs_ty}`" + "cannot calculate the remainder of `{lhs_ty_str}` divided by \ + `{rhs_ty_str}`" ) } hir::BinOpKind::BitAnd => { - format!("no implementation for `{lhs_ty} & {rhs_ty}`") + format!("no implementation for `{lhs_ty_str} & {rhs_ty_str}`") } hir::BinOpKind::BitXor => { - format!("no implementation for `{lhs_ty} ^ {rhs_ty}`") + format!("no implementation for `{lhs_ty_str} ^ {rhs_ty_str}`") } hir::BinOpKind::BitOr => { - format!("no implementation for `{lhs_ty} | {rhs_ty}`") + format!("no implementation for `{lhs_ty_str} | {rhs_ty_str}`") } hir::BinOpKind::Shl => { - format!("no implementation for `{lhs_ty} << {rhs_ty}`") + format!("no implementation for `{lhs_ty_str} << {rhs_ty_str}`") } hir::BinOpKind::Shr => { - format!("no implementation for `{lhs_ty} >> {rhs_ty}`") + format!("no implementation for `{lhs_ty_str} >> {rhs_ty_str}`") } _ => format!( "binary operation `{}` cannot be applied to type `{}`", op.node.as_str(), - lhs_ty + lhs_ty_str, ), }; let output_def_id = trait_def_id.and_then(|def_id| { @@ -375,14 +379,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = struct_span_code_err!(self.dcx(), op.span, E0369, "{message}"); if !lhs_expr.span.eq(&rhs_expr.span) { - err.span_label(lhs_expr.span, lhs_ty.to_string()); - err.span_label(rhs_expr.span, rhs_ty.to_string()); + err.span_label(lhs_expr.span, lhs_ty_str.clone()); + err.span_label(rhs_expr.span, rhs_ty_str); } let suggest_derive = self.can_eq(self.param_env, lhs_ty, rhs_ty); self.note_unmet_impls_on_type(&mut err, errors, suggest_derive); (err, output_def_id) } }; + *err.long_ty_path() = path; // Try to suggest a semicolon if it's `A \n *B` where `B` is a place expr let maybe_missing_semi = self.check_for_missing_semi(expr, &mut err); @@ -417,7 +422,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsAssign::Yes => "=", IsAssign::No => "", }, - lhs_deref_ty, + self.tcx.short_string(lhs_deref_ty, err.long_ty_path()), ); err.span_suggestion_verbose( lhs_expr.span.shrink_to_lo(), @@ -443,8 +448,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .is_ok() { - let op_str = op.node.as_str(); - err.note(format!("an implementation for `{lhs_adjusted_ty} {op_str} {rhs_adjusted_ty}` exists")); + let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path()); + let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path()); + let op = op.node.as_str(); + err.note(format!("an implementation for `{lhs} {op} {rhs}` exists")); if let Some(lhs_new_mutbl) = lhs_new_mutbl && let Some(rhs_new_mutbl) = rhs_new_mutbl @@ -628,7 +635,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // When we know that a missing bound is responsible, we don't show // this note as it is redundant. err.note(format!( - "the trait `{missing_trait}` is not implemented for `{lhs_ty}`" + "the trait `{missing_trait}` is not implemented for `{lhs_ty_str}`" )); } } @@ -654,24 +661,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::BinOpKind::Sub => { if lhs_ty.is_unsafe_ptr() && rhs_ty.is_integral() { err.multipart_suggestion( - "consider using `wrapping_sub` or `sub` for pointer - {integer}", + "consider using `wrapping_sub` or `sub` for \ + pointer - {integer}", vec![ - (lhs_expr.span.between(rhs_expr.span), ".wrapping_sub(".to_owned()), + ( + lhs_expr.span.between(rhs_expr.span), + ".wrapping_sub(".to_owned(), + ), (rhs_expr.span.shrink_to_hi(), ")".to_owned()), ], - Applicability::MaybeIncorrect + Applicability::MaybeIncorrect, ); } if lhs_ty.is_unsafe_ptr() && rhs_ty.is_unsafe_ptr() { err.multipart_suggestion( - "consider using `offset_from` for pointer - pointer if the pointers point to the same allocation", + "consider using `offset_from` for pointer - pointer if the \ + pointers point to the same allocation", vec![ (lhs_expr.span.shrink_to_lo(), "unsafe { ".to_owned()), - (lhs_expr.span.between(rhs_expr.span), ".offset_from(".to_owned()), + ( + lhs_expr.span.between(rhs_expr.span), + ".offset_from(".to_owned(), + ), (rhs_expr.span.shrink_to_hi(), ") }".to_owned()), ], - Applicability::MaybeIncorrect + Applicability::MaybeIncorrect, ); } } @@ -793,14 +808,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(errors) => { let actual = self.resolve_vars_if_possible(operand_ty); let guar = actual.error_reported().err().unwrap_or_else(|| { + let mut file = None; + let ty_str = self.tcx.short_string(actual, &mut file); let mut err = struct_span_code_err!( self.dcx(), ex.span, E0600, - "cannot apply unary operator `{}` to type `{}`", + "cannot apply unary operator `{}` to type `{ty_str}`", op.as_str(), - actual ); + *err.long_ty_path() = file; err.span_label( ex.span, format!("cannot apply unary operator `{}`", op.as_str()), diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index ad15b764bcc3..2cd67cc4da21 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -137,6 +137,7 @@ impl<'a, 'tcx> At<'a, 'tcx> { expected, ty::Contravariant, actual, + self.cause.span, ) .map(|goals| self.goals_to_obligations(goals)) } else { @@ -163,8 +164,15 @@ impl<'a, 'tcx> At<'a, 'tcx> { T: ToTrace<'tcx>, { if self.infcx.next_trait_solver { - NextSolverRelate::relate(self.infcx, self.param_env, expected, ty::Covariant, actual) - .map(|goals| self.goals_to_obligations(goals)) + NextSolverRelate::relate( + self.infcx, + self.param_env, + expected, + ty::Covariant, + actual, + self.cause.span, + ) + .map(|goals| self.goals_to_obligations(goals)) } else { let mut op = TypeRelating::new( self.infcx, @@ -208,8 +216,15 @@ impl<'a, 'tcx> At<'a, 'tcx> { T: Relate>, { if self.infcx.next_trait_solver { - NextSolverRelate::relate(self.infcx, self.param_env, expected, ty::Invariant, actual) - .map(|goals| self.goals_to_obligations(goals)) + NextSolverRelate::relate( + self.infcx, + self.param_env, + expected, + ty::Invariant, + actual, + self.cause.span, + ) + .map(|goals| self.goals_to_obligations(goals)) } else { let mut op = TypeRelating::new( self.infcx, diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 69ab0e69e214..eae69ec3e0fe 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::relate::combine::PredicateEmittingRelation; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::{DUMMY_SP, ErrorGuaranteed}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use super::{BoundRegionConversionTime, InferCtxt, RegionVariableOrigin, SubregionOrigin}; @@ -203,23 +203,23 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.probe(|_| probe()) } - fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>) { + fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>, span: Span) { self.inner.borrow_mut().unwrap_region_constraints().make_subregion( - SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None), + SubregionOrigin::RelateRegionParamBound(span, None), sub, sup, ); } - fn equate_regions(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>) { + fn equate_regions(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>, span: Span) { self.inner.borrow_mut().unwrap_region_constraints().make_eqregion( - SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None), + SubregionOrigin::RelateRegionParamBound(span, None), a, b, ); } - fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>) { - self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy()); + fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>, span: Span) { + self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(span)); } } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index d9803236f85c..b35703d8e737 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -1,12 +1,12 @@ use std::path::PathBuf; use std::result; +use std::sync::Arc; use rustc_ast::{LitKind, MetaItemKind, token}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::jobserver; use rustc_data_structures::stable_hasher::StableHasher; -use rustc_data_structures::sync::Lrc; use rustc_errors::registry::Registry; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; use rustc_lint::LintStore; @@ -442,7 +442,6 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se locale_resources.push(codegen_backend.locale_resource()); let mut sess = rustc_session::build_session( - early_dcx, config.opts, CompilerIO { input: config.input, @@ -490,7 +489,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se if let Some(register_lints) = config.register_lints.as_deref() { register_lints(&sess, &mut lint_store); } - sess.lint_store = Some(Lrc::new(lint_store)); + sess.lint_store = Some(Arc::new(lint_store)); util::check_abi_required_features(&sess); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 8a121c5a8655..eda9c4e03fec 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -9,7 +9,7 @@ use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::parallel; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, Lrc, OnceLock, WorkerLocal}; +use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, OnceLock, WorkerLocal}; use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_feature::Features; use rustc_fs_util::try_canonicalize; @@ -602,7 +602,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P fn resolver_for_lowering_raw<'tcx>( tcx: TyCtxt<'tcx>, (): (), -) -> (&'tcx Steal<(ty::ResolverAstLowering, Lrc)>, &'tcx ty::ResolverGlobalCtxt) { +) -> (&'tcx Steal<(ty::ResolverAstLowering, Arc)>, &'tcx ty::ResolverGlobalCtxt) { let arenas = Resolver::arenas(); let _ = tcx.registered_tools(()); // Uses `crate_for_resolver`. let (krate, pre_configured_attrs) = tcx.crate_for_resolver(()).steal(); @@ -624,7 +624,7 @@ fn resolver_for_lowering_raw<'tcx>( } = resolver.into_outputs(); let resolutions = tcx.arena.alloc(untracked_resolutions); - (tcx.arena.alloc(Steal::new((untracked_resolver_for_lowering, Lrc::new(krate)))), resolutions) + (tcx.arena.alloc(Steal::new((untracked_resolver_for_lowering, Arc::new(krate)))), resolutions) } pub fn write_dep_info(tcx: TyCtxt<'_>) { @@ -909,6 +909,11 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { tcx.ensure_ok().check_coroutine_obligations( tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(), ); + // Eagerly check the unsubstituted layout for cycles. + tcx.ensure_ok().layout_of( + ty::TypingEnv::post_analysis(tcx, def_id.to_def_id()) + .as_query_input(tcx.type_of(def_id).instantiate_identity()), + ); } }); }); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index fb69dd548119..46d6f37a91cb 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -65,7 +65,6 @@ where static USING_INTERNAL_FEATURES: AtomicBool = AtomicBool::new(false); let sess = build_session( - early_dcx, sessopts, io, None, diff --git a/compiler/rustc_lexer/Cargo.toml b/compiler/rustc_lexer/Cargo.toml index 84b9e2922955..4b3492fdeda2 100644 --- a/compiler/rustc_lexer/Cargo.toml +++ b/compiler/rustc_lexer/Cargo.toml @@ -14,6 +14,7 @@ Rust lexer used by rustc. No stability guarantees are provided. # Note that this crate purposefully does not depend on other rustc crates [dependencies] +memchr = "2.7.4" unicode-xid = "0.2.0" [dependencies.unicode-properties] diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index d173c3ac0327..e0e3bd0e30b1 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -103,4 +103,11 @@ impl<'a> Cursor<'a> { self.bump(); } } + + pub(crate) fn eat_until(&mut self, byte: u8) { + self.chars = match memchr::memchr(byte, self.as_str().as_bytes()) { + Some(index) => self.as_str()[index..].chars(), + None => "".chars(), + } + } } diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index aa4abf678b9f..c63ab77decac 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -483,7 +483,7 @@ impl Cursor<'_> { _ => None, }; - self.eat_while(|c| c != '\n'); + self.eat_until(b'\n'); LineComment { doc_style } } @@ -888,7 +888,7 @@ impl Cursor<'_> { // Skip the string contents and on each '#' character met, check if this is // a raw string termination. loop { - self.eat_while(|c| c != '"'); + self.eat_until(b'"'); if self.is_eof() { return Err(RawStrError::NoTerminator { diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 826ecc22c24b..3ee908ba9bf4 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -152,6 +152,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas hir_visit::walk_pat(self, p); } + fn visit_lit(&mut self, hir_id: HirId, lit: &'tcx hir::Lit, negated: bool) { + lint_callback!(self, check_lit, hir_id, lit, negated); + } + fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) { self.with_lint_attrs(field.hir_id, |cx| hir_visit::walk_expr_field(cx, field)) } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 8cc8f911d3a6..77bd13aacf73 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -23,6 +23,7 @@ macro_rules! late_lint_methods { fn check_stmt(a: &'tcx rustc_hir::Stmt<'tcx>); fn check_arm(a: &'tcx rustc_hir::Arm<'tcx>); fn check_pat(a: &'tcx rustc_hir::Pat<'tcx>); + fn check_lit(hir_id: rustc_hir::HirId, a: &'tcx rustc_hir::Lit, negated: bool); fn check_expr(a: &'tcx rustc_hir::Expr<'tcx>); fn check_expr_post(a: &'tcx rustc_hir::Expr<'tcx>); fn check_ty(a: &'tcx rustc_hir::Ty<'tcx, rustc_hir::AmbigArg>); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 0060f33888eb..80007f34db35 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -5,7 +5,7 @@ use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, VariantIdx, Variants, Wrapp use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; use rustc_hir::intravisit::VisitorExt; -use rustc_hir::{AmbigArg, Expr, ExprKind, LangItem}; +use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; use rustc_middle::ty::{ @@ -536,6 +536,16 @@ fn lint_fn_pointer<'tcx>( } impl<'tcx> LateLintPass<'tcx> for TypeLimits { + fn check_lit( + &mut self, + cx: &LateContext<'tcx>, + hir_id: HirId, + lit: &'tcx hir::Lit, + negated: bool, + ) { + lint_literal(cx, self, hir_id, lit.span, lit, negated) + } + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) { match e.kind { hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { @@ -557,7 +567,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } } } - hir::ExprKind::Lit(lit) => lint_literal(cx, self, e, lit), hir::ExprKind::Call(path, [l, r]) if let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index 4b5163522f86..71e6e229907a 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -1,8 +1,10 @@ use hir::{ExprKind, Node, is_range_literal}; use rustc_abi::{Integer, Size}; +use rustc_hir::HirId; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::{bug, ty}; +use rustc_span::Span; use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; use crate::LateContext; @@ -21,21 +23,22 @@ fn lint_overflowing_range_endpoint<'tcx>( lit: &hir::Lit, lit_val: u128, max: u128, - expr: &'tcx hir::Expr<'tcx>, + hir_id: HirId, + lit_span: Span, ty: &str, ) -> bool { // Look past casts to support cases like `0..256 as u8` - let (expr, lit_span) = if let Node::Expr(par_expr) = cx.tcx.parent_hir_node(expr.hir_id) + let (hir_id, span) = if let Node::Expr(par_expr) = cx.tcx.parent_hir_node(hir_id) && let ExprKind::Cast(_, _) = par_expr.kind { - (par_expr, expr.span) + (par_expr.hir_id, par_expr.span) } else { - (expr, expr.span) + (hir_id, lit_span) }; // We only want to handle exclusive (`..`) ranges, // which are represented as `ExprKind::Struct`. - let Node::ExprField(field) = cx.tcx.parent_hir_node(expr.hir_id) else { return false }; + let Node::ExprField(field) = cx.tcx.parent_hir_node(hir_id) else { return false }; let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false }; if !is_range_literal(struct_expr) { return false; @@ -45,7 +48,7 @@ fn lint_overflowing_range_endpoint<'tcx>( // We can suggest using an inclusive range // (`..=`) instead only if it is the `end` that is // overflowing and only by 1. - if !(end.expr.hir_id == expr.hir_id && lit_val - 1 == max) { + if !(end.expr.hir_id == hir_id && lit_val - 1 == max) { return false; }; @@ -57,7 +60,7 @@ fn lint_overflowing_range_endpoint<'tcx>( _ => bug!(), }; - let sub_sugg = if expr.span.lo() == lit_span.lo() { + let sub_sugg = if span.lo() == lit_span.lo() { let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false }; UseInclusiveRange::WithoutParen { sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()), @@ -67,7 +70,7 @@ fn lint_overflowing_range_endpoint<'tcx>( } } else { UseInclusiveRange::WithParen { - eq_sugg: expr.span.shrink_to_lo(), + eq_sugg: span.shrink_to_lo(), lit_sugg: lit_span, literal: lit_val - 1, suffix, @@ -125,7 +128,8 @@ fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option { fn report_bin_hex_error( cx: &LateContext<'_>, - expr: &hir::Expr<'_>, + hir_id: HirId, + span: Span, ty: attr::IntType, size: Size, repr_str: String, @@ -144,11 +148,11 @@ fn report_bin_hex_error( }; let sign = if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive }; - let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map( + let sub = get_type_suggestion(cx.typeck_results().node_type(hir_id), val, negative).map( |suggestion_ty| { if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { let (sans_suffix, _) = repr_str.split_at(pos); - OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix } + OverflowingBinHexSub::Suggestion { span, suggestion_ty, sans_suffix } } else { OverflowingBinHexSub::Help { suggestion_ty } } @@ -156,7 +160,7 @@ fn report_bin_hex_error( ); let sign_bit_sub = (!negative) .then(|| { - let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else { + let ty::Int(int_ty) = cx.typeck_results().node_type(hir_id).kind() else { return None; }; @@ -177,7 +181,7 @@ fn report_bin_hex_error( }; Some(OverflowingBinHexSignBitSub { - span: expr.span, + span, lit_no_suffix, negative_val: actually.clone(), int_ty: int_ty.name_str(), @@ -186,7 +190,7 @@ fn report_bin_hex_error( }) .flatten(); - cx.emit_span_lint(OVERFLOWING_LITERALS, expr.span, OverflowingBinHex { + cx.emit_span_lint(OVERFLOWING_LITERALS, span, OverflowingBinHex { ty: t, lit: repr_str.clone(), dec: val, @@ -236,15 +240,17 @@ fn literal_to_i128(val: u128, negative: bool) -> Option { fn lint_int_literal<'tcx>( cx: &LateContext<'tcx>, type_limits: &TypeLimits, - e: &'tcx hir::Expr<'tcx>, + hir_id: HirId, + span: Span, lit: &hir::Lit, t: ty::IntTy, v: u128, + negated: bool, ) { let int_type = t.normalize(cx.sess().target.pointer_width); let (min, max) = int_ty_range(int_type); let max = max as u128; - let negative = type_limits.negated_expr_id == Some(e.hir_id); + let negative = negated ^ (type_limits.negated_expr_id == Some(hir_id)); // Detect literal value out of range [min, max] inclusive // avoiding use of -min to prevent overflow/panic @@ -252,7 +258,8 @@ fn lint_int_literal<'tcx>( if let Some(repr_str) = get_bin_hex_repr(cx, lit) { report_bin_hex_error( cx, - e, + hir_id, + span, attr::IntType::SignedInt(ty::ast_int_ty(t)), Integer::from_int_ty(cx, t).size(), repr_str, @@ -262,18 +269,18 @@ fn lint_int_literal<'tcx>( return; } - if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) { + if lint_overflowing_range_endpoint(cx, lit, v, max, hir_id, span, t.name_str()) { // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. return; } - let span = if negative { type_limits.negated_expr_span.unwrap() } else { e.span }; + let span = if negative { type_limits.negated_expr_span.unwrap() } else { span }; let lit = cx .sess() .source_map() .span_to_snippet(span) .unwrap_or_else(|_| if negative { format!("-{v}") } else { v.to_string() }); - let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) + let help = get_type_suggestion(cx.typeck_results().node_type(hir_id), v, negative) .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty }); cx.emit_span_lint(OVERFLOWING_LITERALS, span, OverflowingInt { @@ -288,7 +295,8 @@ fn lint_int_literal<'tcx>( fn lint_uint_literal<'tcx>( cx: &LateContext<'tcx>, - e: &'tcx hir::Expr<'tcx>, + hir_id: HirId, + span: Span, lit: &hir::Lit, t: ty::UintTy, ) { @@ -302,7 +310,7 @@ fn lint_uint_literal<'tcx>( }; if lit_val < min || lit_val > max { - if let Node::Expr(par_e) = cx.tcx.parent_hir_node(e.hir_id) { + if let Node::Expr(par_e) = cx.tcx.parent_hir_node(hir_id) { match par_e.kind { hir::ExprKind::Cast(..) => { if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { @@ -316,14 +324,15 @@ fn lint_uint_literal<'tcx>( _ => {} } } - if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) { + if lint_overflowing_range_endpoint(cx, lit, lit_val, max, hir_id, span, t.name_str()) { // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. return; } if let Some(repr_str) = get_bin_hex_repr(cx, lit) { report_bin_hex_error( cx, - e, + hir_id, + span, attr::IntType::UnsignedInt(ty::ast_uint_ty(t)), Integer::from_uint_ty(cx, t).size(), repr_str, @@ -332,7 +341,7 @@ fn lint_uint_literal<'tcx>( ); return; } - cx.emit_span_lint(OVERFLOWING_LITERALS, e.span, OverflowingUInt { + cx.emit_span_lint(OVERFLOWING_LITERALS, span, OverflowingUInt { ty: t.name_str(), lit: cx .sess() @@ -348,19 +357,24 @@ fn lint_uint_literal<'tcx>( pub(crate) fn lint_literal<'tcx>( cx: &LateContext<'tcx>, type_limits: &TypeLimits, - e: &'tcx hir::Expr<'tcx>, + hir_id: HirId, + span: Span, lit: &hir::Lit, + negated: bool, ) { - match *cx.typeck_results().node_type(e.hir_id).kind() { + match *cx.typeck_results().node_type(hir_id).kind() { ty::Int(t) => { match lit.node { ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => { - lint_int_literal(cx, type_limits, e, lit, t, v.get()) + lint_int_literal(cx, type_limits, hir_id, span, lit, t, v.get(), negated) } _ => bug!(), }; } - ty::Uint(t) => lint_uint_literal(cx, e, lit, t), + ty::Uint(t) => { + assert!(!negated); + lint_uint_literal(cx, hir_id, span, lit, t) + } ty::Float(t) => { let (is_infinite, sym) = match lit.node { ast::LitKind::Float(v, _) => match t { @@ -374,7 +388,7 @@ pub(crate) fn lint_literal<'tcx>( _ => bug!(), }; if is_infinite == Ok(true) { - cx.emit_span_lint(OVERFLOWING_LITERALS, e.span, OverflowingLiteral { + cx.emit_span_lint(OVERFLOWING_LITERALS, span, OverflowingLiteral { ty: t.name_str(), lit: cx .sess() diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 2ae4a6a6066d..e8dcda875e67 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -2,6 +2,7 @@ use std::iter::TrustedLen; use std::path::Path; +use std::sync::Arc; use std::{io, iter, mem}; pub(super) use cstore_impl::provide; @@ -11,7 +12,7 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::owned_slice::OwnedSlice; -use rustc_data_structures::sync::{Lock, Lrc, OnceLock}; +use rustc_data_structures::sync::{Lock, OnceLock}; use rustc_data_structures::unhash::UnhashMap; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro}; @@ -117,7 +118,7 @@ pub(crate) struct CrateMetadata { /// How to link (or not link) this crate to the currently compiled crate. dep_kind: CrateDepKind, /// Filesystem location of this crate. - source: Lrc, + source: Arc, /// Whether or not this crate should be consider a private dependency. /// Used by the 'exported_private_dependencies' lint, and for determining /// whether to emit suggestions that reference this crate. @@ -149,7 +150,7 @@ struct ImportedSourceFile { /// The end of this SourceFile within the source_map of its original crate original_end_pos: rustc_span::BytePos, /// The imported SourceFile's representation within the local source_map - translated_source_file: Lrc, + translated_source_file: Arc, } pub(super) struct DecodeContext<'a, 'tcx> { @@ -1866,7 +1867,7 @@ impl CrateMetadata { cnum_map, dependencies, dep_kind, - source: Lrc::new(source), + source: Arc::new(source), private_dep, host_hash, used: false, diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 028ff56155f2..776b081a4630 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -1,8 +1,8 @@ use std::any::Any; use std::mem; +use std::sync::Arc; use rustc_attr_parsing::Deprecation; -use rustc_data_structures::sync::Lrc; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; @@ -409,7 +409,7 @@ provide! { tcx, def_id, other, cdata, matches!(cdata.extern_crate, Some(extern_crate) if !extern_crate.is_direct()) } - used_crate_source => { Lrc::clone(&cdata.source) } + used_crate_source => { Arc::clone(&cdata.source) } debugger_visualizers => { cdata.get_debugger_visualizers() } exported_symbols => { @@ -548,7 +548,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { visible_parent_map }, - dependency_formats: |tcx, ()| Lrc::new(crate::dependency_format::calculate(tcx)), + dependency_formats: |tcx, ()| Arc::new(crate::dependency_format::calculate(tcx)), has_global_allocator: |tcx, LocalCrate| CStore::from_tcx(tcx).has_global_allocator(), has_alloc_error_handler: |tcx, LocalCrate| CStore::from_tcx(tcx).has_alloc_error_handler(), postorder_cnums: |tcx, ()| { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index db41a33dcc52..81fb918c6040 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -3,11 +3,12 @@ use std::collections::hash_map::Entry; use std::fs::File; use std::io::{Read, Seek, Write}; use std::path::{Path, PathBuf}; +use std::sync::Arc; use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; -use rustc_data_structures::sync::{Lrc, join, par_for_each_in}; +use rustc_data_structures::sync::{join, par_for_each_in}; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_data_structures::thousands::format_with_underscores; use rustc_feature::Features; @@ -52,7 +53,7 @@ pub(super) struct EncodeContext<'a, 'tcx> { // This is used to speed up Span encoding. // The `usize` is an index into the `MonotonicVec` // that stores the `SourceFile` - source_file_cache: (Lrc, usize), + source_file_cache: (Arc, usize), // The indices (into the `SourceMap`'s `MonotonicVec`) // of all of the `SourceFiles` that we need to serialize. // When we serialize a `Span`, we insert the index of its @@ -278,7 +279,7 @@ impl<'a, 'tcx> Encodable> for SpanData { let source_map = s.tcx.sess.source_map(); let source_file_index = source_map.lookup_source_file_idx(self.lo); s.source_file_cache = - (Lrc::clone(&source_map.files()[source_file_index]), source_file_index); + (Arc::clone(&source_map.files()[source_file_index]), source_file_index); } let (ref source_file, source_file_index) = s.source_file_cache; debug_assert!(source_file.contains(self.lo)); @@ -2306,7 +2307,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) { encoder.emit_raw_bytes(&0u64.to_le_bytes()); let source_map_files = tcx.sess.source_map().files(); - let source_file_cache = (Lrc::clone(&source_map_files[0]), 0); + let source_file_cache = (Arc::clone(&source_map_files[0]), 0); let required_source_files = Some(FxIndexSet::default()); drop(source_map_files); diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index eaccd8c360eb..69b2f92f7166 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -31,7 +31,7 @@ macro_rules! arena_types { [decode] borrowck_result: rustc_middle::mir::BorrowCheckResult<'tcx>, [] resolver: rustc_data_structures::steal::Steal<( rustc_middle::ty::ResolverAstLowering, - rustc_data_structures::sync::Lrc, + std::sync::Arc, )>, [] crate_for_resolver: rustc_data_structures::steal::Steal<(rustc_ast::Crate, rustc_ast::AttrVec)>, [] resolutions: rustc_middle::ty::ResolverGlobalCtxt, diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 276a02b4e0fe..c5ce6efcb817 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -98,6 +98,10 @@ declare_hooks! { hook save_dep_graph() -> (); hook query_key_hash_verify_all() -> (); + + /// Ensure the given scalar is valid for the given type. + /// This checks non-recursive runtime validity. + hook validate_scalar_in_layout(scalar: crate::ty::ScalarInt, ty: Ty<'tcx>) -> bool; } #[cold] diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 0f408375e05f..3cd148cd4427 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -142,10 +142,6 @@ impl<'tcx, R> QueryResponse<'tcx, R> { pub type QueryOutlivesConstraint<'tcx> = (ty::OutlivesPredicate<'tcx, GenericArg<'tcx>>, ConstraintCategory<'tcx>); -TrivialTypeTraversalImpls! { - crate::infer::canonical::Certainty, -} - #[derive(Default)] pub struct CanonicalParamEnvCache<'tcx> { map: Lock< diff --git a/compiler/rustc_middle/src/middle/debugger_visualizer.rs b/compiler/rustc_middle/src/middle/debugger_visualizer.rs index c241c06fce44..5a811321f58b 100644 --- a/compiler/rustc_middle/src/middle/debugger_visualizer.rs +++ b/compiler/rustc_middle/src/middle/debugger_visualizer.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; +use std::sync::Arc; -use rustc_data_structures::sync::Lrc; use rustc_macros::{Decodable, Encodable, HashStable}; #[derive(HashStable)] @@ -15,7 +15,7 @@ pub enum DebuggerVisualizerType { #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)] pub struct DebuggerVisualizerFile { /// The complete debugger visualizer source. - pub src: Lrc<[u8]>, + pub src: Arc<[u8]>, /// Indicates which visualizer type this targets. pub visualizer_type: DebuggerVisualizerType, /// The file path to the visualizer file. This is used for reporting @@ -26,13 +26,13 @@ pub struct DebuggerVisualizerFile { } impl DebuggerVisualizerFile { - pub fn new(src: Lrc<[u8]>, visualizer_type: DebuggerVisualizerType, path: PathBuf) -> Self { + pub fn new(src: Arc<[u8]>, visualizer_type: DebuggerVisualizerType, path: PathBuf) -> Self { DebuggerVisualizerFile { src, visualizer_type, path: Some(path) } } pub fn path_erased(&self) -> Self { DebuggerVisualizerFile { - src: Lrc::clone(&self.src), + src: Arc::clone(&self.src), visualizer_type: self.visualizer_type, path: None, } diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index 3eb563d7d6e5..c32cf5f82533 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -163,6 +163,7 @@ impl<'tcx> graph::Predecessors for BasicBlocks<'tcx> { } } +// Done here instead of in `structural_impls.rs` because `Cache` is private, as is `basic_blocks`. TrivialTypeTraversalImpls! { Cache } impl Encodable for Cache { diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 8c085fa310ab..1222ba052cce 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -95,8 +95,6 @@ impl From for ErrorGuaranteed { } } -TrivialTypeTraversalImpls! { ErrorHandled } - pub type EvalToAllocationRawResult<'tcx> = Result, ErrorHandled>; pub type EvalStaticInitializerRawResult<'tcx> = Result, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index cfb78e5dddff..68d5e5f4dd29 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -34,7 +34,6 @@ use self::visit::TyContext; use crate::mir::interpret::{AllocRange, Scalar}; use crate::mir::visit::MirVisitable; use crate::ty::codec::{TyDecoder, TyEncoder}; -use crate::ty::fold::{FallibleTypeFolder, TypeFoldable}; use crate::ty::print::{FmtPrinter, Printer, pretty_print_const, with_no_trimmed_paths}; use crate::ty::visit::TypeVisitableExt; use crate::ty::{ @@ -59,7 +58,6 @@ pub mod tcx; mod terminator; pub mod traversal; -mod type_foldable; pub mod visit; pub use consts::*; @@ -927,8 +925,6 @@ pub enum BindingForm<'tcx> { RefForGuard, } -TrivialTypeTraversalImpls! { BindingForm<'tcx> } - mod binding_form_impl { use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_query_system::ich::StableHashingContext; diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs deleted file mode 100644 index 9893dd0484cb..000000000000 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! `TypeFoldable` implementations for MIR types - -use rustc_ast::InlineAsmTemplatePiece; -use rustc_hir::UnsafeBinderCastKind; -use rustc_hir::def_id::LocalDefId; - -use super::*; - -TrivialTypeTraversalImpls! { - BlockTailInfo, - MirPhase, - SourceInfo, - FakeReadCause, - RetagKind, - SourceScope, - SourceScopeLocalData, - UserTypeAnnotationIndex, - BorrowKind, - RawPtrKind, - CastKind, - BasicBlock, - SwitchTargets, - CoroutineKind, - CoroutineSavedLocal, - UnsafeBinderCastKind, -} - -TrivialTypeTraversalImpls! { - ConstValue<'tcx>, - NullOp<'tcx>, -} - -impl<'tcx> TypeFoldable> for &'tcx [InlineAsmTemplatePiece] { - fn try_fold_with>>( - self, - _folder: &mut F, - ) -> Result { - Ok(self) - } -} - -impl<'tcx> TypeFoldable> for &'tcx [Span] { - fn try_fold_with>>( - self, - _folder: &mut F, - ) -> Result { - Ok(self) - } -} - -impl<'tcx> TypeFoldable> for &'tcx ty::List { - fn try_fold_with>>( - self, - _folder: &mut F, - ) -> Result { - Ok(self) - } -} - -impl<'tcx> TypeFoldable> for &'tcx ty::List> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - ty::util::fold_list(self, folder, |tcx, v| tcx.mk_place_elems(v)) - } -} diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 6c442fc1047f..d94efe2d7d6f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -18,7 +18,6 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, DocLinkResMap}; @@ -125,7 +124,7 @@ rustc_queries! { desc { "getting the resolver outputs" } } - query resolver_for_lowering_raw(_: ()) -> (&'tcx Steal<(ty::ResolverAstLowering, Lrc)>, &'tcx ty::ResolverGlobalCtxt) { + query resolver_for_lowering_raw(_: ()) -> (&'tcx Steal<(ty::ResolverAstLowering, Arc)>, &'tcx ty::ResolverGlobalCtxt) { eval_always no_hash desc { "getting the resolver for lowering" } @@ -1628,7 +1627,7 @@ rustc_queries! { separate_provide_extern } - query dependency_formats(_: ()) -> &'tcx Lrc { + query dependency_formats(_: ()) -> &'tcx Arc { arena_cache desc { "getting the linkage format of all dependencies" } } @@ -2077,7 +2076,7 @@ rustc_queries! { desc { "seeing if we're missing an `extern crate` item for this crate" } separate_provide_extern } - query used_crate_source(_: CrateNum) -> &'tcx Lrc { + query used_crate_source(_: CrateNum) -> &'tcx Arc { arena_cache eval_always desc { "looking at the source for a crate" } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 4a144ebb8994..957b63584be5 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -1,9 +1,10 @@ use std::collections::hash_map::Entry; use std::mem; +use std::sync::Arc; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; -use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, RwLock}; +use rustc_data_structures::sync::{HashMapExt, Lock, RwLock}; use rustc_data_structures::unhash::UnhashMap; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE, LocalDefId, StableCrateId}; @@ -60,7 +61,7 @@ pub struct OnDiskCache { file_index_to_stable_id: FxHashMap, // Caches that are populated lazily during decoding. - file_index_to_file: Lock>>, + file_index_to_file: Lock>>, // A map from dep-node to the position of the cached query result in // `serialized_data`. @@ -453,7 +454,7 @@ impl OnDiskCache { pub struct CacheDecoder<'a, 'tcx> { tcx: TyCtxt<'tcx>, opaque: MemDecoder<'a>, - file_index_to_file: &'a Lock>>, + file_index_to_file: &'a Lock>>, file_index_to_stable_id: &'a FxHashMap, alloc_decoding_session: AllocDecodingSession<'a>, syntax_contexts: &'a FxHashMap, @@ -464,10 +465,10 @@ pub struct CacheDecoder<'a, 'tcx> { impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { #[inline] - fn file_index_to_file(&self, index: SourceFileIndex) -> Lrc { + fn file_index_to_file(&self, index: SourceFileIndex) -> Arc { let CacheDecoder { tcx, file_index_to_file, file_index_to_stable_id, .. } = *self; - Lrc::clone(file_index_to_file.borrow_mut().entry(index).or_insert_with(|| { + Arc::clone(file_index_to_file.borrow_mut().entry(index).or_insert_with(|| { let source_file_id = &file_index_to_stable_id[&index]; let source_file_cnum = tcx.stable_crate_id_to_crate_num(source_file_id.stable_crate_id); @@ -824,7 +825,7 @@ pub struct CacheEncoder<'a, 'tcx> { impl<'a, 'tcx> CacheEncoder<'a, 'tcx> { #[inline] - fn source_file_index(&mut self, source_file: Lrc) -> SourceFileIndex { + fn source_file_index(&mut self, source_file: Arc) -> SourceFileIndex { self.file_to_file_index[&(&raw const *source_file)] } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index e2e2029a362b..4dc8f2795535 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -11,6 +11,7 @@ use std::cmp::Ordering; use std::fmt; use std::ops::Index; +use std::sync::Arc; use rustc_abi::{FieldIdx, Integer, Size, VariantIdx}; use rustc_ast::{AsmMacro, InlineAsmOptions, InlineAsmTemplatePiece}; @@ -618,7 +619,7 @@ pub enum InlineAsmOperand<'tcx> { #[derive(Debug, HashStable, TypeVisitable)] pub struct FieldPat<'tcx> { pub field: FieldIdx, - pub pattern: Box>, + pub pattern: Pat<'tcx>, } #[derive(Debug, HashStable, TypeVisitable)] @@ -679,7 +680,7 @@ impl<'tcx> Pat<'tcx> { Or { pats } => pats.iter().for_each(|p| p.walk_(it)), Array { box ref prefix, ref slice, box ref suffix } | Slice { box ref prefix, ref slice, box ref suffix } => { - prefix.iter().chain(slice.iter()).chain(suffix.iter()).for_each(|p| p.walk_(it)) + prefix.iter().chain(slice.as_deref()).chain(suffix.iter()).for_each(|p| p.walk_(it)) } } } @@ -836,28 +837,28 @@ pub enum PatKind<'tcx> { subpattern: Box>, }, - Range(Box>), + Range(Arc>), /// Matches against a slice, checking the length and extracting elements. /// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty. /// e.g., `&[ref xs @ ..]`. Slice { - prefix: Box<[Box>]>, + prefix: Box<[Pat<'tcx>]>, slice: Option>>, - suffix: Box<[Box>]>, + suffix: Box<[Pat<'tcx>]>, }, /// Fixed match against an array; irrefutable. Array { - prefix: Box<[Box>]>, + prefix: Box<[Pat<'tcx>]>, slice: Option>>, - suffix: Box<[Box>]>, + suffix: Box<[Pat<'tcx>]>, }, /// An or-pattern, e.g. `p | q`. /// Invariant: `pats.len() >= 2`. Or { - pats: Box<[Box>]>, + pats: Box<[Pat<'tcx>]>, }, /// A never pattern `!`. diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index d0ce4e6242ea..f039da772fd4 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -10,8 +10,8 @@ mod structural_impls; use std::borrow::Cow; use std::hash::{Hash, Hasher}; +use std::sync::Arc; -use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diag, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::HirId; @@ -157,7 +157,7 @@ pub struct UnifyReceiverContext<'tcx> { pub struct InternedObligationCauseCode<'tcx> { /// `None` for `ObligationCauseCode::Misc` (a common case, occurs ~60% of /// the time). `Some` otherwise. - code: Option>>, + code: Option>>, } impl<'tcx> std::fmt::Debug for InternedObligationCauseCode<'tcx> { @@ -171,7 +171,7 @@ impl<'tcx> ObligationCauseCode<'tcx> { #[inline(always)] fn into(self) -> InternedObligationCauseCode<'tcx> { InternedObligationCauseCode { - code: if let ObligationCauseCode::Misc = self { None } else { Some(Lrc::new(self)) }, + code: if let ObligationCauseCode::Misc = self { None } else { Some(Arc::new(self)) }, } } } @@ -427,10 +427,6 @@ pub enum IsConstable { Ctor, } -TrivialTypeTraversalAndLiftImpls! { - IsConstable, -} - /// The 'location' at which we try to perform HIR-based wf checking. /// This information is used to obtain an `hir::Ty`, which /// we can walk in order to obtain precise spans for any diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 094fc62afbbf..b7cd545d02db 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -260,8 +260,6 @@ impl From for OverflowError { } } -TrivialTypeTraversalImpls! { OverflowError } - impl<'tcx> From for SelectionError<'tcx> { fn from(overflow_error: OverflowError) -> SelectionError<'tcx> { match overflow_error { diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 002d38196212..c9b9ec771b32 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -30,8 +30,6 @@ impl From for NotConstEvaluatable { } } -TrivialTypeTraversalImpls! { NotConstEvaluatable } - pub type BoundAbstractConst<'tcx> = Result>>, ErrorGuaranteed>; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 86248b495cdc..c6fc5f98f56f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -10,7 +10,7 @@ use std::cmp::Ordering; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::ops::{Bound, Deref}; -use std::sync::OnceLock; +use std::sync::{Arc, OnceLock}; use std::{fmt, iter, mem}; use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, VariantIdx}; @@ -24,7 +24,7 @@ use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{ - self, DynSend, DynSync, FreezeReadGuard, Lock, Lrc, RwLock, WorkerLocal, + self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal, }; use rustc_data_structures::unord::UnordSet; use rustc_errors::{ @@ -76,11 +76,11 @@ use crate::traits::solve::{ }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ - self, AdtDef, AdtDefData, AdtKind, Binder, BoundConstness, Clause, Clauses, Const, GenericArg, - GenericArgs, GenericArgsRef, GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, - ParamConst, ParamTy, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, - PredicateKind, PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, - TyKind, TyVid, Visibility, + self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs, + GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, ParamTy, + Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, + PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, + Visibility, }; #[allow(rustc::usage_of_ty_tykind)] @@ -1406,7 +1406,7 @@ impl<'tcx> GlobalCtxt<'tcx> { pub struct CurrentGcx { /// This stores a pointer to a `GlobalCtxt`. This is set to `Some` inside `GlobalCtxt::enter` /// and reset to `None` when that function returns or unwinds. - value: Lrc>>, + value: Arc>>, } unsafe impl DynSend for CurrentGcx {} @@ -1414,7 +1414,7 @@ unsafe impl DynSync for CurrentGcx {} impl CurrentGcx { pub fn new() -> Self { - Self { value: Lrc::new(RwLock::new(None)) } + Self { value: Arc::new(RwLock::new(None)) } } pub fn access(&self, f: impl for<'tcx> FnOnce(&'tcx GlobalCtxt<'tcx>) -> R) -> R { @@ -2243,21 +2243,23 @@ macro_rules! nop_list_lift { }; } -nop_lift! {type_; Ty<'a> => Ty<'tcx>} -nop_lift! {region; Region<'a> => Region<'tcx>} -nop_lift! {const_; Const<'a> => Const<'tcx>} -nop_lift! {pat; Pattern<'a> => Pattern<'tcx>} -nop_lift! {const_allocation; ConstAllocation<'a> => ConstAllocation<'tcx>} -nop_lift! {predicate; Predicate<'a> => Predicate<'tcx>} -nop_lift! {predicate; Clause<'a> => Clause<'tcx>} -nop_lift! {layout; Layout<'a> => Layout<'tcx>} +nop_lift! { type_; Ty<'a> => Ty<'tcx> } +nop_lift! { region; Region<'a> => Region<'tcx> } +nop_lift! { const_; Const<'a> => Const<'tcx> } +nop_lift! { pat; Pattern<'a> => Pattern<'tcx> } +nop_lift! { const_allocation; ConstAllocation<'a> => ConstAllocation<'tcx> } +nop_lift! { predicate; Predicate<'a> => Predicate<'tcx> } +nop_lift! { predicate; Clause<'a> => Clause<'tcx> } +nop_lift! { layout; Layout<'a> => Layout<'tcx> } -nop_list_lift! {type_lists; Ty<'a> => Ty<'tcx>} -nop_list_lift! {poly_existential_predicates; PolyExistentialPredicate<'a> => PolyExistentialPredicate<'tcx>} -nop_list_lift! {bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariableKind} +nop_list_lift! { type_lists; Ty<'a> => Ty<'tcx> } +nop_list_lift! { + poly_existential_predicates; PolyExistentialPredicate<'a> => PolyExistentialPredicate<'tcx> +} +nop_list_lift! { bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariableKind } // This is the impl for `&'a GenericArgs<'a>`. -nop_list_lift! {args; GenericArg<'a> => GenericArg<'tcx>} +nop_list_lift! { args; GenericArg<'a> => GenericArg<'tcx> } macro_rules! nop_slice_lift { ($ty:ty => $lifted:ty) => { @@ -2277,11 +2279,7 @@ macro_rules! nop_slice_lift { }; } -nop_slice_lift! {ty::ValTree<'a> => ty::ValTree<'tcx>} - -TrivialLiftImpls! { - ImplPolarity, PredicatePolarity, Promoted, BoundConstness, -} +nop_slice_lift! { ty::ValTree<'a> => ty::ValTree<'tcx> } macro_rules! sty_debug_print { ($fmt: expr, $ctxt: expr, $($variant: ident),*) => {{ @@ -3224,7 +3222,7 @@ impl<'tcx> TyCtxt<'tcx> { self.resolutions(()).module_children.get(&def_id).map_or(&[], |v| &v[..]) } - pub fn resolver_for_lowering(self) -> &'tcx Steal<(ty::ResolverAstLowering, Lrc)> { + pub fn resolver_for_lowering(self) -> &'tcx Steal<(ty::ResolverAstLowering, Arc)> { self.resolver_for_lowering_raw(()).0 } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 3431081b189e..feae8ea312ea 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1741,7 +1741,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { " as ", )?; } - ty::Pat(base_ty, pat) => { + ty::Pat(base_ty, pat) if self.tcx().validate_scalar_in_layout(int, ty) => { self.pretty_print_const_scalar_int(int, *base_ty, print_ty)?; p!(write(" is {pat:?}")); } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 03b26b445380..c33f952fc864 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -6,15 +6,18 @@ use std::fmt::{self, Debug}; use rustc_abi::TyAndLayout; +use rustc_ast::InlineAsmTemplatePiece; use rustc_ast_ir::try_visit; use rustc_ast_ir::visit::VisitorResult; use rustc_hir::def::Namespace; +use rustc_hir::def_id::LocalDefId; +use rustc_span::Span; use rustc_span::source_map::Spanned; use rustc_type_ir::ConstKind; use super::print::PrettyPrinter; use super::{GenericArg, GenericArgKind, Pattern, Region}; -use crate::mir::interpret; +use crate::mir::PlaceElem; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use crate::ty::print::{FmtPrinter, Printer, with_no_trimmed_paths}; use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; @@ -221,76 +224,89 @@ impl<'tcx> fmt::Debug for Region<'tcx> { // copy...), just add them to one of these lists as appropriate. // For things for which the type library provides traversal implementations -// for all Interners, we only need to provide a Lift implementation: +// for all Interners, we only need to provide a Lift implementation. TrivialLiftImpls! { - (), - bool, - usize, - u64, + (), + bool, + usize, + u64, + // tidy-alphabetical-start + crate::mir::interpret::AllocId, + crate::mir::interpret::Scalar, + crate::mir::Promoted, + rustc_abi::ExternAbi, + rustc_abi::Size, + rustc_hir::Safety, + rustc_type_ir::BoundConstness, + rustc_type_ir::PredicatePolarity, + // tidy-alphabetical-end } // For some things about which the type library does not know, or does not // provide any traversal implementations, we need to provide a traversal // implementation (only for TyCtxt<'_> interners). TrivialTypeTraversalImpls! { - ::rustc_abi::FieldIdx, - ::rustc_abi::VariantIdx, - crate::middle::region::Scope, - ::rustc_ast::InlineAsmOptions, - ::rustc_ast::InlineAsmTemplatePiece, - ::rustc_ast::NodeId, - ::rustc_hir::def::Res, - ::rustc_hir::def_id::LocalDefId, - ::rustc_hir::ByRef, - ::rustc_hir::HirId, - ::rustc_hir::MatchSource, - ::rustc_target::asm::InlineAsmRegOrRegClass, - crate::mir::coverage::BlockMarkerId, - crate::mir::coverage::CounterId, - crate::mir::coverage::ExpressionId, - crate::mir::coverage::ConditionId, + // tidy-alphabetical-start + crate::infer::canonical::Certainty, + crate::mir::BasicBlock, + crate::mir::BindingForm<'tcx>, + crate::mir::BlockTailInfo, + crate::mir::BorrowKind, + crate::mir::CastKind, + crate::mir::ConstValue<'tcx>, + crate::mir::CoroutineSavedLocal, + crate::mir::FakeReadCause, crate::mir::Local, + crate::mir::MirPhase, + crate::mir::NullOp<'tcx>, crate::mir::Promoted, + crate::mir::RawPtrKind, + crate::mir::RetagKind, + crate::mir::SourceInfo, + crate::mir::SourceScope, + crate::mir::SourceScopeLocalData, + crate::mir::SwitchTargets, + crate::traits::IsConstable, + crate::traits::OverflowError, + crate::ty::abstract_const::NotConstEvaluatable, crate::ty::adjustment::AutoBorrowMutability, + crate::ty::adjustment::PointerCoercion, crate::ty::AdtKind, - crate::ty::BoundRegion, - // Including `BoundRegionKind` is a *bit* dubious, but direct - // references to bound region appear in `ty::Error`, and aren't - // really meant to be folded. In general, we can only fold a fully - // general `Region`. - crate::ty::BoundRegionKind, crate::ty::AssocItem, crate::ty::AssocKind, + crate::ty::BoundRegion, + crate::ty::BoundVar, crate::ty::Placeholder, crate::ty::Placeholder, crate::ty::Placeholder, - crate::ty::LateParamRegion, - crate::ty::adjustment::PointerCoercion, - ::rustc_span::Ident, - ::rustc_span::Span, - ::rustc_span::Symbol, - ty::BoundVar, - ty::ValTree<'tcx>, + crate::ty::UserTypeAnnotationIndex, + crate::ty::ValTree<'tcx>, + rustc_abi::FieldIdx, + rustc_abi::VariantIdx, + rustc_ast::InlineAsmOptions, + rustc_ast::InlineAsmTemplatePiece, + rustc_hir::CoroutineKind, + rustc_hir::def_id::LocalDefId, + rustc_hir::HirId, + rustc_hir::MatchSource, + rustc_span::Ident, + rustc_span::Span, + rustc_span::Symbol, + rustc_target::asm::InlineAsmRegOrRegClass, + // tidy-alphabetical-end } + // For some things about which the type library does not know, or does not // provide any traversal implementations, we need to provide a traversal // implementation and a lift implementation (the former only for TyCtxt<'_> // interners). TrivialTypeTraversalAndLiftImpls! { - ::rustc_hir::def_id::DefId, - crate::ty::ClosureKind, + // tidy-alphabetical-start + crate::ty::instance::ReifyReason, crate::ty::ParamConst, crate::ty::ParamTy, - crate::ty::instance::ReifyReason, - interpret::AllocId, - interpret::CtfeProvenance, - interpret::Scalar, - rustc_abi::Size, -} - -TrivialLiftImpls! { - ::rustc_hir::Safety, - ::rustc_abi::ExternAbi, + rustc_hir::def_id::DefId, + // tidy-alphabetical-end } /////////////////////////////////////////////////////////////////////////// @@ -672,3 +688,39 @@ impl<'tcx, T: TypeFoldable> + Debug + Clone> TypeFoldable TypeFoldable> for &'tcx [InlineAsmTemplatePiece] { + fn try_fold_with>>( + self, + _folder: &mut F, + ) -> Result { + Ok(self) + } +} + +impl<'tcx> TypeFoldable> for &'tcx [Span] { + fn try_fold_with>>( + self, + _folder: &mut F, + ) -> Result { + Ok(self) + } +} + +impl<'tcx> TypeFoldable> for &'tcx ty::List { + fn try_fold_with>>( + self, + _folder: &mut F, + ) -> Result { + Ok(self) + } +} + +impl<'tcx> TypeFoldable> for &'tcx ty::List> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + ty::util::fold_list(self, folder, |tcx, v| tcx.mk_place_elems(v)) + } +} diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index f94f52e4f618..3c6ed9978456 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -148,7 +148,7 @@ pub struct TypeckResults<'tcx> { /// Set of trait imports actually used in the method resolution. /// This is used for warning unused imports. During type - /// checking, this `Lrc` should not be cloned: it must have a ref-count + /// checking, this `Arc` should not be cloned: it must have a ref-count /// of 1 so that we can insert things into the set mutably. pub used_trait_imports: UnordSet, diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 318bd0c7ec02..6fd0fb9a0a4f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -777,7 +777,6 @@ impl<'tcx> TyCtxt<'tcx> { self, def_id: DefId, args: GenericArgsRef<'tcx>, - inspect_coroutine_fields: InspectCoroutineFields, ) -> Result, Ty<'tcx>> { let mut visitor = OpaqueTypeExpander { seen_opaque_tys: FxHashSet::default(), @@ -786,9 +785,7 @@ impl<'tcx> TyCtxt<'tcx> { found_recursion: false, found_any_recursion: false, check_recursion: true, - expand_coroutines: true, tcx: self, - inspect_coroutine_fields, }; let expanded_type = visitor.expand_opaque_ty(def_id, args).unwrap(); @@ -965,19 +962,11 @@ struct OpaqueTypeExpander<'tcx> { primary_def_id: Option, found_recursion: bool, found_any_recursion: bool, - expand_coroutines: bool, /// Whether or not to check for recursive opaque types. /// This is `true` when we're explicitly checking for opaque type /// recursion, and 'false' otherwise to avoid unnecessary work. check_recursion: bool, tcx: TyCtxt<'tcx>, - inspect_coroutine_fields: InspectCoroutineFields, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum InspectCoroutineFields { - No, - Yes, } impl<'tcx> OpaqueTypeExpander<'tcx> { @@ -1009,41 +998,6 @@ impl<'tcx> OpaqueTypeExpander<'tcx> { None } } - - fn expand_coroutine(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) -> Option> { - if self.found_any_recursion { - return None; - } - let args = args.fold_with(self); - if !self.check_recursion || self.seen_opaque_tys.insert(def_id) { - let expanded_ty = match self.expanded_cache.get(&(def_id, args)) { - Some(expanded_ty) => *expanded_ty, - None => { - if matches!(self.inspect_coroutine_fields, InspectCoroutineFields::Yes) { - for bty in self.tcx.bound_coroutine_hidden_types(def_id) { - let hidden_ty = self.tcx.instantiate_bound_regions_with_erased( - bty.instantiate(self.tcx, args), - ); - self.fold_ty(hidden_ty); - } - } - let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args); - self.expanded_cache.insert((def_id, args), expanded_ty); - expanded_ty - } - }; - if self.check_recursion { - self.seen_opaque_tys.remove(&def_id); - } - Some(expanded_ty) - } else { - // If another opaque type that we contain is recursive, then it - // will report the error, so we don't have to. - self.found_any_recursion = true; - self.found_recursion = def_id == *self.primary_def_id.as_ref().unwrap(); - None - } - } } impl<'tcx> TypeFolder> for OpaqueTypeExpander<'tcx> { @@ -1052,19 +1006,13 @@ impl<'tcx> TypeFolder> for OpaqueTypeExpander<'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - let mut t = if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) = *t.kind() { + if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) = *t.kind() { self.expand_opaque_ty(def_id, args).unwrap_or(t) - } else if t.has_opaque_types() || t.has_coroutines() { + } else if t.has_opaque_types() { t.super_fold_with(self) } else { t - }; - if self.expand_coroutines { - if let ty::CoroutineWitness(def_id, args) = *t.kind() { - t = self.expand_coroutine(def_id, args).unwrap_or(t); - } } - t } fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { @@ -1753,9 +1701,7 @@ pub fn reveal_opaque_types_in_bounds<'tcx>( found_recursion: false, found_any_recursion: false, check_recursion: false, - expand_coroutines: false, tcx, - inspect_coroutine_fields: InspectCoroutineFields::No, }; val.fold_with(&mut visitor) } diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 9d59ffc88ba2..bca57817d661 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -12,11 +14,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// [`PatKind::Leaf`]. /// /// Used internally by [`MatchPairTree::for_pattern`]. - fn field_match_pairs<'pat>( + fn field_match_pairs( &mut self, place: PlaceBuilder<'tcx>, - subpatterns: &'pat [FieldPat<'tcx>], - ) -> Vec> { + subpatterns: &[FieldPat<'tcx>], + ) -> Vec> { subpatterns .iter() .map(|fieldpat| { @@ -31,13 +33,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// array pattern or slice pattern, and adds those trees to `match_pairs`. /// /// Used internally by [`MatchPairTree::for_pattern`]. - fn prefix_slice_suffix<'pat>( + fn prefix_slice_suffix( &mut self, - match_pairs: &mut Vec>, + match_pairs: &mut Vec>, place: &PlaceBuilder<'tcx>, - prefix: &'pat [Box>], - opt_slice: &'pat Option>>, - suffix: &'pat [Box>], + prefix: &[Pat<'tcx>], + opt_slice: &Option>>, + suffix: &[Pat<'tcx>], ) { let tcx = self.tcx; let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) { @@ -83,14 +85,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } -impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { +impl<'tcx> MatchPairTree<'tcx> { /// Recursively builds a match pair tree for the given pattern and its /// subpatterns. pub(in crate::builder) fn for_pattern( mut place_builder: PlaceBuilder<'tcx>, - pattern: &'pat Pat<'tcx>, + pattern: &Pat<'tcx>, cx: &mut Builder<'_, 'tcx>, - ) -> MatchPairTree<'pat, 'tcx> { + ) -> MatchPairTree<'tcx> { // Force the place type to the pattern's type. // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? if let Some(resolved) = place_builder.resolve_upvar(cx) { @@ -125,7 +127,7 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { if range.is_full_range(cx.tcx) == Some(true) { default_irrefutable() } else { - TestCase::Range(range) + TestCase::Range(Arc::clone(range)) } } @@ -255,6 +257,12 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { PatKind::Never => TestCase::Never, }; - MatchPairTree { place, test_case, subpairs, pattern } + MatchPairTree { + place, + test_case, + subpairs, + pattern_ty: pattern.ty, + pattern_span: pattern.span, + } } } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index b21ec8f3083b..a2f92a93ec54 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -33,6 +33,7 @@ mod util; use std::assert_matches::assert_matches; use std::borrow::Borrow; use std::mem; +use std::sync::Arc; /// Arguments to [`Builder::then_else_break_inner`] that are usually forwarded /// to recursive invocations. @@ -361,11 +362,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let scrutinee_place = unpack!(block = self.lower_scrutinee(block, scrutinee_id, scrutinee_span)); - let arms = arms.iter().map(|arm| &self.thir[*arm]); let match_start_span = span.shrink_to_lo().to(scrutinee_span); let patterns = arms - .clone() - .map(|arm| { + .iter() + .map(|&arm| { + let arm = &self.thir[arm]; let has_match_guard = if arm.guard.is_some() { HasMatchGuard::Yes } else { HasMatchGuard::No }; (&*arm.pattern, has_match_guard) @@ -412,20 +413,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// (by [Builder::lower_match_tree]). /// /// `outer_source_info` is the SourceInfo for the whole match. - fn lower_match_arms<'pat>( + fn lower_match_arms( &mut self, destination: Place<'tcx>, scrutinee_place_builder: PlaceBuilder<'tcx>, scrutinee_span: Span, - arms: impl IntoIterator>, + arms: &[ArmId], built_match_tree: BuiltMatchTree<'tcx>, outer_source_info: SourceInfo, - ) -> BlockAnd<()> - where - 'tcx: 'pat, - { + ) -> BlockAnd<()> { let arm_end_blocks: Vec = arms - .into_iter() + .iter() + .map(|&arm| &self.thir[arm]) .zip(built_match_tree.branches) .map(|(arm, branch)| { debug!("lowering arm {:?}\ncorresponding branch = {:?}", arm, branch); @@ -604,19 +603,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Optimize the case of `let x: T = ...` to write directly // into `x` and then require that `T == typeof(x)`. PatKind::AscribeUserType { - subpattern: - box Pat { - kind: - PatKind::Binding { - mode: BindingMode(ByRef::No, _), - var, - subpattern: None, - .. - }, - .. - }, + ref subpattern, ascription: thir::Ascription { ref annotation, variance: _ }, - } => { + } if let PatKind::Binding { + mode: BindingMode(ByRef::No, _), + var, + subpattern: None, + .. + } = subpattern.kind => + { let place = self.storage_live_binding( block, var, @@ -989,23 +984,19 @@ impl<'tcx> PatternExtraData<'tcx> { /// /// Will typically be incorporated into a [`Candidate`]. #[derive(Debug, Clone)] -struct FlatPat<'pat, 'tcx> { +struct FlatPat<'tcx> { /// To match the pattern, all of these must be satisfied... // Invariant: all the match pairs are recursively simplified. // Invariant: or-patterns must be sorted to the end. - match_pairs: Vec>, + match_pairs: Vec>, extra_data: PatternExtraData<'tcx>, } -impl<'tcx, 'pat> FlatPat<'pat, 'tcx> { +impl<'tcx> FlatPat<'tcx> { /// Creates a `FlatPat` containing a simplified [`MatchPairTree`] list/forest /// for the given pattern. - fn new( - place: PlaceBuilder<'tcx>, - pattern: &'pat Pat<'tcx>, - cx: &mut Builder<'_, 'tcx>, - ) -> Self { + fn new(place: PlaceBuilder<'tcx>, pattern: &Pat<'tcx>, cx: &mut Builder<'_, 'tcx>) -> Self { // First, recursively build a tree of match pairs for the given pattern. let mut match_pairs = vec![MatchPairTree::for_pattern(place, pattern, cx)]; let mut extra_data = PatternExtraData { @@ -1033,7 +1024,7 @@ impl<'tcx, 'pat> FlatPat<'pat, 'tcx> { /// of candidates, where each "leaf" candidate represents one of the ways for /// the arm pattern to successfully match. #[derive(Debug)] -struct Candidate<'pat, 'tcx> { +struct Candidate<'tcx> { /// For the candidate to match, all of these must be satisfied... /// /// --- @@ -1055,7 +1046,7 @@ struct Candidate<'pat, 'tcx> { /// Invariants: /// - All [`TestCase::Irrefutable`] patterns have been removed by simplification. /// - All or-patterns ([`TestCase::Or`]) have been sorted to the end. - match_pairs: Vec>, + match_pairs: Vec>, /// ...and if this is non-empty, one of these subcandidates also has to match... /// @@ -1072,7 +1063,7 @@ struct Candidate<'pat, 'tcx> { /// Invariant: at the end of match tree lowering, this must not contain an /// `is_never` candidate, because that would break binding consistency. /// - See [`Builder::remove_never_subcandidates`]. - subcandidates: Vec>, + subcandidates: Vec>, /// ...and if there is a guard it must be evaluated; if it's `false` then branch to `otherwise_block`. /// @@ -1107,10 +1098,10 @@ struct Candidate<'pat, 'tcx> { false_edge_start_block: Option, } -impl<'tcx, 'pat> Candidate<'pat, 'tcx> { +impl<'tcx> Candidate<'tcx> { fn new( place: PlaceBuilder<'tcx>, - pattern: &'pat Pat<'tcx>, + pattern: &Pat<'tcx>, has_guard: HasMatchGuard, cx: &mut Builder<'_, 'tcx>, ) -> Self { @@ -1123,7 +1114,7 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { } /// Incorporates an already-simplified [`FlatPat`] into a new candidate. - fn from_flat_pat(flat_pat: FlatPat<'pat, 'tcx>, has_guard: bool) -> Self { + fn from_flat_pat(flat_pat: FlatPat<'tcx>, has_guard: bool) -> Self { Candidate { match_pairs: flat_pat.match_pairs, extra_data: flat_pat.extra_data, @@ -1172,7 +1163,7 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { /// reference or by value, and to allow a mutable "context" to be shared by the /// traversal callbacks. Most traversals can use the simpler /// [`Candidate::visit_leaves`] wrapper instead. -fn traverse_candidate<'pat, 'tcx: 'pat, C, T, I>( +fn traverse_candidate<'tcx, C, T, I>( candidate: C, context: &mut T, // Called when visiting a "leaf" candidate (with no subcandidates). @@ -1184,7 +1175,7 @@ fn traverse_candidate<'pat, 'tcx: 'pat, C, T, I>( // Called after visiting a "node" candidate's children. complete_children: impl Copy + Fn(&mut T), ) where - C: Borrow>, // Typically `Candidate` or `&mut Candidate` + C: Borrow>, // Typically `Candidate` or `&mut Candidate` I: Iterator, { if candidate.borrow().subcandidates.is_empty() { @@ -1234,20 +1225,20 @@ struct Ascription<'tcx> { /// participate in or-pattern expansion, where they are transformed into subcandidates. /// - See [`Builder::expand_and_match_or_candidates`]. #[derive(Debug, Clone)] -enum TestCase<'pat, 'tcx> { +enum TestCase<'tcx> { Irrefutable { binding: Option>, ascription: Option> }, Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx }, Constant { value: mir::Const<'tcx> }, - Range(&'pat PatRange<'tcx>), + Range(Arc>), Slice { len: usize, variable_length: bool }, Deref { temp: Place<'tcx>, mutability: Mutability }, Never, - Or { pats: Box<[FlatPat<'pat, 'tcx>]> }, + Or { pats: Box<[FlatPat<'tcx>]> }, } -impl<'pat, 'tcx> TestCase<'pat, 'tcx> { - fn as_range(&self) -> Option<&'pat PatRange<'tcx>> { - if let Self::Range(v) = self { Some(*v) } else { None } +impl<'tcx> TestCase<'tcx> { + fn as_range(&self) -> Option<&PatRange<'tcx>> { + if let Self::Range(v) = self { Some(v.as_ref()) } else { None } } } @@ -1257,7 +1248,7 @@ impl<'pat, 'tcx> TestCase<'pat, 'tcx> { /// Each node also has a list of subpairs (possibly empty) that must also match, /// and a reference to the THIR pattern it represents. #[derive(Debug, Clone)] -pub(crate) struct MatchPairTree<'pat, 'tcx> { +pub(crate) struct MatchPairTree<'tcx> { /// This place... /// /// --- @@ -1272,7 +1263,7 @@ pub(crate) struct MatchPairTree<'pat, 'tcx> { /// --- /// Invariant: after creation and simplification in [`FlatPat::new`], /// this must not be [`TestCase::Irrefutable`]. - test_case: TestCase<'pat, 'tcx>, + test_case: TestCase<'tcx>, /// ... and these subpairs must match. /// @@ -1283,8 +1274,10 @@ pub(crate) struct MatchPairTree<'pat, 'tcx> { /// that tests its field for the value `3`. subpairs: Vec, - /// The pattern this was created from. - pattern: &'pat Pat<'tcx>, + /// Type field of the pattern this node was created from. + pattern_ty: Ty<'tcx>, + /// Span field of the pattern this node was created from. + pattern_span: Span, } /// See [`Test`] for more. @@ -1320,7 +1313,7 @@ enum TestKind<'tcx> { }, /// Test whether the value falls within an inclusive or exclusive range. - Range(Box>), + Range(Arc>), /// Test that the length of the slice is `== len` or `>= len`. Len { len: u64, op: BinOp }, @@ -1423,7 +1416,7 @@ struct BuiltMatchTree<'tcx> { impl<'tcx> MatchTreeSubBranch<'tcx> { fn from_sub_candidate( - candidate: Candidate<'_, 'tcx>, + candidate: Candidate<'tcx>, parent_data: &Vec>, ) -> Self { debug_assert!(candidate.match_pairs.is_empty()); @@ -1449,12 +1442,12 @@ impl<'tcx> MatchTreeSubBranch<'tcx> { } impl<'tcx> MatchTreeBranch<'tcx> { - fn from_candidate(candidate: Candidate<'_, 'tcx>) -> Self { + fn from_candidate(candidate: Candidate<'tcx>) -> Self { let mut sub_branches = Vec::new(); traverse_candidate( candidate, &mut Vec::new(), - &mut |candidate: Candidate<'_, '_>, parent_data: &mut Vec>| { + &mut |candidate: Candidate<'_>, parent_data: &mut Vec>| { sub_branches.push(MatchTreeSubBranch::from_sub_candidate(candidate, parent_data)); }, |inner_candidate, parent_data| { @@ -1485,23 +1478,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`) /// or not (for `let` and `match`). In the refutable case we return the block to which we branch /// on failure. - fn lower_match_tree<'pat>( + fn lower_match_tree( &mut self, block: BasicBlock, scrutinee_span: Span, scrutinee_place_builder: &PlaceBuilder<'tcx>, match_start_span: Span, - patterns: Vec<(&'pat Pat<'tcx>, HasMatchGuard)>, + patterns: Vec<(&Pat<'tcx>, HasMatchGuard)>, refutable: bool, - ) -> BuiltMatchTree<'tcx> - where - 'tcx: 'pat, - { + ) -> BuiltMatchTree<'tcx> { // Assemble the initial list of candidates. These top-level candidates are 1:1 with the // input patterns, but other parts of match lowering also introduce subcandidates (for // sub-or-patterns). So inside the algorithm, the candidates list may not correspond to // match arms directly. - let mut candidates: Vec> = patterns + let mut candidates: Vec> = patterns .into_iter() .map(|(pat, has_guard)| { Candidate::new(scrutinee_place_builder.clone(), pat, has_guard, self) @@ -1664,7 +1654,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, scrutinee_span: Span, start_block: BasicBlock, - candidates: &mut [&mut Candidate<'_, 'tcx>], + candidates: &mut [&mut Candidate<'tcx>], ) -> BasicBlock { ensure_sufficient_stack(|| { self.match_candidates_inner(span, scrutinee_span, start_block, candidates) @@ -1678,7 +1668,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, scrutinee_span: Span, mut start_block: BasicBlock, - candidates: &mut [&mut Candidate<'_, 'tcx>], + candidates: &mut [&mut Candidate<'tcx>], ) -> BasicBlock { if let [first, ..] = candidates { if first.false_edge_start_block.is_none() { @@ -1747,7 +1737,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// [otherwise block]: Candidate::otherwise_block fn select_matched_candidate( &mut self, - candidate: &mut Candidate<'_, 'tcx>, + candidate: &mut Candidate<'tcx>, start_block: BasicBlock, ) -> BasicBlock { assert!(candidate.otherwise_block.is_none()); @@ -1765,13 +1755,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Takes a list of candidates such that some of the candidates' first match pairs are /// or-patterns. This expands as many or-patterns as possible and processes the resulting /// candidates. Returns the unprocessed candidates if any. - fn expand_and_match_or_candidates<'pat, 'b, 'c>( + fn expand_and_match_or_candidates<'b, 'c>( &mut self, span: Span, scrutinee_span: Span, start_block: BasicBlock, - candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], - ) -> BlockAnd<&'b mut [&'c mut Candidate<'pat, 'tcx>]> { + candidates: &'b mut [&'c mut Candidate<'tcx>], + ) -> BlockAnd<&'b mut [&'c mut Candidate<'tcx>]> { // We can't expand or-patterns freely. The rule is: // - If a candidate doesn't start with an or-pattern, we include it in // the expansion list as-is (i.e. it "expands" to itself). @@ -1865,14 +1855,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new /// subcandidate. Any candidate that has been expanded this way should also be postprocessed /// at the end of [`Self::expand_and_match_or_candidates`]. - fn create_or_subcandidates<'pat>( + fn create_or_subcandidates( &mut self, - candidate: &mut Candidate<'pat, 'tcx>, - match_pair: MatchPairTree<'pat, 'tcx>, + candidate: &mut Candidate<'tcx>, + match_pair: MatchPairTree<'tcx>, ) { let TestCase::Or { pats } = match_pair.test_case else { bug!() }; debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats); - candidate.or_span = Some(match_pair.pattern.span); + candidate.or_span = Some(match_pair.pattern_span); candidate.subcandidates = pats .into_vec() .into_iter() @@ -1938,7 +1928,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// Note that this takes place _after_ the subcandidates have participated /// in match tree lowering. - fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { + fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'tcx>) { assert!(!candidate.subcandidates.is_empty()); if candidate.has_guard { // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. @@ -1981,7 +1971,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Never subcandidates may have a set of bindings inconsistent with their siblings, /// which would break later code. So we filter them out. Note that we can't filter out /// top-level candidates this way. - fn remove_never_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { + fn remove_never_subcandidates(&mut self, candidate: &mut Candidate<'tcx>) { if candidate.subcandidates.is_empty() { return; } @@ -2020,7 +2010,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, span: Span, scrutinee_span: Span, - candidate: &mut Candidate<'_, 'tcx>, + candidate: &mut Candidate<'tcx>, ) { if candidate.match_pairs.is_empty() { return; @@ -2086,7 +2076,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// [`Switch`]: TestKind::Switch /// [`SwitchInt`]: TestKind::SwitchInt /// [`Range`]: TestKind::Range - fn pick_test(&mut self, candidates: &[&mut Candidate<'_, 'tcx>]) -> (Place<'tcx>, Test<'tcx>) { + fn pick_test(&mut self, candidates: &[&mut Candidate<'tcx>]) -> (Place<'tcx>, Test<'tcx>) { // Extract the match-pair from the highest priority candidate let match_pair = &candidates[0].match_pairs[0]; let test = self.pick_test_for_match_pair(match_pair); @@ -2137,18 +2127,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// The sorted candidates are mutated to remove entailed match pairs: /// - candidate 0 becomes `[z @ true]` since we know that `x` was `true`; /// - candidate 1 becomes `[y @ false]` since we know that `x` was `false`. - fn sort_candidates<'b, 'c, 'pat>( + fn sort_candidates<'b, 'c>( &mut self, match_place: Place<'tcx>, test: &Test<'tcx>, - mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], + mut candidates: &'b mut [&'c mut Candidate<'tcx>], ) -> ( - &'b mut [&'c mut Candidate<'pat, 'tcx>], - FxIndexMap, Vec<&'b mut Candidate<'pat, 'tcx>>>, + &'b mut [&'c mut Candidate<'tcx>], + FxIndexMap, Vec<&'b mut Candidate<'tcx>>>, ) { // For each of the possible outcomes, collect vector of candidates that apply if the test // has that particular outcome. - let mut target_candidates: FxIndexMap<_, Vec<&mut Candidate<'_, '_>>> = Default::default(); + let mut target_candidates: FxIndexMap<_, Vec<&mut Candidate<'_>>> = Default::default(); let total_candidate_count = candidates.len(); @@ -2274,13 +2264,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// ``` /// /// We return the unprocessed candidates. - fn test_candidates<'pat, 'b, 'c>( + fn test_candidates<'b, 'c>( &mut self, span: Span, scrutinee_span: Span, - candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], + candidates: &'b mut [&'c mut Candidate<'tcx>], start_block: BasicBlock, - ) -> BlockAnd<&'b mut [&'c mut Candidate<'pat, 'tcx>]> { + ) -> BlockAnd<&'b mut [&'c mut Candidate<'tcx>]> { // Choose a match pair from the first candidate, and use it to determine a // test to perform that will confirm or refute that match pair. let (match_place, test) = self.pick_test(candidates); diff --git a/compiler/rustc_mir_build/src/builder/matches/simplify.rs b/compiler/rustc_mir_build/src/builder/matches/simplify.rs index ebaed1e431bb..15c860151dc9 100644 --- a/compiler/rustc_mir_build/src/builder/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/builder/matches/simplify.rs @@ -23,9 +23,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Simplify a list of match pairs so they all require a test. Stores relevant bindings and /// ascriptions in `extra_data`. #[instrument(skip(self), level = "debug")] - pub(super) fn simplify_match_pairs<'pat>( + pub(super) fn simplify_match_pairs( &mut self, - match_pairs: &mut Vec>, + match_pairs: &mut Vec>, extra_data: &mut PatternExtraData<'tcx>, ) { // In order to please the borrow checker, in a pattern like `x @ pat` we must lower the diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index afe6b4475be3..834f8be0d007 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -6,6 +6,7 @@ // the candidates based on the result. use std::cmp::Ordering; +use std::sync::Arc; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::{LangItem, RangeEnd}; @@ -26,20 +27,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Identifies what test is needed to decide if `match_pair` is applicable. /// /// It is a bug to call this with a not-fully-simplified pattern. - pub(super) fn pick_test_for_match_pair<'pat>( + pub(super) fn pick_test_for_match_pair( &mut self, - match_pair: &MatchPairTree<'pat, 'tcx>, + match_pair: &MatchPairTree<'tcx>, ) -> Test<'tcx> { let kind = match match_pair.test_case { TestCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def }, - TestCase::Constant { .. } if match_pair.pattern.ty.is_bool() => TestKind::If, - TestCase::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => TestKind::SwitchInt, - TestCase::Constant { value } => TestKind::Eq { value, ty: match_pair.pattern.ty }, + TestCase::Constant { .. } if match_pair.pattern_ty.is_bool() => TestKind::If, + TestCase::Constant { .. } if is_switch_ty(match_pair.pattern_ty) => TestKind::SwitchInt, + TestCase::Constant { value } => TestKind::Eq { value, ty: match_pair.pattern_ty }, - TestCase::Range(range) => { - assert_eq!(range.ty, match_pair.pattern.ty); - TestKind::Range(Box::new(range.clone())) + TestCase::Range(ref range) => { + assert_eq!(range.ty, match_pair.pattern_ty); + TestKind::Range(Arc::clone(range)) } TestCase::Slice { len, variable_length } => { @@ -56,13 +57,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TestCase::Or { .. } => bug!("or-patterns should have already been handled"), TestCase::Irrefutable { .. } => span_bug!( - match_pair.pattern.span, + match_pair.pattern_span, "simplifiable pattern found: {:?}", - match_pair.pattern + match_pair.pattern_span ), }; - Test { span: match_pair.pattern.span, kind } + Test { span: match_pair.pattern_span, kind } } #[instrument(skip(self, target_blocks, place), level = "debug")] @@ -521,8 +522,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, test_place: Place<'tcx>, test: &Test<'tcx>, - candidate: &mut Candidate<'_, 'tcx>, - sorted_candidates: &FxIndexMap, Vec<&mut Candidate<'_, 'tcx>>>, + candidate: &mut Candidate<'tcx>, + sorted_candidates: &FxIndexMap, Vec<&mut Candidate<'tcx>>>, ) -> Option> { // Find the match_pair for this place (if any). At present, // afaik, there can be at most one. (In the future, if we @@ -558,14 +559,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // FIXME(#29623) we could use PatKind::Range to rule // things out here, in some cases. (TestKind::SwitchInt, &TestCase::Constant { value }) - if is_switch_ty(match_pair.pattern.ty) => + if is_switch_ty(match_pair.pattern_ty) => { // An important invariant of candidate sorting is that a candidate // must not match in multiple branches. For `SwitchInt` tests, adding // a new value might invalidate that property for range patterns that // have already been sorted into the failure arm, so we must take care // not to add such values here. - let is_covering_range = |test_case: &TestCase<'_, 'tcx>| { + let is_covering_range = |test_case: &TestCase<'tcx>| { test_case.as_range().is_some_and(|range| { matches!( range.contains(value, self.tcx, self.typing_env()), @@ -573,7 +574,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) }) }; - let is_conflicting_candidate = |candidate: &&mut Candidate<'_, 'tcx>| { + let is_conflicting_candidate = |candidate: &&mut Candidate<'tcx>| { candidate .match_pairs .iter() @@ -685,8 +686,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - (TestKind::Range(test), &TestCase::Range(pat)) => { - if test.as_ref() == pat { + (TestKind::Range(test), TestCase::Range(pat)) => { + if test == pat { fully_matched = true; Some(TestBranch::Success) } else { diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index 1bd399e511b3..83e79572b2a8 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -67,7 +67,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// a MIR pass run after borrow checking. pub(super) fn collect_fake_borrows<'tcx>( cx: &mut Builder<'_, 'tcx>, - candidates: &[Candidate<'_, 'tcx>], + candidates: &[Candidate<'tcx>], temp_span: Span, scrutinee_base: PlaceBase, ) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> { @@ -135,7 +135,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } } - fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) { + fn visit_candidate(&mut self, candidate: &Candidate<'tcx>) { for binding in &candidate.extra_data.bindings { self.visit_binding(binding); } @@ -144,7 +144,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } } - fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'_, 'tcx>) { + fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'tcx>) { for binding in &flat_pat.extra_data.bindings { self.visit_binding(binding); } @@ -153,7 +153,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } } - fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'_, 'tcx>) { + fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'tcx>) { if let TestCase::Or { pats, .. } = &match_pair.test_case { for flat_pat in pats.iter() { self.visit_flat_pat(flat_pat) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 8247a6c6a32d..697cb7cf37a3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -676,12 +676,14 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { let mut interpreted_as_const = None; let mut interpreted_as_const_sugg = None; - if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } - | PatKind::AscribeUserType { - subpattern: - box Pat { kind: PatKind::ExpandedConstant { def_id, is_inline: false, .. }, .. }, - .. - } = pat.kind + // These next few matches want to peek through `AscribeUserType` to see + // the underlying pattern. + let mut unpeeled_pat = pat; + while let PatKind::AscribeUserType { ref subpattern, .. } = unpeeled_pat.kind { + unpeeled_pat = subpattern; + } + + if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } = unpeeled_pat.kind && let DefKind::Const = self.tcx.def_kind(def_id) && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) // We filter out paths with multiple path::segments. @@ -692,11 +694,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { // When we encounter a constant as the binding name, point at the `const` definition. interpreted_as_const = Some(span); interpreted_as_const_sugg = Some(InterpretedAsConst { span: pat.span, variable }); - } else if let PatKind::Constant { .. } - | PatKind::AscribeUserType { - subpattern: box Pat { kind: PatKind::Constant { .. }, .. }, - .. - } = pat.kind + } else if let PatKind::Constant { .. } = unpeeled_pat.kind && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) { // If the pattern to match is an integer literal: diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index cc6d69710e4c..551ec5cf4e9c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -208,7 +208,7 @@ impl<'tcx> ConstToPat<'tcx> { let field = FieldIdx::new(idx); // Patterns can only use monomorphic types. let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty); - FieldPat { field, pattern: self.valtree_to_pat(val, ty) } + FieldPat { field, pattern: *self.valtree_to_pat(val, ty) } }) .collect() } @@ -277,7 +277,7 @@ impl<'tcx> ConstToPat<'tcx> { prefix: cv .unwrap_branch() .iter() - .map(|val| self.valtree_to_pat(*val, *elem_ty)) + .map(|val| *self.valtree_to_pat(*val, *elem_ty)) .collect(), slice: None, suffix: Box::new([]), @@ -286,7 +286,7 @@ impl<'tcx> ConstToPat<'tcx> { prefix: cv .unwrap_branch() .iter() - .map(|val| self.valtree_to_pat(*val, *elem_ty)) + .map(|val| *self.valtree_to_pat(*val, *elem_ty)) .collect(), slice: None, suffix: Box::new([]), diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 8ecdbecf1651..c862d012f4eb 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -4,6 +4,7 @@ mod check_match; mod const_to_pat; use std::cmp::Ordering; +use std::sync::Arc; use rustc_abi::{FieldIdx, Integer}; use rustc_errors::MultiSpan; @@ -262,7 +263,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let hi = lower_endpoint(hi_expr)?.unwrap_or(PatRangeBoundary::PosInfinity); let cmp = lo.compare_with(hi, ty, self.tcx, self.typing_env); - let mut kind = PatKind::Range(Box::new(PatRange { lo, hi, end, ty })); + let mut kind = PatKind::Range(Arc::new(PatRange { lo, hi, end, ty })); match (end, cmp) { // `x..y` where `x < y`. (RangeEnd::Excluded, Some(Ordering::Less)) => {} @@ -418,7 +419,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .iter() .map(|field| FieldPat { field: self.typeck_results.field_index(field.hir_id), - pattern: self.lower_pattern(field.pat), + pattern: *self.lower_pattern(field.pat), }) .collect(); @@ -446,13 +447,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .enumerate_and_adjust(expected_len, gap_pos) .map(|(i, subpattern)| FieldPat { field: FieldIdx::new(i), - pattern: self.lower_pattern(subpattern), + pattern: *self.lower_pattern(subpattern), }) .collect() } - fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Box<[Box>]> { - pats.iter().map(|p| self.lower_pattern(p)).collect() + fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Box<[Pat<'tcx>]> { + pats.iter().map(|p| *self.lower_pattern(p)).collect() } fn lower_opt_pattern(&mut self, pat: Option<&'tcx hir::Pat<'tcx>>) -> Option>> { diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 729c8f784ba9..9ab87dd99ffc 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -643,8 +643,8 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "}", depth_lvl); } - fn print_pat(&mut self, pat: &Box>, depth_lvl: usize) { - let Pat { ty, span, kind } = &**pat; + fn print_pat(&mut self, pat: &Pat<'tcx>, depth_lvl: usize) { + let &Pat { ty, span, ref kind } = pat; print_indented!(self, "Pat: {", depth_lvl); print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 1); diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index d2ffd26f0a06..1a2120ecd710 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1367,16 +1367,17 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_cast( &mut self, - kind: &mut CastKind, - operand: &mut Operand<'tcx>, + initial_kind: &mut CastKind, + initial_operand: &mut Operand<'tcx>, to: Ty<'tcx>, location: Location, ) -> Option { use CastKind::*; use rustc_middle::ty::adjustment::PointerCoercion::*; - let mut from = operand.ty(self.local_decls, self.tcx); - let mut value = self.simplify_operand(operand, location)?; + let mut from = initial_operand.ty(self.local_decls, self.tcx); + let mut kind = *initial_kind; + let mut value = self.simplify_operand(initial_operand, location)?; if from == to { return Some(value); } @@ -1400,7 +1401,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && to.is_unsafe_ptr() && self.pointers_have_same_metadata(from, to) { - *kind = PtrToPtr; + kind = PtrToPtr; was_updated_this_iteration = true; } @@ -1443,7 +1444,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { to: inner_to, } = *self.get(value) { - let new_kind = match (inner_kind, *kind) { + let new_kind = match (inner_kind, kind) { // Even if there's a narrowing cast in here that's fine, because // things like `*mut [i32] -> *mut i32 -> *const i32` and // `*mut [i32] -> *const [i32] -> *const i32` can skip the middle in MIR. @@ -1471,7 +1472,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { _ => None, }; if let Some(new_kind) = new_kind { - *kind = new_kind; + kind = new_kind; from = inner_from; value = inner_value; was_updated_this_iteration = true; @@ -1489,10 +1490,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } if was_ever_updated && let Some(op) = self.try_as_operand(value, location) { - *operand = op; + *initial_operand = op; + *initial_kind = kind; } - Some(self.insert(Value::Cast { kind: *kind, value, from, to })) + Some(self.insert(Value::Cast { kind, value, from, to })) } fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option { diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 2d2d50e62f9a..a64941cce3af 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -4,15 +4,13 @@ use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::solve::{Certainty, Goal, NoSolution}; use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; -pub trait SolverDelegate: Deref::Infcx> + Sized { - type Infcx: InferCtxtLike::Interner>; +pub trait SolverDelegate: Deref + Sized { + type Infcx: InferCtxtLike; type Interner: Interner; fn cx(&self) -> Self::Interner { (**self).cx() } - type Span: Copy; - fn build_with_canonical( cx: Self::Interner, canonical: &ty::CanonicalQueryInput, @@ -23,7 +21,7 @@ pub trait SolverDelegate: Deref::Infcx> + Size fn fresh_var_for_kind_with_span( &self, arg: ::GenericArg, - span: Self::Span, + span: ::Span, ) -> ::GenericArg; // FIXME: Uplift the leak check into this crate. @@ -61,6 +59,7 @@ pub trait SolverDelegate: Deref::Infcx> + Size fn instantiate_canonical_var_with_infer( &self, cv_info: ty::CanonicalVarInfo, + span: ::Span, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> ::GenericArg; @@ -86,6 +85,7 @@ pub trait SolverDelegate: Deref::Infcx> + Size &self, key: ty::OpaqueTypeKey, hidden_ty: ::Ty, + span: ::Span, ); fn reset_opaque_types(&self); diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index e99cd3d27270..a0d41110d967 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -255,20 +255,29 @@ where self.delegate, &original_values, &response, + self.origin_span, ); let Response { var_values, external_constraints, certainty } = self.delegate.instantiate_canonical(response, instantiation); - Self::unify_query_var_values(self.delegate, param_env, &original_values, var_values); + Self::unify_query_var_values( + self.delegate, + param_env, + &original_values, + var_values, + self.origin_span, + ); let ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals, } = &*external_constraints; + self.register_region_constraints(region_constraints); self.register_new_opaque_types(opaque_types); + (normalization_nested_goals.clone(), certainty) } @@ -279,6 +288,7 @@ where delegate: &D, original_values: &[I::GenericArg], response: &Canonical, + span: I::Span, ) -> CanonicalVarValues { // FIXME: Longterm canonical queries should deal with all placeholders // created inside of the query directly instead of returning them to the @@ -331,7 +341,7 @@ where // A variable from inside a binder of the query. While ideally these shouldn't // exist at all (see the FIXME at the start of this method), we have to deal with // them for now. - delegate.instantiate_canonical_var_with_infer(info, |idx| { + delegate.instantiate_canonical_var_with_infer(info, span, |idx| { ty::UniverseIndex::from(prev_universe.index() + idx.index()) }) } else if info.is_existential() { @@ -345,7 +355,7 @@ where if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] { v } else { - delegate.instantiate_canonical_var_with_infer(info, |_| prev_universe) + delegate.instantiate_canonical_var_with_infer(info, span, |_| prev_universe) } } else { // For placeholders which were already part of the input, we simply map this @@ -376,12 +386,13 @@ where param_env: I::ParamEnv, original_values: &[I::GenericArg], var_values: CanonicalVarValues, + span: I::Span, ) { assert_eq!(original_values.len(), var_values.len()); for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) { let goals = - delegate.eq_structurally_relating_aliases(param_env, orig, response).unwrap(); + delegate.eq_structurally_relating_aliases(param_env, orig, response, span).unwrap(); assert!(goals.is_empty()); } } @@ -401,7 +412,7 @@ where fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey, I::Ty)]) { for &(key, ty) in opaque_types { - self.delegate.inject_new_hidden_type_unchecked(key, ty); + self.delegate.inject_new_hidden_type_unchecked(key, ty, self.origin_span); } } } @@ -431,7 +442,7 @@ where // `rustc_trait_selection::solve::inspect::analyse`. pub fn instantiate_canonical_state>( delegate: &D, - span: D::Span, + span: I::Span, param_env: I::ParamEnv, orig_values: &mut Vec, state: inspect::CanonicalState, @@ -442,19 +453,17 @@ where { // In case any fresh inference variables have been created between `state` // and the previous instantiation, extend `orig_values` for it. - assert!(orig_values.len() <= state.value.var_values.len()); - for &arg in &state.value.var_values.var_values.as_slice() - [orig_values.len()..state.value.var_values.len()] - { - let unconstrained = delegate.fresh_var_for_kind_with_span(arg, span); - orig_values.push(unconstrained); - } + orig_values.extend( + state.value.var_values.var_values.as_slice()[orig_values.len()..] + .iter() + .map(|&arg| delegate.fresh_var_for_kind_with_span(arg, span)), + ); let instantiation = - EvalCtxt::compute_query_response_instantiation_values(delegate, orig_values, &state); + EvalCtxt::compute_query_response_instantiation_values(delegate, orig_values, &state, span); let inspect::State { var_values, data } = delegate.instantiate_canonical(state, instantiation); - EvalCtxt::unify_query_var_values(delegate, param_env, orig_values, var_values); + EvalCtxt::unify_query_var_values(delegate, param_env, orig_values, var_values, span); data } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 91ad24bff674..48a05488148b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -78,6 +78,8 @@ where nested_goals: NestedGoals, + pub(super) origin_span: I::Span, + // Has this `EvalCtxt` errored out with `NoSolution` in `try_evaluate_added_goals`? // // If so, then it can no longer be used to make a canonical query response, @@ -134,6 +136,7 @@ pub trait SolverDelegateEvalExt: SolverDelegate { &self, goal: Goal::Predicate>, generate_proof_tree: GenerateProofTree, + span: ::Span, ) -> ( Result<(HasChanged, Certainty), NoSolution>, Option>, @@ -174,8 +177,9 @@ where &self, goal: Goal, generate_proof_tree: GenerateProofTree, + span: I::Span, ) -> (Result<(HasChanged, Certainty), NoSolution>, Option>) { - EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, |ecx| { + EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, span, |ecx| { ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal) }) } @@ -186,7 +190,7 @@ where goal: Goal::Predicate>, ) -> bool { self.probe(|| { - EvalCtxt::enter_root(self, root_depth, GenerateProofTree::No, |ecx| { + EvalCtxt::enter_root(self, root_depth, GenerateProofTree::No, I::Span::dummy(), |ecx| { ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal) }) .0 @@ -203,9 +207,13 @@ where Result<(NestedNormalizationGoals, HasChanged, Certainty), NoSolution>, Option>, ) { - EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, |ecx| { - ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal) - }) + EvalCtxt::enter_root( + self, + self.cx().recursion_limit(), + generate_proof_tree, + I::Span::dummy(), + |ecx| ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal), + ) } } @@ -229,6 +237,7 @@ where delegate: &D, root_depth: usize, generate_proof_tree: GenerateProofTree, + origin_span: I::Span, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> R, ) -> (R, Option>) { let mut search_graph = SearchGraph::new(root_depth); @@ -248,6 +257,7 @@ where variables: Default::default(), var_values: CanonicalVarValues::dummy(), is_normalizes_to_goal: false, + origin_span, tainted: Ok(()), }; let result = f(&mut ecx); @@ -289,12 +299,13 @@ where max_input_universe: canonical_input.canonical.max_universe, search_graph, nested_goals: NestedGoals::new(), + origin_span: I::Span::dummy(), tainted: Ok(()), inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values), }; for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { - ecx.delegate.inject_new_hidden_type_unchecked(key, ty); + ecx.delegate.inject_new_hidden_type_unchecked(key, ty, ecx.origin_span); } if !ecx.nested_goals.is_empty() { @@ -822,8 +833,12 @@ where let identity_args = self.fresh_args_for_item(alias.def_id); let rigid_ctor = ty::AliasTerm::new_from_args(cx, alias.def_id, identity_args); let ctor_term = rigid_ctor.to_term(cx); - let obligations = - self.delegate.eq_structurally_relating_aliases(param_env, term, ctor_term)?; + let obligations = self.delegate.eq_structurally_relating_aliases( + param_env, + term, + ctor_term, + self.origin_span, + )?; debug_assert!(obligations.is_empty()); self.relate(param_env, alias, variance, rigid_ctor) } else { @@ -841,7 +856,12 @@ where lhs: T, rhs: T, ) -> Result<(), NoSolution> { - let result = self.delegate.eq_structurally_relating_aliases(param_env, lhs, rhs)?; + let result = self.delegate.eq_structurally_relating_aliases( + param_env, + lhs, + rhs, + self.origin_span, + )?; assert_eq!(result, vec![]); Ok(()) } @@ -864,7 +884,7 @@ where variance: ty::Variance, rhs: T, ) -> Result<(), NoSolution> { - let goals = self.delegate.relate(param_env, lhs, variance, rhs)?; + let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?; self.add_goals(GoalSource::Misc, goals); Ok(()) } @@ -881,7 +901,7 @@ where lhs: T, rhs: T, ) -> Result>, NoSolution> { - Ok(self.delegate.relate(param_env, lhs, ty::Variance::Invariant, rhs)?) + Ok(self.delegate.relate(param_env, lhs, ty::Variance::Invariant, rhs, self.origin_span)?) } pub(super) fn instantiate_binder_with_infer + Copy>( @@ -917,12 +937,12 @@ where } pub(super) fn register_ty_outlives(&self, ty: I::Ty, lt: I::Region) { - self.delegate.register_ty_outlives(ty, lt); + self.delegate.register_ty_outlives(ty, lt, self.origin_span); } pub(super) fn register_region_outlives(&self, a: I::Region, b: I::Region) { // `b : a` ==> `a <= b` - self.delegate.sub_regions(b, a); + self.delegate.sub_regions(b, a, self.origin_span); } /// Computes the list of goals required for `arg` to be well-formed diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index be69a6e84ea1..add96a1fdf7a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -39,6 +39,7 @@ where max_input_universe, search_graph: outer_ecx.search_graph, nested_goals: outer_ecx.nested_goals.clone(), + origin_span: outer_ecx.origin_span, tainted: outer_ecx.tainted, inspect: outer_ecx.inspect.take_and_enter_probe(), }; diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index e681987ff07f..1a104ff5e337 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -17,12 +17,12 @@ use std::path::{Path, PathBuf}; use std::str::Utf8Error; +use std::sync::Arc; use rustc_ast as ast; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AttrItem, Attribute, MetaItemInner, token}; use rustc_ast_pretty::pprust; -use rustc_data_structures::sync::Lrc; use rustc_errors::{Diag, EmissionGuarantee, FatalError, PResult, pluralize}; use rustc_session::parse::ParseSess; use rustc_span::source_map::SourceMap; @@ -147,7 +147,7 @@ pub fn utf8_error( /// the initial token stream. fn new_parser_from_source_file( psess: &ParseSess, - source_file: Lrc, + source_file: Arc, ) -> Result, Vec>> { let end_pos = source_file.end_position(); let stream = source_file_to_stream(psess, source_file, None)?; @@ -172,7 +172,7 @@ pub fn source_str_to_stream( /// parsing the token stream. fn source_file_to_stream<'psess>( psess: &'psess ParseSess, - source_file: Lrc, + source_file: Arc, override_span: Option, ) -> Result>> { let src = source_file.src.as_ref().unwrap_or_else(|| { diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9ee6c2fae1ac..47bf80a07833 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1,5 +1,6 @@ use std::mem::take; use std::ops::{Deref, DerefMut}; +use std::sync::Arc; use ast::token::IdentIsRaw; use rustc_ast as ast; @@ -14,7 +15,6 @@ use rustc_ast::{ }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync::Lrc; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, PResult, Subdiagnostic, Suggestions, pluralize, @@ -2403,7 +2403,7 @@ impl<'a> Parser<'a> { let mut labels = vec![]; while let TokenKind::Interpolated(nt) = &tok.kind { let tokens = nt.tokens(); - labels.push(Lrc::clone(nt)); + labels.push(Arc::clone(nt)); if let Some(tokens) = tokens && let tokens = tokens.to_attr_token_stream() && let tokens = tokens.0.deref() diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index faebb5a40bb1..ea464fc8ebbb 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -13,6 +13,7 @@ mod ty; use std::assert_matches::debug_assert_matches; use std::ops::Range; +use std::sync::Arc; use std::{fmt, mem, slice}; use attr_wrapper::{AttrWrapper, UsePreAttrPos}; @@ -34,7 +35,6 @@ use rustc_ast::{ }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diag, FatalError, MultiSpan, PResult}; use rustc_index::interval::IntervalSet; use rustc_session::parse::ParseSess; @@ -1685,5 +1685,5 @@ pub enum ParseNtResult { Lifetime(Ident, IdentIsRaw), /// This case will eventually be removed, along with `Token::Interpolate`. - Nt(Lrc), + Nt(Arc), } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 67cabb757e9e..eefdb641da22 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use rustc_ast::HasTokens; use rustc_ast::ptr::P; use rustc_ast::token::Nonterminal::*; @@ -7,7 +9,6 @@ use rustc_ast::token::{ self, Delimiter, InvisibleOrigin, MetaVarKind, Nonterminal, NonterminalKind, Token, }; use rustc_ast_pretty::pprust; -use rustc_data_structures::sync::Lrc; use rustc_errors::PResult; use rustc_span::{Ident, kw}; @@ -235,7 +236,7 @@ impl<'a> Parser<'a> { ); } - Ok(ParseNtResult::Nt(Lrc::new(nt))) + Ok(ParseNtResult::Nt(Arc::new(nt))) } } diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 655ab8223597..8b8c81a77a01 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -13,7 +13,6 @@ use rustc_ast::token::{self, Delimiter, Token}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast::{self as ast, PatKind, visit}; use rustc_ast_pretty::pprust::item_to_string; -use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::{HumanEmitter, OutputTheme}; use rustc_errors::{DiagCtxt, MultiSpan, PResult}; use rustc_session::parse::ParseSess; @@ -27,7 +26,7 @@ use crate::parser::{ForceCollect, Parser}; use crate::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; fn psess() -> ParseSess { - ParseSess::new(vec![crate::DEFAULT_LOCALE_RESOURCE, crate::DEFAULT_LOCALE_RESOURCE]) + ParseSess::new(vec![crate::DEFAULT_LOCALE_RESOURCE]) } /// Map string to parser (via tts). @@ -39,13 +38,11 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> { )) } -fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Lrc, Arc>>) { +fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Arc, Arc>>) { let output = Arc::new(Mutex::new(Vec::new())); - let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - vec![crate::DEFAULT_LOCALE_RESOURCE, crate::DEFAULT_LOCALE_RESOURCE], - false, - ); + let source_map = Arc::new(SourceMap::new(FilePathMapping::empty())); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false); let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), fallback_bundle) .sm(Some(source_map.clone())) .diagnostic_width(Some(140)); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 837da6e7724c..4c683c82b6c0 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1851,6 +1851,34 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let mut is_simd = false; let mut is_transparent = false; + // catch `repr()` with no arguments, applied to an item (i.e. not `#![repr()]`) + if hints.is_empty() && item.is_some() { + for attr in attrs.iter().filter(|attr| attr.has_name(sym::repr)) { + match target { + Target::Struct | Target::Union | Target::Enum => {} + Target::Fn | Target::Method(_) => { + feature_err( + &self.tcx.sess, + sym::fn_align, + attr.span, + fluent::passes_repr_align_function, + ) + .emit(); + } + _ => { + self.dcx().emit_err( + errors::AttrApplication::StructEnumFunctionMethodUnion { + hint_span: attr.span, + span, + }, + ); + } + } + } + + return; + } + for hint in &hints { if !hint.is_meta_item() { self.dcx().emit_err(errors::ReprIdent { span: hint.span() }); @@ -1883,24 +1911,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } sym::align => { - if let (Target::Fn | Target::Method(MethodKind::Inherent), false) = - (target, self.tcx.features().fn_align()) - { - feature_err( - &self.tcx.sess, - sym::fn_align, - hint.span(), - fluent::passes_repr_align_function, - ) - .emit(); - } - match target { - Target::Struct - | Target::Union - | Target::Enum - | Target::Fn - | Target::Method(_) => {} + Target::Struct | Target::Union | Target::Enum => {} + Target::Fn | Target::Method(_) => { + if !self.tcx.features().fn_align() { + feature_err( + &self.tcx.sess, + sym::fn_align, + hint.span(), + fluent::passes_repr_align_function, + ) + .emit(); + } + } _ => { self.dcx().emit_err( errors::AttrApplication::StructEnumFunctionMethodUnion { diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index fa095b108848..7988afd3e139 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::{QueryInvocationId, SelfProfilerRef}; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc}; +use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock}; use rustc_data_structures::unord::UnordMap; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable}; @@ -29,13 +29,13 @@ use crate::query::{QueryContext, QuerySideEffects}; #[derive(Clone)] pub struct DepGraph { - data: Option>>, + data: Option>>, /// This field is used for assigning DepNodeIndices when running in /// non-incremental mode. Even in non-incremental mode we make sure that /// each task has a `DepNodeIndex` that uniquely identifies it. This unique /// ID is used for self-profiling. - virtual_dep_node_index: Lrc, + virtual_dep_node_index: Arc, } rustc_index::newtype_index! { @@ -171,7 +171,7 @@ impl DepGraph { } DepGraph { - data: Some(Lrc::new(DepGraphData { + data: Some(Arc::new(DepGraphData { previous_work_products: prev_work_products, dep_node_debug: Default::default(), current, @@ -180,12 +180,12 @@ impl DepGraph { colors, debug_loaded_from_disk: Default::default(), })), - virtual_dep_node_index: Lrc::new(AtomicU32::new(0)), + virtual_dep_node_index: Arc::new(AtomicU32::new(0)), } } pub fn new_disabled() -> DepGraph { - DepGraph { data: None, virtual_dep_node_index: Lrc::new(AtomicU32::new(0)) } + DepGraph { data: None, virtual_dep_node_index: Arc::new(AtomicU32::new(0)) } } #[inline] diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index 6b737ceb50a6..cf50e61e72ba 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -1,6 +1,7 @@ +use std::sync::Arc; + use rustc_ast as ast; use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher}; -use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::definitions::DefPathHash; use rustc_session::Session; @@ -117,7 +118,7 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { fn span_data_to_lines_and_cols( &mut self, span: &SpanData, - ) -> Option<(Lrc, usize, BytePos, usize, BytePos)> { + ) -> Option<(Arc, usize, BytePos, usize, BytePos)> { self.source_map().span_data_to_lines_and_cols(span) } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index eec9e9a85150..ca7064a36a17 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -6,6 +6,7 @@ //! Imports are also considered items and placed into modules here, but not resolved yet. use std::cell::Cell; +use std::sync::Arc; use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind}; use rustc_ast::{ @@ -13,7 +14,6 @@ use rustc_ast::{ ItemKind, MetaItemKind, NodeId, StmtKind, }; use rustc_attr_parsing as attr; -use rustc_data_structures::sync::Lrc; use rustc_expand::base::ResolverExpand; use rustc_expand::expand::AstFragment; use rustc_hir::def::{self, *}; @@ -179,7 +179,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { LoadedMacro::MacroDef { def, ident, attrs, span, edition } => { self.compile_macro(&def, ident, &attrs, span, ast::DUMMY_NODE_ID, edition) } - LoadedMacro::ProcMacro(ext) => MacroData::new(Lrc::new(ext)), + LoadedMacro::ProcMacro(ext) => MacroData::new(Arc::new(ext)), }; self.macro_map.entry(def_id).or_insert(macro_data) diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 04144eb616fa..1cf821653c9e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -27,6 +27,7 @@ use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; use std::fmt; +use std::sync::Arc; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use effective_visibilities::EffectiveVisibilitiesVisitor; @@ -46,7 +47,7 @@ use rustc_ast::{ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{FreezeReadGuard, Lrc}; +use rustc_data_structures::sync::FreezeReadGuard; use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed}; use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind}; use rustc_feature::BUILTIN_ATTRIBUTES; @@ -995,13 +996,13 @@ struct DeriveData { } struct MacroData { - ext: Lrc, + ext: Arc, rule_spans: Vec<(usize, Span)>, macro_rules: bool, } impl MacroData { - fn new(ext: Lrc) -> MacroData { + fn new(ext: Arc) -> MacroData { MacroData { ext, rule_spans: Vec::new(), macro_rules: false } } } @@ -1110,8 +1111,8 @@ pub struct Resolver<'ra, 'tcx> { registered_tools: &'tcx RegisteredTools, macro_use_prelude: FxHashMap>, macro_map: FxHashMap, - dummy_ext_bang: Lrc, - dummy_ext_derive: Lrc, + dummy_ext_bang: Arc, + dummy_ext_derive: Arc, non_macro_attr: MacroData, local_macro_def_scopes: FxHashMap>, ast_transform_scopes: FxHashMap>, @@ -1510,9 +1511,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { registered_tools, macro_use_prelude: FxHashMap::default(), macro_map: FxHashMap::default(), - dummy_ext_bang: Lrc::new(SyntaxExtension::dummy_bang(edition)), - dummy_ext_derive: Lrc::new(SyntaxExtension::dummy_derive(edition)), - non_macro_attr: MacroData::new(Lrc::new(SyntaxExtension::non_macro_attr(edition))), + dummy_ext_bang: Arc::new(SyntaxExtension::dummy_bang(edition)), + dummy_ext_derive: Arc::new(SyntaxExtension::dummy_derive(edition)), + non_macro_attr: MacroData::new(Arc::new(SyntaxExtension::non_macro_attr(edition))), invocation_parent_scopes: Default::default(), output_macro_rules_scopes: Default::default(), macro_rules_scopes: Default::default(), @@ -1688,11 +1689,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { CStore::from_tcx(self.tcx) } - fn dummy_ext(&self, macro_kind: MacroKind) -> Lrc { + fn dummy_ext(&self, macro_kind: MacroKind) -> Arc { match macro_kind { - MacroKind::Bang => Lrc::clone(&self.dummy_ext_bang), - MacroKind::Derive => Lrc::clone(&self.dummy_ext_derive), - MacroKind::Attr => Lrc::clone(&self.non_macro_attr.ext), + MacroKind::Bang => Arc::clone(&self.dummy_ext_bang), + MacroKind::Derive => Arc::clone(&self.dummy_ext_derive), + MacroKind::Attr => Arc::clone(&self.non_macro_attr.ext), } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 4ff54f474359..aeb9672b7583 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -3,6 +3,7 @@ use std::cell::Cell; use std::mem; +use std::sync::Arc; use rustc_ast::attr::AttributeExt; use rustc_ast::expand::StrippedCfgItem; @@ -10,7 +11,6 @@ use rustc_ast::{self as ast, Crate, NodeId, attr}; use rustc_ast_pretty::pprust; use rustc_attr_parsing::StabilityLevel; use rustc_data_structures::intern::Interned; -use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, StashKey}; use rustc_expand::base::{ DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind, @@ -239,7 +239,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { invoc: &Invocation, eager_expansion_root: LocalExpnId, force: bool, - ) -> Result, Indeterminate> { + ) -> Result, Indeterminate> { let invoc_id = invoc.expansion_data.id; let parent_scope = match self.invocation_parent_scopes.get(&invoc_id) { Some(parent_scope) => *parent_scope, @@ -529,7 +529,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { force: bool, deleg_impl: Option, invoc_in_mod_inert_attr: Option, - ) -> Result<(Lrc, Res), Indeterminate> { + ) -> Result<(Arc, Res), Indeterminate> { let (ext, res) = match self.resolve_macro_or_delegation_path( path, Some(kind), @@ -682,7 +682,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { trace: bool, force: bool, ignore_import: Option>, - ) -> Result<(Option>, Res), Determinacy> { + ) -> Result<(Option>, Res), Determinacy> { self.resolve_macro_or_delegation_path( path, kind, @@ -705,7 +705,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { deleg_impl: Option, invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>, ignore_import: Option>, - ) -> Result<(Option>, Res), Determinacy> { + ) -> Result<(Option>, Res), Determinacy> { let path_span = ast_path.span; let mut path = Segment::from_path(ast_path); @@ -788,11 +788,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(impl_def_id) => match res { def::Res::Def(DefKind::Trait, def_id) => { let edition = self.tcx.sess.edition(); - Some(Lrc::new(SyntaxExtension::glob_delegation(def_id, impl_def_id, edition))) + Some(Arc::new(SyntaxExtension::glob_delegation(def_id, impl_def_id, edition))) } _ => None, }, - None => self.get_macro(res).map(|macro_data| Lrc::clone(¯o_data.ext)), + None => self.get_macro(res).map(|macro_data| Arc::clone(¯o_data.ext)), }; Ok((ext, res)) } @@ -1130,7 +1130,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - MacroData { ext: Lrc::new(ext), rule_spans, macro_rules: macro_def.macro_rules } + MacroData { ext: Arc::new(ext), rule_spans, macro_rules: macro_def.macro_rules } } fn path_accessible( diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 52920e0372e2..4762281c329a 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -427,7 +427,7 @@ impl CheckCfg { Some(values_target_os), Some(values_target_pointer_width), Some(values_target_vendor), - ] = self.expecteds.get_many_mut(VALUES) + ] = self.expecteds.get_disjoint_mut(VALUES) else { panic!("unable to get all the check-cfg values buckets"); }; diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 924350b8cbcd..112adde3740b 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -2,7 +2,6 @@ #![allow(internal_features)] #![feature(iter_intersperse)] #![feature(let_chains)] -#![feature(map_many_mut)] #![feature(rustc_attrs)] // To generate CodegenOptionsTargetModifiers and UnstableOptionsTargetModifiers enums // with macro_rules, it is necessary to use recursive mechanic ("Incremental TT Munchers"). diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index abfd3efc6117..d4db05ce139f 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -2,11 +2,12 @@ //! It also serves as an input to the parser itself. use std::str; +use std::sync::Arc; use rustc_ast::attr::AttrIdGenerator; use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; -use rustc_data_structures::sync::{AppendOnlyVec, Lock, Lrc}; +use rustc_data_structures::sync::{AppendOnlyVec, Lock}; use rustc_errors::emitter::{HumanEmitter, SilentEmitter, stderr_destination}; use rustc_errors::{ ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan, @@ -218,7 +219,7 @@ pub struct ParseSess { /// should be. Useful to avoid bad tokenization when encountering emoji. We group them to /// provide a single error per unique incorrect identifier. pub bad_unicode_identifiers: Lock>>, - source_map: Lrc, + source_map: Arc, pub buffered_lints: Lock>, /// Contains the spans of block expressions that could have been incomplete based on the /// operation token that followed it, but that the parser cannot identify without further @@ -243,16 +244,16 @@ impl ParseSess { /// Used for testing. pub fn new(locale_resources: Vec<&'static str>) -> Self { let fallback_bundle = fallback_fluent_bundle(locale_resources, false); - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let emitter = Box::new( HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle) - .sm(Some(Lrc::clone(&sm))), + .sm(Some(Arc::clone(&sm))), ); let dcx = DiagCtxt::new(emitter); ParseSess::with_dcx(dcx, sm) } - pub fn with_dcx(dcx: DiagCtxt, source_map: Lrc) -> Self { + pub fn with_dcx(dcx: DiagCtxt, source_map: Arc) -> Self { Self { dcx, unstable_features: UnstableFeatures::from_environment(None), @@ -281,7 +282,7 @@ impl ParseSess { emit_fatal_diagnostic: bool, ) -> Self { let fallback_bundle = fallback_fluent_bundle(locale_resources, false); - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let fatal_emitter = Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle)); let dcx = DiagCtxt::new(Box::new(SilentEmitter { @@ -298,8 +299,8 @@ impl ParseSess { &self.source_map } - pub fn clone_source_map(&self) -> Lrc { - Lrc::clone(&self.source_map) + pub fn clone_source_map(&self) -> Arc { + Arc::clone(&self.source_map) } pub fn buffer_lint( diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index c9bb42bdfa1e..0851e859a0fe 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -9,9 +9,7 @@ use std::{env, fmt, io}; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; -use rustc_data_structures::sync::{ - DynSend, DynSync, Lock, Lrc, MappedReadGuard, ReadGuard, RwLock, -}; +use rustc_data_structures::sync::{DynSend, DynSync, Lock, MappedReadGuard, ReadGuard, RwLock}; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; use rustc_errors::codes::*; use rustc_errors::emitter::{ @@ -138,8 +136,8 @@ pub struct Session { pub target: Target, pub host: Target, pub opts: config::Options, - pub host_tlib_path: Lrc, - pub target_tlib_path: Lrc, + pub host_tlib_path: Arc, + pub target_tlib_path: Arc, pub psess: ParseSess, pub sysroot: PathBuf, /// Input, input file path and output file path to this compilation process. @@ -154,7 +152,7 @@ pub struct Session { pub code_stats: CodeStats, /// This only ever stores a `LintStore` but we don't want a dependency on that type here. - pub lint_store: Option>, + pub lint_store: Option>, /// Cap lint level specified by a driver specifically. pub driver_lint_caps: FxHashMap, @@ -885,8 +883,8 @@ impl Session { #[allow(rustc::bad_opt_access)] fn default_emitter( sopts: &config::Options, - source_map: Lrc, - bundle: Option>, + source_map: Arc, + bundle: Option>, fallback_bundle: LazyFallbackBundle, ) -> Box { let macro_backtrace = sopts.unstable_opts.macro_backtrace; @@ -967,10 +965,9 @@ fn default_emitter( #[allow(rustc::bad_opt_access)] #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn build_session( - early_dcx: EarlyDiagCtxt, sopts: config::Options, io: CompilerIO, - bundle: Option>, + bundle: Option>, registry: rustc_errors::registry::Registry, fluent_resources: Vec<&'static str>, driver_lint_caps: FxHashMap, @@ -992,20 +989,12 @@ pub fn build_session( let cap_lints_allow = sopts.lint_cap.is_some_and(|cap| cap == lint::Allow); let can_emit_warnings = !(warnings_allow || cap_lints_allow); - let host_triple = TargetTuple::from_tuple(config::host_tuple()); - let (host, target_warnings) = Target::search(&host_triple, &sysroot).unwrap_or_else(|e| { - early_dcx.early_fatal(format!("Error loading host specification: {e}")) - }); - for warning in target_warnings.warning_messages() { - early_dcx.early_warn(warning) - } - let fallback_bundle = fallback_fluent_bundle( fluent_resources, sopts.unstable_opts.translate_directionality_markers, ); let source_map = rustc_span::source_map::get_source_map().unwrap(); - let emitter = default_emitter(&sopts, Lrc::clone(&source_map), bundle, fallback_bundle); + let emitter = default_emitter(&sopts, Arc::clone(&source_map), bundle, fallback_bundle); let mut dcx = DiagCtxt::new(emitter) .with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)) @@ -1014,9 +1003,12 @@ pub fn build_session( dcx = dcx.with_ice_file(ice_file); } - // Now that the proper handler has been constructed, drop early_dcx to - // prevent accidental use. - drop(early_dcx); + let host_triple = TargetTuple::from_tuple(config::host_tuple()); + let (host, target_warnings) = Target::search(&host_triple, &sysroot) + .unwrap_or_else(|e| dcx.handle().fatal(format!("Error loading host specification: {e}"))); + for warning in target_warnings.warning_messages() { + dcx.handle().warn(warning) + } let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.unstable_opts.self_profile { @@ -1045,13 +1037,13 @@ pub fn build_session( let host_triple = config::host_tuple(); let target_triple = sopts.target_triple.tuple(); - let host_tlib_path = Lrc::new(SearchPath::from_sysroot_and_triple(&sysroot, host_triple)); + let host_tlib_path = Arc::new(SearchPath::from_sysroot_and_triple(&sysroot, host_triple)); let target_tlib_path = if host_triple == target_triple { // Use the same `SearchPath` if host and target triple are identical to avoid unnecessary // rescanning of the target lib path and an unnecessary allocation. - Lrc::clone(&host_tlib_path) + Arc::clone(&host_tlib_path) } else { - Lrc::new(SearchPath::from_sysroot_and_triple(&sysroot, target_triple)) + Arc::new(SearchPath::from_sysroot_and_triple(&sysroot, target_triple)) }; let prof = SelfProfilerRef::new( @@ -1446,7 +1438,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box { config::ErrorOutputType::Json { pretty, json_rendered, color_config } => { Box::new(JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), - Some(Lrc::new(SourceMap::new(FilePathMapping::empty()))), + Some(Arc::new(SourceMap::new(FilePathMapping::empty()))), fallback_bundle, pretty, json_rendered, diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 3bc896dd7efc..50cf605ba2a8 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -10,7 +10,7 @@ use rustc_span::Symbol; use stable_mir::abi::Layout; use stable_mir::mir::alloc::AllocId; use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; -use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety, UnOp}; +use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, RawPtrKind, Safety, UnOp}; use stable_mir::ty::{ Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, @@ -226,6 +226,18 @@ impl RustcInternal for Movability { } } +impl RustcInternal for RawPtrKind { + type T<'tcx> = rustc_middle::mir::RawPtrKind; + + fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + match self { + RawPtrKind::Mut => rustc_middle::mir::RawPtrKind::Mut, + RawPtrKind::Const => rustc_middle::mir::RawPtrKind::Const, + RawPtrKind::FakeForPtrMetadata => rustc_middle::mir::RawPtrKind::FakeForPtrMetadata, + } + } +} + impl RustcInternal for FnSig { type T<'tcx> = rustc_ty::FnSig<'tcx>; diff --git a/compiler/rustc_span/src/caching_source_map_view.rs b/compiler/rustc_span/src/caching_source_map_view.rs index 9f977b98b82b..d8a4cc2f2e24 100644 --- a/compiler/rustc_span/src/caching_source_map_view.rs +++ b/compiler/rustc_span/src/caching_source_map_view.rs @@ -1,6 +1,5 @@ use std::ops::Range; - -use rustc_data_structures::sync::Lrc; +use std::sync::Arc; use crate::source_map::SourceMap; use crate::{BytePos, Pos, RelativeBytePos, SourceFile, SpanData}; @@ -22,7 +21,7 @@ struct CacheEntry { // misses for these rare positions. A line lookup for the position via `SourceMap::lookup_line` // after a cache miss will produce the last line number, as desired. line: Range, - file: Lrc, + file: Arc, file_index: usize, } @@ -30,7 +29,7 @@ impl CacheEntry { #[inline] fn update( &mut self, - new_file_and_idx: Option<(Lrc, usize)>, + new_file_and_idx: Option<(Arc, usize)>, pos: BytePos, time_stamp: usize, ) { @@ -63,7 +62,7 @@ pub struct CachingSourceMapView<'sm> { impl<'sm> CachingSourceMapView<'sm> { pub fn new(source_map: &'sm SourceMap) -> CachingSourceMapView<'sm> { let files = source_map.files(); - let first_file = Lrc::clone(&files[0]); + let first_file = Arc::clone(&files[0]); let entry = CacheEntry { time_stamp: 0, line_number: 0, @@ -82,7 +81,7 @@ impl<'sm> CachingSourceMapView<'sm> { pub fn byte_pos_to_line_and_col( &mut self, pos: BytePos, - ) -> Option<(Lrc, usize, RelativeBytePos)> { + ) -> Option<(Arc, usize, RelativeBytePos)> { self.time_stamp += 1; // Check if the position is in one of the cached lines @@ -92,7 +91,7 @@ impl<'sm> CachingSourceMapView<'sm> { cache_entry.touch(self.time_stamp); let col = RelativeBytePos(pos.to_u32() - cache_entry.line.start.to_u32()); - return Some((Lrc::clone(&cache_entry.file), cache_entry.line_number, col)); + return Some((Arc::clone(&cache_entry.file), cache_entry.line_number, col)); } // No cache hit ... @@ -109,13 +108,13 @@ impl<'sm> CachingSourceMapView<'sm> { cache_entry.update(new_file_and_idx, pos, self.time_stamp); let col = RelativeBytePos(pos.to_u32() - cache_entry.line.start.to_u32()); - Some((Lrc::clone(&cache_entry.file), cache_entry.line_number, col)) + Some((Arc::clone(&cache_entry.file), cache_entry.line_number, col)) } pub fn span_data_to_lines_and_cols( &mut self, span_data: &SpanData, - ) -> Option<(Lrc, usize, BytePos, usize, BytePos)> { + ) -> Option<(Arc, usize, BytePos, usize, BytePos)> { self.time_stamp += 1; // Check if lo and hi are in the cached lines. @@ -133,7 +132,7 @@ impl<'sm> CachingSourceMapView<'sm> { } ( - Lrc::clone(&lo.file), + Arc::clone(&lo.file), lo.line_number, span_data.lo - lo.line.start, hi.line_number, @@ -181,7 +180,7 @@ impl<'sm> CachingSourceMapView<'sm> { lo.update(new_file_and_idx, span_data.lo, self.time_stamp); if !lo.line.contains(&span_data.hi) { - let new_file_and_idx = Some((Lrc::clone(&lo.file), lo.file_index)); + let new_file_and_idx = Some((Arc::clone(&lo.file), lo.file_index)); let next_oldest = self.oldest_cache_entry_index_avoid(oldest); let hi = &mut self.line_cache[next_oldest]; hi.update(new_file_and_idx, span_data.hi, self.time_stamp); @@ -227,7 +226,7 @@ impl<'sm> CachingSourceMapView<'sm> { assert_eq!(lo.file_index, hi.file_index); Some(( - Lrc::clone(&lo.file), + Arc::clone(&lo.file), lo.line_number, span_data.lo - lo.line.start, hi.line_number, @@ -271,13 +270,13 @@ impl<'sm> CachingSourceMapView<'sm> { oldest } - fn file_for_position(&self, pos: BytePos) -> Option<(Lrc, usize)> { + fn file_for_position(&self, pos: BytePos) -> Option<(Arc, usize)> { if !self.source_map.files().is_empty() { let file_idx = self.source_map.lookup_source_file_idx(pos); let file = &self.source_map.files()[file_idx]; if file_contains(file, pos) { - return Some((Lrc::clone(file), file_idx)); + return Some((Arc::clone(file), file_idx)); } } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index dbbbb5077cb5..2910bcdf51d9 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -29,11 +29,12 @@ use std::collections::hash_map::Entry; use std::collections::hash_set::Entry as SetEntry; use std::fmt; use std::hash::Hash; +use std::sync::Arc; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{Hash64, HashStable, HashingControls, StableHasher}; -use rustc_data_structures::sync::{Lock, Lrc, WorkerLocal}; +use rustc_data_structures::sync::{Lock, WorkerLocal}; use rustc_data_structures::unhash::UnhashMap; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; @@ -904,7 +905,7 @@ impl Span { /// allowed inside this span. pub fn mark_with_reason( self, - allow_internal_unstable: Option>, + allow_internal_unstable: Option>, reason: DesugaringKind, edition: Edition, ctx: impl HashStableContext, @@ -959,7 +960,7 @@ pub struct ExpnData { /// List of `#[unstable]`/feature-gated features that the macro is allowed to use /// internally without forcing the whole crate to opt-in /// to them. - pub allow_internal_unstable: Option>, + pub allow_internal_unstable: Option>, /// Edition of the crate in which the macro is defined. pub edition: Edition, /// The `DefId` of the macro being invoked, @@ -985,7 +986,7 @@ impl ExpnData { parent: ExpnId, call_site: Span, def_site: Span, - allow_internal_unstable: Option>, + allow_internal_unstable: Option>, edition: Edition, macro_def_id: Option, parent_module: Option, @@ -1037,7 +1038,7 @@ impl ExpnData { kind: ExpnKind, call_site: Span, edition: Edition, - allow_internal_unstable: Lrc<[Symbol]>, + allow_internal_unstable: Arc<[Symbol]>, macro_def_id: Option, parent_module: Option, ) -> ExpnData { diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index b1f9f4a01c54..0e146baef375 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -83,11 +83,12 @@ use std::io::{self, Read}; use std::ops::{Add, Range, Sub}; use std::path::{Path, PathBuf}; use std::str::FromStr; +use std::sync::Arc; use std::{fmt, iter}; use md5::{Digest, Md5}; use rustc_data_structures::stable_hasher::{Hash64, Hash128, HashStable, StableHasher}; -use rustc_data_structures::sync::{FreezeLock, FreezeWriteGuard, Lock, Lrc}; +use rustc_data_structures::sync::{FreezeLock, FreezeWriteGuard, Lock}; use rustc_data_structures::unord::UnordMap; use sha1::Sha1; use sha2::Sha256; @@ -110,7 +111,7 @@ pub struct SessionGlobals { /// The session's source map, if there is one. This field should only be /// used in places where the `Session` is truly not available, such as /// `::fmt`. - source_map: Option>, + source_map: Option>, } impl SessionGlobals { @@ -120,7 +121,7 @@ impl SessionGlobals { span_interner: Lock::new(span_encoding::SpanInterner::default()), metavar_spans: Default::default(), hygiene_data: Lock::new(hygiene::HygieneData::new(edition)), - source_map: sm_inputs.map(|inputs| Lrc::new(SourceMap::with_inputs(inputs))), + source_map: sm_inputs.map(|inputs| Arc::new(SourceMap::with_inputs(inputs))), } } } @@ -1430,7 +1431,7 @@ pub enum ExternalSource { #[derive(PartialEq, Eq, Clone, Debug)] pub enum ExternalSourceKind { /// The external source has been loaded already. - Present(Lrc), + Present(Arc), /// No attempt has been made to load the external source. AbsentOk, /// A failed attempt has been made to load the external source. @@ -1670,7 +1671,7 @@ pub struct SourceFile { /// (e.g., ``). pub name: FileName, /// The complete source code. - pub src: Option>, + pub src: Option>, /// The source code's hash. pub src_hash: SourceFileHash, /// Used to enable cargo to use checksums to check if a crate is fresh rather @@ -1931,7 +1932,7 @@ impl SourceFile { Ok(SourceFile { name, - src: Some(Lrc::new(src)), + src: Some(Arc::new(src)), src_hash, checksum_hash, external_src: FreezeLock::frozen(ExternalSource::Unneeded), @@ -2050,7 +2051,7 @@ impl SourceFile { } = &mut *external_src { *src_kind = if let Some(src) = src { - ExternalSourceKind::Present(Lrc::new(src)) + ExternalSourceKind::Present(Arc::new(src)) } else { ExternalSourceKind::AbsentErr }; @@ -2490,7 +2491,7 @@ impl Decodable for RelativeBytePos { #[derive(Debug, Clone)] pub struct Loc { /// Information about the original source. - pub file: Lrc, + pub file: Arc, /// The (1-based) line number. pub line: usize, /// The (0-based) column offset. @@ -2502,13 +2503,13 @@ pub struct Loc { // Used to be structural records. #[derive(Debug)] pub struct SourceFileAndLine { - pub sf: Lrc, + pub sf: Arc, /// Index of line, starting from 0. pub line: usize, } #[derive(Debug)] pub struct SourceFileAndBytePos { - pub sf: Lrc, + pub sf: Arc, pub pos: BytePos, } @@ -2525,7 +2526,7 @@ pub struct LineInfo { } pub struct FileLines { - pub file: Lrc, + pub file: Arc, pub lines: Vec, } @@ -2591,7 +2592,7 @@ pub trait HashStableContext { fn span_data_to_lines_and_cols( &mut self, span: &SpanData, - ) -> Option<(Lrc, usize, BytePos, usize, BytePos)>; + ) -> Option<(Arc, usize, BytePos, usize, BytePos)>; fn hashing_controls(&self) -> HashingControls; } diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 55e106b661b2..6fdf8e46fec6 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -102,8 +102,8 @@ pub trait FileLoader { fn read_file(&self, path: &Path) -> io::Result; /// Read the contents of a potentially non-UTF-8 file into memory. - /// We don't normalize binary files, so we can start in an Lrc. - fn read_binary_file(&self, path: &Path) -> io::Result>; + /// We don't normalize binary files, so we can start in an Arc. + fn read_binary_file(&self, path: &Path) -> io::Result>; } /// A FileLoader that uses std::fs to load real files. @@ -124,12 +124,12 @@ impl FileLoader for RealFileLoader { fs::read_to_string(path) } - fn read_binary_file(&self, path: &Path) -> io::Result> { + fn read_binary_file(&self, path: &Path) -> io::Result> { let mut file = fs::File::open(path)?; let len = file.metadata()?.len(); - let mut bytes = Lrc::new_uninit_slice(len as usize); - let mut buf = BorrowedBuf::from(Lrc::get_mut(&mut bytes).unwrap()); + let mut bytes = Arc::new_uninit_slice(len as usize); + let mut buf = BorrowedBuf::from(Arc::get_mut(&mut bytes).unwrap()); match file.read_buf_exact(buf.unfilled()) { Ok(()) => {} Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => { @@ -146,9 +146,9 @@ impl FileLoader for RealFileLoader { // But we are not guaranteed to be at the end of the file, because we did not attempt to do // a read with a non-zero-sized buffer and get Ok(0). // So we do small read to a fixed-size buffer. If the read returns no bytes then we're - // already done, and we just return the Lrc we built above. + // already done, and we just return the Arc we built above. // If the read returns bytes however, we just fall back to reading into a Vec then turning - // that into an Lrc, losing our nice peak memory behavior. This fallback code path should + // that into an Arc, losing our nice peak memory behavior. This fallback code path should // be rarely exercised. let mut probe = [0u8; 32]; @@ -172,8 +172,8 @@ impl FileLoader for RealFileLoader { #[derive(Default)] struct SourceMapFiles { - source_files: monotonic::MonotonicVec>, - stable_id_to_source_file: UnhashMap>, + source_files: monotonic::MonotonicVec>, + stable_id_to_source_file: UnhashMap>, } /// Used to construct a `SourceMap` with `SourceMap::with_inputs`. @@ -232,7 +232,7 @@ impl SourceMap { self.file_loader.file_exists(path) } - pub fn load_file(&self, path: &Path) -> io::Result> { + pub fn load_file(&self, path: &Path) -> io::Result> { let src = self.file_loader.read_file(path)?; let filename = path.to_owned().into(); Ok(self.new_source_file(filename, src)) @@ -242,7 +242,7 @@ impl SourceMap { /// /// Unlike `load_file`, guarantees that no normalization like BOM-removal /// takes place. - pub fn load_binary_file(&self, path: &Path) -> io::Result<(Lrc<[u8]>, Span)> { + pub fn load_binary_file(&self, path: &Path) -> io::Result<(Arc<[u8]>, Span)> { let bytes = self.file_loader.read_binary_file(path)?; // We need to add file to the `SourceMap`, so that it is present @@ -265,14 +265,14 @@ impl SourceMap { // By returning a `MonotonicVec`, we ensure that consumers cannot invalidate // any existing indices pointing into `files`. - pub fn files(&self) -> MappedReadGuard<'_, monotonic::MonotonicVec>> { + pub fn files(&self) -> MappedReadGuard<'_, monotonic::MonotonicVec>> { ReadGuard::map(self.files.borrow(), |files| &files.source_files) } pub fn source_file_by_stable_id( &self, stable_id: StableSourceFileId, - ) -> Option> { + ) -> Option> { self.files.borrow().stable_id_to_source_file.get(&stable_id).cloned() } @@ -280,7 +280,7 @@ impl SourceMap { &self, file_id: StableSourceFileId, mut file: SourceFile, - ) -> Result, OffsetOverflowError> { + ) -> Result, OffsetOverflowError> { let mut files = self.files.borrow_mut(); file.start_pos = BytePos(if let Some(last_file) = files.source_files.last() { @@ -291,9 +291,9 @@ impl SourceMap { 0 }); - let file = Lrc::new(file); - files.source_files.push(Lrc::clone(&file)); - files.stable_id_to_source_file.insert(file_id, Lrc::clone(&file)); + let file = Arc::new(file); + files.source_files.push(Arc::clone(&file)); + files.stable_id_to_source_file.insert(file_id, Arc::clone(&file)); Ok(file) } @@ -301,7 +301,7 @@ impl SourceMap { /// Creates a new `SourceFile`. /// If a file already exists in the `SourceMap` with the same ID, that file is returned /// unmodified. - pub fn new_source_file(&self, filename: FileName, src: String) -> Lrc { + pub fn new_source_file(&self, filename: FileName, src: String) -> Arc { self.try_new_source_file(filename, src).unwrap_or_else(|OffsetOverflowError| { eprintln!( "fatal error: rustc does not support text files larger than {} bytes", @@ -315,7 +315,7 @@ impl SourceMap { &self, filename: FileName, src: String, - ) -> Result, OffsetOverflowError> { + ) -> Result, OffsetOverflowError> { // Note that filename may not be a valid path, eg it may be `` etc, // but this is okay because the directory determined by `path.pop()` will // be empty, so the working directory will be used. @@ -353,7 +353,7 @@ impl SourceMap { multibyte_chars: Vec, normalized_pos: Vec, metadata_index: u32, - ) -> Lrc { + ) -> Arc { let source_len = RelativeBytePos::from_u32(source_len); let source_file = SourceFile { @@ -393,9 +393,9 @@ impl SourceMap { } /// Return the SourceFile that contains the given `BytePos` - pub fn lookup_source_file(&self, pos: BytePos) -> Lrc { + pub fn lookup_source_file(&self, pos: BytePos) -> Arc { let idx = self.lookup_source_file_idx(pos); - Lrc::clone(&(*self.files.borrow().source_files)[idx]) + Arc::clone(&(*self.files.borrow().source_files)[idx]) } /// Looks up source information about a `BytePos`. @@ -406,7 +406,7 @@ impl SourceMap { } /// If the corresponding `SourceFile` is empty, does not return a line number. - pub fn lookup_line(&self, pos: BytePos) -> Result> { + pub fn lookup_line(&self, pos: BytePos) -> Result> { let f = self.lookup_source_file(pos); let pos = f.relative_position(pos); @@ -441,7 +441,7 @@ impl SourceMap { pub fn span_to_location_info( &self, sp: Span, - ) -> (Option>, usize, usize, usize, usize) { + ) -> (Option>, usize, usize, usize, usize) { if self.files.borrow().source_files.is_empty() || sp.is_dummy() { return (None, 0, 0, 0, 0); } @@ -477,7 +477,7 @@ impl SourceMap { if lo != hi { return true; } - let f = Lrc::clone(&(*self.files.borrow().source_files)[lo]); + let f = Arc::clone(&(*self.files.borrow().source_files)[lo]); let lo = f.relative_position(sp.lo()); let hi = f.relative_position(sp.hi()); f.lookup_line(lo) != f.lookup_line(hi) @@ -998,12 +998,12 @@ impl SourceMap { } } - pub fn get_source_file(&self, filename: &FileName) -> Option> { + pub fn get_source_file(&self, filename: &FileName) -> Option> { // Remap filename before lookup let filename = self.path_mapping().map_filename_prefix(filename).0; for sf in self.files.borrow().source_files.iter() { if filename == sf.name { - return Some(Lrc::clone(&sf)); + return Some(Arc::clone(&sf)); } } None @@ -1012,7 +1012,7 @@ impl SourceMap { /// For a global `BytePos`, computes the local offset within the containing `SourceFile`. pub fn lookup_byte_offset(&self, bpos: BytePos) -> SourceFileAndBytePos { let idx = self.lookup_source_file_idx(bpos); - let sf = Lrc::clone(&(*self.files.borrow().source_files)[idx]); + let sf = Arc::clone(&(*self.files.borrow().source_files)[idx]); let offset = bpos - sf.start_pos; SourceFileAndBytePos { sf, pos: offset } } @@ -1082,7 +1082,7 @@ impl SourceMap { } } -pub fn get_source_map() -> Option> { +pub fn get_source_map() -> Option> { with_session_globals(|session_globals| session_globals.source_map.clone()) } diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 5b39706f3ade..957f55e39138 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -538,7 +538,7 @@ fn test_next_point() { #[cfg(target_os = "linux")] #[test] fn read_binary_file_handles_lying_stat() { - // read_binary_file tries to read the contents of a file into an Lrc<[u8]> while + // read_binary_file tries to read the contents of a file into an Arc<[u8]> while // never having two copies of the data in memory at once. This is an optimization // to support include_bytes! with large files. But since Rust allocators are // sensitive to alignment, our implementation can't be bootstrapped off calling diff --git a/compiler/rustc_target/src/callconv/nvptx64.rs b/compiler/rustc_target/src/callconv/nvptx64.rs index 2e8b16d3a938..c64164372a11 100644 --- a/compiler/rustc_target/src/callconv/nvptx64.rs +++ b/compiler/rustc_target/src/callconv/nvptx64.rs @@ -1,5 +1,5 @@ use super::{ArgAttribute, ArgAttributes, ArgExtension, CastTarget}; -use crate::abi::call::{ArgAbi, FnAbi, PassMode, Reg, Size, Uniform}; +use crate::abi::call::{ArgAbi, FnAbi, Reg, Size, Uniform}; use crate::abi::{HasDataLayout, TyAbiInterface}; fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { @@ -53,21 +53,37 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - if matches!(arg.mode, PassMode::Pair(..)) && (arg.layout.is_adt() || arg.layout.is_tuple()) { - let align_bytes = arg.layout.align.abi.bytes(); + match arg.mode { + super::PassMode::Ignore | super::PassMode::Direct(_) => return, + super::PassMode::Pair(_, _) => {} + super::PassMode::Cast { .. } => unreachable!(), + super::PassMode::Indirect { .. } => {} + } - let unit = match align_bytes { - 1 => Reg::i8(), - 2 => Reg::i16(), - 4 => Reg::i32(), - 8 => Reg::i64(), - 16 => Reg::i128(), - _ => unreachable!("Align is given as power of 2 no larger than 16 bytes"), - }; - arg.cast_to(Uniform::new(unit, Size::from_bytes(2 * align_bytes))); + // FIXME only allow structs and wide pointers here + // panic!( + // "`extern \"ptx-kernel\"` doesn't allow passing types other than primitives and structs" + // ); + + let align_bytes = arg.layout.align.abi.bytes(); + + let unit = match align_bytes { + 1 => Reg::i8(), + 2 => Reg::i16(), + 4 => Reg::i32(), + 8 => Reg::i64(), + 16 => Reg::i128(), + _ => unreachable!("Align is given as power of 2 no larger than 16 bytes"), + }; + if arg.layout.size.bytes() / align_bytes == 1 { + // Make sure we pass the struct as array at the LLVM IR level and not as a single integer. + arg.cast_to(CastTarget { + prefix: [Some(unit), None, None, None, None, None, None, None], + rest: Uniform::new(unit, Size::ZERO), + attrs: ArgAttributes::new(), + }); } else { - // FIXME: find a better way to do this. See https://github.com/rust-lang/rust/issues/117271. - arg.make_direct_deprecated(); + arg.cast_to(Uniform::new(unit, arg.layout.size)); } } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index abb794934324..58d8a3a62547 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -43,8 +43,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< self.0.tcx } - type Span = Span; - fn build_with_canonical( interner: TyCtxt<'tcx>, canonical: &CanonicalQueryInput<'tcx, V>, @@ -147,9 +145,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn instantiate_canonical_var_with_infer( &self, cv_info: CanonicalVarInfo<'tcx>, + span: Span, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> ty::GenericArg<'tcx> { - self.0.instantiate_canonical_var(DUMMY_SP, cv_info, universe_map) + self.0.instantiate_canonical_var(span, cv_info, universe_map) } fn insert_hidden_type( @@ -175,11 +174,13 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals); } - fn inject_new_hidden_type_unchecked(&self, key: ty::OpaqueTypeKey<'tcx>, hidden_ty: Ty<'tcx>) { - self.0.inject_new_hidden_type_unchecked(key, ty::OpaqueHiddenType { - ty: hidden_ty, - span: DUMMY_SP, - }) + fn inject_new_hidden_type_unchecked( + &self, + key: ty::OpaqueTypeKey<'tcx>, + hidden_ty: Ty<'tcx>, + span: Span, + ) { + self.0.inject_new_hidden_type_unchecked(key, ty::OpaqueHiddenType { ty: hidden_ty, span }) } fn reset_opaque_types(&self) { diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 0db44eda8470..c238e708ab8f 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -82,7 +82,7 @@ impl<'tcx> ObligationStorage<'tcx> { self.overflowed.extend(ExtractIf::new(&mut self.pending, |o| { let goal = o.clone().into(); let result = <&SolverDelegate<'tcx>>::from(infcx) - .evaluate_root_goal(goal, GenerateProofTree::No) + .evaluate_root_goal(goal, GenerateProofTree::No, o.cause.span) .0; matches!(result, Ok((HasChanged::Yes, _))) })); @@ -163,7 +163,7 @@ where for obligation in self.obligations.unstalled_for_select() { let goal = obligation.clone().into(); let result = <&SolverDelegate<'tcx>>::from(infcx) - .evaluate_root_goal(goal, GenerateProofTree::No) + .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span) .0; self.inspect_evaluated_obligation(infcx, &obligation, &result); let (changed, certainty) = match result { diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index c64bc19835ba..8edd623e5d00 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -88,7 +88,11 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( ) -> FulfillmentError<'tcx> { let (code, refine_obligation) = infcx.probe(|_| { match <&SolverDelegate<'tcx>>::from(infcx) - .evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No) + .evaluate_root_goal( + root_obligation.clone().into(), + GenerateProofTree::No, + root_obligation.cause.span, + ) .0 { Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => { diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 9ba48cd588fa..9f4ee54bd4c6 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -238,7 +238,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { // constraints, we get an ICE if we already applied the constraints // from the chosen candidate. let proof_tree = infcx - .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1) + .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes, span).1) .unwrap(); InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source) } @@ -440,8 +440,11 @@ impl<'tcx> InferCtxt<'tcx> { depth: usize, visitor: &mut V, ) -> V::Result { - let (_, proof_tree) = - <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal(goal, GenerateProofTree::Yes); + let (_, proof_tree) = <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal( + goal, + GenerateProofTree::Yes, + visitor.span(), + ); let proof_tree = proof_tree.unwrap(); visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc)) } diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 000e6a765d35..0024316b877c 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -318,9 +318,17 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe bug!(); }; let source_principal = tcx.instantiate_bound_regions_with_erased( - source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self), + source.principal().unwrap().with_self_ty(tcx, key.self_ty()), ); + // We're monomorphizing a call to a dyn trait object that can never be constructed. + if tcx.instantiate_and_check_impossible_predicates(( + source_principal.def_id, + source_principal.args, + )) { + return 0; + } + let target_principal = ty::ExistentialTraitRef::erase_self_ty(tcx, key); let vtable_segment_callback = { @@ -373,19 +381,27 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( let (source, target) = key; // If the target principal is `None`, we can just return `None`. - let ty::Dynamic(target, _, _) = *target.kind() else { + let ty::Dynamic(target_data, _, _) = *target.kind() else { bug!(); }; - let target_principal = tcx.instantiate_bound_regions_with_erased(target.principal()?); + let target_principal = tcx.instantiate_bound_regions_with_erased(target_data.principal()?); // Given that we have a target principal, it is a bug for there not to be a source principal. - let ty::Dynamic(source, _, _) = *source.kind() else { + let ty::Dynamic(source_data, _, _) = *source.kind() else { bug!(); }; let source_principal = tcx.instantiate_bound_regions_with_erased( - source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self), + source_data.principal().unwrap().with_self_ty(tcx, source), ); + // We're monomorphizing a dyn trait object upcast that can never be constructed. + if tcx.instantiate_and_check_impossible_predicates(( + source_principal.def_id, + source_principal.args, + )) { + return None; + } + let vtable_segment_callback = { let mut vptr_offset = 0; move |segment| { diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index c528179ae0e7..169f3a78c26a 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -489,21 +489,16 @@ fn fn_abi_sanity_check<'tcx>( // have to allow it -- but we absolutely shouldn't let any more targets do // that. (Also see .) // - // The unstable abi `PtxKernel` also uses Direct for now. - // It needs to switch to something else before stabilization can happen. - // (See issue: https://github.com/rust-lang/rust/issues/117271) - // - // And finally the unadjusted ABI is ill specified and uses Direct for all - // args, but unfortunately we need it for calling certain LLVM intrinsics. + // The unadjusted ABI also uses Direct for all args and is ill-specified, + // but unfortunately we need it for calling certain LLVM intrinsics. match spec_abi { ExternAbi::Unadjusted => {} - ExternAbi::PtxKernel => {} ExternAbi::C { unwind: _ } if matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64") => {} _ => { panic!( - "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\ + "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" functions and on wasm\n\ Problematic type: {:#?}", arg.layout, ); diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 4038a1d68fae..cbe49d000b7d 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -373,7 +373,8 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { match pat.kind { thir::PatKind::Constant { value } => value.has_non_region_param(), - thir::PatKind::Range(box thir::PatRange { lo, hi, .. }) => { + thir::PatKind::Range(ref range) => { + let &thir::PatRange { lo, hi, .. } = range.as_ref(); lo.has_non_region_param() || hi.has_non_region_param() } _ => false, diff --git a/compiler/rustc_type_ir/src/data_structures/mod.rs b/compiler/rustc_type_ir/src/data_structures/mod.rs index d9766d91845c..30c67d10d0e8 100644 --- a/compiler/rustc_type_ir/src/data_structures/mod.rs +++ b/compiler/rustc_type_ir/src/data_structures/mod.rs @@ -12,13 +12,11 @@ mod delayed_map; mod impl_ { pub use rustc_data_structures::sso::{SsoHashMap, SsoHashSet}; pub use rustc_data_structures::stack::ensure_sufficient_stack; - pub use rustc_data_structures::sync::Lrc; } #[cfg(not(feature = "nightly"))] mod impl_ { pub use std::collections::{HashMap as SsoHashMap, HashSet as SsoHashSet}; - pub use std::sync::Arc as Lrc; #[inline] pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 711e42ff0558..0bc8b94bbf4c 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -46,12 +46,12 @@ //! ``` use std::mem; +use std::sync::Arc; use rustc_index::{Idx, IndexVec}; use thin_vec::ThinVec; use tracing::{debug, instrument}; -use crate::data_structures::Lrc; use crate::inherent::*; use crate::visit::{TypeVisitable, TypeVisitableExt as _}; use crate::{self as ty, Interner}; @@ -273,28 +273,28 @@ impl, E: TypeFoldable> TypeFoldable for Re } } -impl> TypeFoldable for Lrc { +impl> TypeFoldable for Arc { fn try_fold_with>(mut self, folder: &mut F) -> Result { // We merely want to replace the contained `T`, if at all possible, - // so that we don't needlessly allocate a new `Lrc` or indeed clone + // so that we don't needlessly allocate a new `Arc` or indeed clone // the contained type. unsafe { // First step is to ensure that we have a unique reference to - // the contained type, which `Lrc::make_mut` will accomplish (by - // allocating a new `Lrc` and cloning the `T` only if required). - // This is done *before* casting to `Lrc>` so that + // the contained type, which `Arc::make_mut` will accomplish (by + // allocating a new `Arc` and cloning the `T` only if required). + // This is done *before* casting to `Arc>` so that // panicking during `make_mut` does not leak the `T`. - Lrc::make_mut(&mut self); + Arc::make_mut(&mut self); - // Casting to `Lrc>` is safe because `ManuallyDrop` + // Casting to `Arc>` is safe because `ManuallyDrop` // is `repr(transparent)`. - let ptr = Lrc::into_raw(self).cast::>(); - let mut unique = Lrc::from_raw(ptr); + let ptr = Arc::into_raw(self).cast::>(); + let mut unique = Arc::from_raw(ptr); - // Call to `Lrc::make_mut` above guarantees that `unique` is the + // Call to `Arc::make_mut` above guarantees that `unique` is the // sole reference to the contained value, so we can avoid doing // a checked `get_mut` here. - let slot = Lrc::get_mut(&mut unique).unwrap_unchecked(); + let slot = Arc::get_mut(&mut unique).unwrap_unchecked(); // Semantically move the contained type out from `unique`, fold // it, then move the folded value back into `unique`. Should @@ -304,8 +304,8 @@ impl> TypeFoldable for Lrc { let folded = owned.try_fold_with(folder)?; *slot = mem::ManuallyDrop::new(folded); - // Cast back to `Lrc`. - Ok(Lrc::from_raw(Lrc::into_raw(unique).cast())) + // Cast back to `Arc`. + Ok(Arc::from_raw(Arc::into_raw(unique).cast())) } } } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index a892b88c2c63..c9b636132f80 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -201,17 +201,20 @@ pub trait InferCtxtLike: Sized { &self, sub: ::Region, sup: ::Region, + span: ::Span, ); fn equate_regions( &self, a: ::Region, b: ::Region, + span: ::Span, ); fn register_ty_outlives( &self, ty: ::Ty, r: ::Region, + span: ::Span, ); } diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs index aae5aeb5fb36..fab1a11304d5 100644 --- a/compiler/rustc_type_ir/src/macros.rs +++ b/compiler/rustc_type_ir/src/macros.rs @@ -47,23 +47,16 @@ TrivialTypeTraversalImpls! { u16, u32, u64, - String, + // tidy-alphabetical-start crate::AliasRelationDirection, - crate::AliasTyKind, crate::BoundConstness, crate::DebruijnIndex, - crate::FloatTy, - crate::InferTy, - crate::IntVarValue, crate::PredicatePolarity, - crate::RegionVid, crate::solve::BuiltinImplSource, crate::solve::Certainty, crate::solve::GoalSource, - crate::solve::MaybeCause, - crate::solve::NoSolution, crate::UniverseIndex, crate::Variance, - rustc_ast_ir::Movability, rustc_ast_ir::Mutability, + // tidy-alphabetical-end } diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index ad10ad5af9ca..dc2312b2da3a 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -13,6 +13,7 @@ pub trait RelateExt: InferCtxtLike { lhs: T, variance: ty::Variance, rhs: T, + span: ::Span, ) -> Result< Vec::Predicate>>, TypeError, @@ -23,6 +24,7 @@ pub trait RelateExt: InferCtxtLike { param_env: ::ParamEnv, lhs: T, rhs: T, + span: ::Span, ) -> Result< Vec::Predicate>>, TypeError, @@ -36,12 +38,13 @@ impl RelateExt for Infcx { lhs: T, variance: ty::Variance, rhs: T, + span: ::Span, ) -> Result< Vec::Predicate>>, TypeError, > { let mut relate = - SolverRelating::new(self, StructurallyRelateAliases::No, variance, param_env); + SolverRelating::new(self, StructurallyRelateAliases::No, variance, param_env, span); relate.relate(lhs, rhs)?; Ok(relate.goals) } @@ -51,12 +54,18 @@ impl RelateExt for Infcx { param_env: ::ParamEnv, lhs: T, rhs: T, + span: ::Span, ) -> Result< Vec::Predicate>>, TypeError, > { - let mut relate = - SolverRelating::new(self, StructurallyRelateAliases::Yes, ty::Invariant, param_env); + let mut relate = SolverRelating::new( + self, + StructurallyRelateAliases::Yes, + ty::Invariant, + param_env, + span, + ); relate.relate(lhs, rhs)?; Ok(relate.goals) } @@ -68,6 +77,7 @@ pub struct SolverRelating<'infcx, Infcx, I: Interner> { // Immutable fields. structurally_relate_aliases: StructurallyRelateAliases, param_env: I::ParamEnv, + span: I::Span, // Mutable fields. ambient_variance: ty::Variance, goals: Vec>, @@ -106,10 +116,12 @@ where structurally_relate_aliases: StructurallyRelateAliases, ambient_variance: ty::Variance, param_env: I::ParamEnv, + span: I::Span, ) -> Self { SolverRelating { infcx, structurally_relate_aliases, + span, ambient_variance, param_env, goals: vec![], @@ -241,10 +253,10 @@ where fn regions(&mut self, a: I::Region, b: I::Region) -> RelateResult { match self.ambient_variance { // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a) - ty::Covariant => self.infcx.sub_regions(b, a), + ty::Covariant => self.infcx.sub_regions(b, a, self.span), // Suptype(&'a u8, &'b u8) => Outlives('b: 'a) => SubRegion('a, 'b) - ty::Contravariant => self.infcx.sub_regions(a, b), - ty::Invariant => self.infcx.equate_regions(a, b), + ty::Contravariant => self.infcx.sub_regions(a, b, self.span), + ty::Invariant => self.infcx.equate_regions(a, b, self.span), ty::Bivariant => { unreachable!("Expected bivariance to be handled in relate_with_variance") } diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 0a0749421705..a12e9856304a 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -43,6 +43,7 @@ use std::fmt; use std::ops::ControlFlow; +use std::sync::Arc; use rustc_ast_ir::visit::VisitorResult; use rustc_ast_ir::{try_visit, walk_visitable_list}; @@ -50,7 +51,6 @@ use rustc_index::{Idx, IndexVec}; use smallvec::SmallVec; use thin_vec::ThinVec; -use crate::data_structures::Lrc; use crate::inherent::*; use crate::{self as ty, Interner, TypeFlags}; @@ -167,7 +167,7 @@ impl, E: TypeVisitable> TypeVisitable for } } -impl> TypeVisitable for Lrc { +impl> TypeVisitable for Arc { fn visit_with>(&self, visitor: &mut V) -> V::Result { (**self).visit_with(visitor) } diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 7166f106b8da..4467b37bd451 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -733,28 +733,9 @@ fn udiv_1e19(n: u128) -> (u128, u64) { let quot = if n < 1 << 83 { ((n >> 19) as u64 / (DIV >> 19)) as u128 } else { - u128_mulhi(n, FACTOR) >> 62 + n.widening_mul(FACTOR).1 >> 62 }; let rem = (n - quot * DIV as u128) as u64; (quot, rem) } - -/// Multiply unsigned 128 bit integers, return upper 128 bits of the result -#[inline] -fn u128_mulhi(x: u128, y: u128) -> u128 { - let x_lo = x as u64; - let x_hi = (x >> 64) as u64; - let y_lo = y as u64; - let y_hi = (y >> 64) as u64; - - // handle possibility of overflow - let carry = (x_lo as u128 * y_lo as u128) >> 64; - let m = x_lo as u128 * y_hi as u128 + carry; - let high1 = m >> 64; - - let m_lo = m as u64; - let high2 = (x_hi as u128 * y_lo as u128 + m_lo as u128) >> 64; - - x_hi as u128 * y_hi as u128 + high1 + high2 -} diff --git a/library/core/src/iter/sources/from_fn.rs b/library/core/src/iter/sources/from_fn.rs index 75cc0ffe3c77..1c7e1b30a2f8 100644 --- a/library/core/src/iter/sources/from_fn.rs +++ b/library/core/src/iter/sources/from_fn.rs @@ -1,7 +1,7 @@ use crate::fmt; /// Creates an iterator with the provided closure -/// `F: FnMut() -> Option` as its `[next](Iterator::next)` method. +/// `F: FnMut() -> Option` as its [`next`](Iterator::next) method. /// /// The iterator will yield the `T`s returned from the closure. /// diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 09f898309bd6..f6dcf42ba603 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -387,7 +387,7 @@ unsafe fn swap_if_less(v_base: *mut T, a_pos: usize, b_pos: usize, is_less where F: FnMut(&T, &T) -> bool, { - // SAFETY: the caller must guarantee that `a` and `b` each added to `v_base` yield valid + // SAFETY: the caller must guarantee that `a_pos` and `b_pos` each added to `v_base` yield valid // pointers into `v_base`, and are properly aligned, and part of the same allocation. unsafe { let v_a = v_base.add(a_pos); @@ -404,16 +404,16 @@ where // The equivalent code with a branch would be: // // if should_swap { - // ptr::swap(left, right, 1); + // ptr::swap(v_a, v_b, 1); // } // The goal is to generate cmov instructions here. - let left_swap = if should_swap { v_b } else { v_a }; - let right_swap = if should_swap { v_a } else { v_b }; + let v_a_swap = should_swap.select_unpredictable(v_b, v_a); + let v_b_swap = should_swap.select_unpredictable(v_a, v_b); - let right_swap_tmp = ManuallyDrop::new(ptr::read(right_swap)); - ptr::copy(left_swap, v_a, 1); - ptr::copy_nonoverlapping(&*right_swap_tmp, v_b, 1); + let v_b_swap_tmp = ManuallyDrop::new(ptr::read(v_b_swap)); + ptr::copy(v_a_swap, v_a, 1); + ptr::copy_nonoverlapping(&*v_b_swap_tmp, v_b, 1); } } @@ -640,26 +640,21 @@ pub unsafe fn sort4_stable bool>( // 1, 1 | c b a d let c3 = is_less(&*c, &*a); let c4 = is_less(&*d, &*b); - let min = select(c3, c, a); - let max = select(c4, b, d); - let unknown_left = select(c3, a, select(c4, c, b)); - let unknown_right = select(c4, d, select(c3, b, c)); + let min = c3.select_unpredictable(c, a); + let max = c4.select_unpredictable(b, d); + let unknown_left = c3.select_unpredictable(a, c4.select_unpredictable(c, b)); + let unknown_right = c4.select_unpredictable(d, c3.select_unpredictable(b, c)); // Sort the last two unknown elements. let c5 = is_less(&*unknown_right, &*unknown_left); - let lo = select(c5, unknown_right, unknown_left); - let hi = select(c5, unknown_left, unknown_right); + let lo = c5.select_unpredictable(unknown_right, unknown_left); + let hi = c5.select_unpredictable(unknown_left, unknown_right); ptr::copy_nonoverlapping(min, dst, 1); ptr::copy_nonoverlapping(lo, dst.add(1), 1); ptr::copy_nonoverlapping(hi, dst.add(2), 1); ptr::copy_nonoverlapping(max, dst.add(3), 1); } - - #[inline(always)] - fn select(cond: bool, if_true: *const T, if_false: *const T) -> *const T { - if cond { if_true } else { if_false } - } } /// SAFETY: The caller MUST guarantee that `v_base` is valid for 8 reads and diff --git a/library/panic_unwind/src/hermit.rs b/library/panic_unwind/src/hermit.rs index 8ac827dd9ccb..7e08ad66577f 100644 --- a/library/panic_unwind/src/hermit.rs +++ b/library/panic_unwind/src/hermit.rs @@ -7,14 +7,14 @@ use core::any::Any; pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box { extern "C" { - pub fn __rust_abort() -> !; + fn __rust_abort() -> !; } __rust_abort(); } pub(crate) unsafe fn panic(_data: Box) -> u32 { extern "C" { - pub fn __rust_abort() -> !; + fn __rust_abort() -> !; } __rust_abort(); } diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index d2342d8fd517..5013826cb9b3 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -969,7 +969,6 @@ where /// # Examples /// /// ``` - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); @@ -979,13 +978,13 @@ where /// libraries.insert("Library of Congress".to_string(), 1800); /// /// // Get Athenæum and Bodleian Library - /// let [Some(a), Some(b)] = libraries.get_many_mut([ + /// let [Some(a), Some(b)] = libraries.get_disjoint_mut([ /// "Athenæum", /// "Bodleian Library", /// ]) else { panic!() }; /// /// // Assert values of Athenæum and Library of Congress - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "Library of Congress", /// ]); @@ -998,7 +997,7 @@ where /// ); /// /// // Missing keys result in None - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "New York Public Library", /// ]); @@ -1012,21 +1011,24 @@ where /// ``` /// /// ```should_panic - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); /// libraries.insert("Athenæum".to_string(), 1807); /// /// // Duplicate keys panic! - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "Athenæum", /// ]); /// ``` #[inline] - #[unstable(feature = "map_many_mut", issue = "97601")] - pub fn get_many_mut(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N] + #[doc(alias = "get_many_mut")] + #[stable(feature = "map_many_mut", since = "CURRENT_RUSTC_VERSION")] + pub fn get_disjoint_mut( + &mut self, + ks: [&Q; N], + ) -> [Option<&'_ mut V>; N] where K: Borrow, Q: Hash + Eq, @@ -1040,7 +1042,7 @@ where /// Returns an array of length `N` with the results of each query. `None` will be used if /// the key is missing. /// - /// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`). + /// For a safe alternative see [`get_disjoint_mut`](`HashMap::get_disjoint_mut`). /// /// # Safety /// @@ -1052,7 +1054,6 @@ where /// # Examples /// /// ``` - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); @@ -1062,13 +1063,13 @@ where /// libraries.insert("Library of Congress".to_string(), 1800); /// /// // SAFETY: The keys do not overlap. - /// let [Some(a), Some(b)] = (unsafe { libraries.get_many_unchecked_mut([ + /// let [Some(a), Some(b)] = (unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "Bodleian Library", /// ]) }) else { panic!() }; /// /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_many_unchecked_mut([ + /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "Library of Congress", /// ]) }; @@ -1081,7 +1082,7 @@ where /// ); /// /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_many_unchecked_mut([ + /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "New York Public Library", /// ]) }; @@ -1089,8 +1090,9 @@ where /// assert_eq!(got, [Some(&mut 1807), None]); /// ``` #[inline] - #[unstable(feature = "map_many_mut", issue = "97601")] - pub unsafe fn get_many_unchecked_mut( + #[doc(alias = "get_many_unchecked_mut")] + #[stable(feature = "map_many_mut", since = "CURRENT_RUSTC_VERSION")] + pub unsafe fn get_disjoint_unchecked_mut( &mut self, ks: [&Q; N], ) -> [Option<&'_ mut V>; N] diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index e8cbfe337bcc..1323aba38b7c 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -418,8 +418,13 @@ fn test_creation_flags() { const EXIT_PROCESS_DEBUG_EVENT: u32 = 5; const DBG_EXCEPTION_NOT_HANDLED: u32 = 0x80010001; - let mut child = - Command::new("cmd").creation_flags(DEBUG_PROCESS).stdin(Stdio::piped()).spawn().unwrap(); + let mut child = Command::new("cmd") + .creation_flags(DEBUG_PROCESS) + .stdin(Stdio::piped()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .unwrap(); child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap(); let mut events = 0; let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] }; @@ -486,9 +491,13 @@ fn test_proc_thread_attributes() { } } - let parent = ProcessDropGuard(Command::new("cmd").spawn().unwrap()); + let mut parent = Command::new("cmd"); + parent.stdout(Stdio::null()).stderr(Stdio::null()); + + let parent = ProcessDropGuard(parent.spawn().unwrap()); let mut child_cmd = Command::new("cmd"); + child_cmd.stdout(Stdio::null()).stderr(Stdio::null()); let parent_process_handle = parent.0.as_raw_handle(); diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 42b444464e52..3f58328a5b59 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -310,6 +310,9 @@ impl<'a> Renderer<'a> { match message { Message::Suite(SuiteMessage::Started { test_count }) => { println!("\nrunning {test_count} tests"); + self.benches = vec![]; + self.failures = vec![]; + self.ignored_tests = 0; self.executed_tests = 0; self.terse_tests_in_line = 0; self.tests_count = Some(test_count); diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index 7211b157c694..a158e5b6253c 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.18.2 \ No newline at end of file +0.20.2 \ No newline at end of file diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 58a9eb696665..92458bfaa347 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -50,7 +50,7 @@ runners: - &job-aarch64-linux # Free some disk space to avoid running out of space during the build. free_disk: true - os: ubuntu-24.04-arm + os: ubuntu-22.04-arm - &job-aarch64-linux-8c os: ubuntu-22.04-arm64-8core-32gb diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs index b0f9af1b8d10..14998965ac86 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs @@ -15,9 +15,9 @@ extern crate rustc_span; use std::io; use std::path::Path; +use std::sync::Arc; use rustc_ast_pretty::pprust::item_to_string; -use rustc_data_structures::sync::Lrc; use rustc_driver::{Compilation, run_compiler}; use rustc_interface::interface::{Compiler, Config}; use rustc_middle::ty::TyCtxt; @@ -43,7 +43,7 @@ fn main() { } } - fn read_binary_file(&self, _path: &Path) -> io::Result> { + fn read_binary_file(&self, _path: &Path) -> io::Result> { Err(io::Error::other("oops")) } } diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs index 8766a8173446..9fcb16b0fca3 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs @@ -15,9 +15,9 @@ extern crate rustc_span; use std::io; use std::path::Path; +use std::sync::Arc; use rustc_ast_pretty::pprust::item_to_string; -use rustc_data_structures::sync::Lrc; use rustc_driver::{Compilation, run_compiler}; use rustc_interface::interface::{Compiler, Config}; use rustc_middle::ty::TyCtxt; @@ -43,7 +43,7 @@ fn main() { } } - fn read_binary_file(&self, _path: &Path) -> io::Result> { + fn read_binary_file(&self, _path: &Path) -> io::Result> { Err(io::Error::other("oops")) } } diff --git a/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md b/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md index bd2b02529456..7b98bc62116b 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md @@ -54,7 +54,7 @@ Lints are registered via the [`LintStore::register_lint`] function. This should happen just once for any lint, or an ICE will occur. Once the registration is complete, we "freeze" the lint store by placing it in -an `Lrc`. +an `Arc`. Lint passes are registered separately into one of the categories (pre-expansion, early, late, late module). Passes are registered as a closure diff --git a/src/doc/rustc-dev-guide/src/parallel-rustc.md b/src/doc/rustc-dev-guide/src/parallel-rustc.md index 2dae95a3491d..44c78a125f4e 100644 --- a/src/doc/rustc-dev-guide/src/parallel-rustc.md +++ b/src/doc/rustc-dev-guide/src/parallel-rustc.md @@ -46,7 +46,6 @@ are implemented differently depending on whether `parallel-compiler` is true. | data structure | parallel | non-parallel | | -------------------------------- | --------------------------------------------------- | ------------ | -| Lrc | std::sync::Arc | std::rc::Rc | | Weak | std::sync::Weak | std::rc::Weak | | Atomic{Bool}/{Usize}/{U32}/{U64} | std::sync::atomic::Atomic{Bool}/{Usize}/{U32}/{U64} | (std::cell::Cell) | | OnceCell | std::sync::OnceLock | std::cell::OnceCell | diff --git a/src/doc/rustc/src/platform-support/apple-darwin.md b/src/doc/rustc/src/platform-support/apple-darwin.md index 17ea225805b5..dba2c4b2aaf0 100644 --- a/src/doc/rustc/src/platform-support/apple-darwin.md +++ b/src/doc/rustc/src/platform-support/apple-darwin.md @@ -30,6 +30,18 @@ The current default deployment target for `rustc` can be retrieved with [deployment target]: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html [rustc-print]: ../command-line-arguments.md#option-print +### Host tooling + +The minimum supported OS versions for the host tooling (`rustc`, `cargo`, +etc.) are currently the same as for applications, namely 10.12 on x86 and 11.0 +on ARM64. +The minimum supported Xcode version is 9.2. + +Building from source likely requires that you can build LLVM from source too, +which [currently][llvm-os] requires Xcode 10.0 and macOS 10.13 (for LLVM 19). + +[llvm-os]: https://releases.llvm.org/19.1.0/docs/GettingStarted.html#host-c-toolchain-both-compiler-and-standard-library + ### Binary format The default binary format is Mach-O, the executable format used on Apple's diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 6af35157a43d..5c146da03acc 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -1,8 +1,7 @@ -use std::sync::LazyLock; +use std::sync::{Arc, LazyLock}; use std::{io, mem}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::UnordSet; use rustc_driver::USING_INTERNAL_FEATURES; use rustc_errors::TerminalUrl; @@ -145,7 +144,7 @@ impl<'tcx> DocContext<'tcx> { /// will be created for the `DiagCtxt`. pub(crate) fn new_dcx( error_format: ErrorOutputType, - source_map: Option>, + source_map: Option>, diagnostic_width: Option, unstable_opts: &UnstableOptions, ) -> rustc_errors::DiagCtxt { @@ -173,7 +172,7 @@ pub(crate) fn new_dcx( } ErrorOutputType::Json { pretty, json_rendered, color_config } => { let source_map = source_map.unwrap_or_else(|| { - Lrc::new(source_map::SourceMap::new(source_map::FilePathMapping::empty())) + Arc::new(source_map::SourceMap::new(source_map::FilePathMapping::empty())) }); Box::new( JsonEmitter::new( diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 7bcb9465948d..d89caabefe3e 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -2,9 +2,9 @@ //! runnable, e.g. by adding a `main` function if it doesn't already exist. use std::io; +use std::sync::Arc; use rustc_ast as ast; -use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::stderr_destination; use rustc_errors::{ColorConfig, FatalError}; use rustc_parse::new_parser_from_source_str; @@ -280,7 +280,7 @@ fn parse_source( // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let fallback_bundle = rustc_errors::fallback_fluent_bundle( rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, @@ -474,7 +474,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> Option let filename = FileName::anon_source_code(source); // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let fallback_bundle = rustc_errors::fallback_fluent_bundle( rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index bd292efeb7ea..c4956bfd2b4a 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -1,9 +1,9 @@ //! Doctest functionality used only for doctests in `.rs` source files. use std::env; +use std::sync::Arc; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, intravisit}; use rustc_middle::hir::nested_filter; @@ -17,7 +17,7 @@ use crate::clean::{Attributes, extract_cfg_from_attrs}; use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine}; struct RustCollector { - source_map: Lrc, + source_map: Arc, tests: Vec, cur_path: Vec, position: Span, diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index d9c277c047f2..459bdd991db4 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -1,6 +1,8 @@ //! Validates syntax inside Rust code blocks (\`\`\`rust). -use rustc_data_structures::sync::{Lock, Lrc}; +use std::sync::Arc; + +use rustc_data_structures::sync::Lock; use rustc_errors::emitter::Emitter; use rustc_errors::registry::Registry; use rustc_errors::translation::{Translate, to_fluent_args}; @@ -32,14 +34,14 @@ fn check_rust_syntax( dox: &str, code_block: RustCodeBlock, ) { - let buffer = Lrc::new(Lock::new(Buffer::default())); + let buffer = Arc::new(Lock::new(Buffer::default())); let fallback_bundle = rustc_errors::fallback_fluent_bundle( rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, ); - let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle }; + let emitter = BufferEmitter { buffer: Arc::clone(&buffer), fallback_bundle }; - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); let source = dox[code_block.code].to_owned(); let psess = ParseSess::with_dcx(dcx, sm); @@ -141,7 +143,7 @@ struct Buffer { } struct BufferEmitter { - buffer: Lrc>, + buffer: Arc>, fallback_bundle: LazyFallbackBundle, } diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index bc42c07224e1..fa03c953aa59 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5765,6 +5765,7 @@ Released 2018-09-13 [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_err [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or +[`manual_option_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_option_as_slice [`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns @@ -5773,6 +5774,7 @@ Released 2018-09-13 [`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain [`manual_rotate`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rotate [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic +[`manual_slice_fill`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_fill [`manual_slice_size_calculation`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_size_calculation [`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once [`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat @@ -5954,6 +5956,7 @@ Released 2018-09-13 [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence +[`precedence_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence_bits [`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal [`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr @@ -6026,6 +6029,7 @@ Released 2018-09-13 [`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used +[`return_and_then`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_and_then [`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use [`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges diff --git a/src/tools/clippy/book/src/attribs.md b/src/tools/clippy/book/src/attribs.md index cf99497bc0fc..9b7f59705041 100644 --- a/src/tools/clippy/book/src/attribs.md +++ b/src/tools/clippy/book/src/attribs.md @@ -5,7 +5,7 @@ To do this, Clippy provides attributes that can be applied to items in the 3rd p ## `#[clippy::format_args]` -_Available since Clippy v1.84_ +_Available since Clippy v1.85_ This attribute can be added to a macro that supports `format!`, `println!`, or similar syntax. It tells Clippy that the macro is a formatting macro, and that the arguments to the macro diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index b8f9fff9c613..dab2630a56fc 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -752,6 +752,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) * [`manual_repeat_n`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n) * [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) +* [`manual_slice_fill`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_fill) * [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) * [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) * [`manual_strip`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip) diff --git a/src/tools/clippy/book/src/lints.md b/src/tools/clippy/book/src/lints.md index 442dc63914e9..1dd517976601 100644 --- a/src/tools/clippy/book/src/lints.md +++ b/src/tools/clippy/book/src/lints.md @@ -101,5 +101,18 @@ The `clippy::cargo` group gives you suggestions on how to improve your your crate and are not sure if you have all useful information in your `Cargo.toml`. +## Nursery + +The `clippy::nursery` group contains lints which are buggy or need more work. It is **not** +recommended to enable the whole group, but rather cherry-pick lints that are useful for your +code base and your use case. + +## Deprecated + +The `clippy::deprecated` is empty lints that exist to ensure that `#[allow(lintname)]` still +compiles after the lint was deprecated. Deprecation "removes" lints by removing their +functionality and marking them as deprecated, which may cause further warnings but cannot +cause a compiler error. + [Clippy lint documentation]: https://rust-lang.github.io/rust-clippy/ [Clippy 1.0 RFC]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 552141476f3a..a1591188bee7 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -621,6 +621,7 @@ define_Conf! { manual_rem_euclid, manual_repeat_n, manual_retain, + manual_slice_fill, manual_split_once, manual_str_repeat, manual_strip, diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 35dd986ff614..cc4b26867a20 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -255,8 +255,9 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { let name_camel = to_camel_case(lint.name); let name_upper = lint_name.to_uppercase(); - result.push_str(&if enable_msrv { - formatdoc!( + if enable_msrv { + let _: fmt::Result = writedoc!( + result, r" use clippy_utils::msrvs::{{self, Msrv}}; use clippy_config::Conf; @@ -265,22 +266,24 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { use rustc_session::impl_lint_pass; " - ) + ); } else { - formatdoc!( + let _: fmt::Result = writedoc!( + result, r" {pass_import} use rustc_lint::{{{context_import}, {pass_type}}}; use rustc_session::declare_lint_pass; " - ) - }); + ); + } let _: fmt::Result = writeln!(result, "{}", get_lint_declaration(&name_upper, category)); - result.push_str(&if enable_msrv { - formatdoc!( + if enable_msrv { + let _: fmt::Result = writedoc!( + result, r" pub struct {name_camel} {{ msrv: Msrv, @@ -301,16 +304,17 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { // TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed. // TODO: Update msrv config comment in `clippy_config/src/conf.rs` " - ) + ); } else { - formatdoc!( + let _: fmt::Result = writedoc!( + result, r" declare_lint_pass!({name_camel} => [{name_upper}]); impl {pass_type}{pass_lifetimes} for {name_camel} {{}} " - ) - }); + ); + } result } diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index fc0780f89a7f..b80ee5aac7e7 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -985,17 +985,23 @@ mod tests { Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), ]; let mut expected: HashMap> = HashMap::new(); - expected.insert("group1".to_string(), vec![ - Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), - Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), - ]); - expected.insert("group2".to_string(), vec![Lint::new( - "should_assert_eq2", - "group2", - "\"abc\"", - "module_name", - Range::default(), - )]); + expected.insert( + "group1".to_string(), + vec![ + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), + ], + ); + expected.insert( + "group2".to_string(), + vec![Lint::new( + "should_assert_eq2", + "group2", + "\"abc\"", + "module_name", + Range::default(), + )], + ); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); } } diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index 95c85f250e98..95f64b74044b 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -3,10 +3,10 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_attr_parsing::RustcVersion; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{HirId, Lit}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::symbol; +use rustc_span::{Span, symbol}; use std::f64::consts as f64; declare_clippy_lint! { @@ -73,22 +73,28 @@ impl ApproxConstant { msrv: conf.msrv.clone(), } } +} - fn check_lit(&self, cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) { - match *lit { +impl<'tcx> LateLintPass<'tcx> for ApproxConstant { + fn check_lit(&mut self, cx: &LateContext<'_>, _hir_id: HirId, lit: &Lit, _negated: bool) { + match lit.node { LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty { - FloatTy::F16 => self.check_known_consts(cx, e, s, "f16"), - FloatTy::F32 => self.check_known_consts(cx, e, s, "f32"), - FloatTy::F64 => self.check_known_consts(cx, e, s, "f64"), - FloatTy::F128 => self.check_known_consts(cx, e, s, "f128"), + FloatTy::F16 => self.check_known_consts(cx, lit.span, s, "f16"), + FloatTy::F32 => self.check_known_consts(cx, lit.span, s, "f32"), + FloatTy::F64 => self.check_known_consts(cx, lit.span, s, "f64"), + FloatTy::F128 => self.check_known_consts(cx, lit.span, s, "f128"), }, // FIXME(f16_f128): add `f16` and `f128` when these types become stable. - LitKind::Float(s, LitFloatType::Unsuffixed) => self.check_known_consts(cx, e, s, "f{32, 64}"), + LitKind::Float(s, LitFloatType::Unsuffixed) => self.check_known_consts(cx, lit.span, s, "f{32, 64}"), _ => (), } } - fn check_known_consts(&self, cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) { + extract_msrv_attr!(LateContext); +} + +impl ApproxConstant { + fn check_known_consts(&self, cx: &LateContext<'_>, span: Span, s: symbol::Symbol, module: &str) { let s = s.as_str(); if s.parse::().is_ok() { for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { @@ -96,7 +102,7 @@ impl ApproxConstant { span_lint_and_help( cx, APPROX_CONSTANT, - e.span, + span, format!("approximate value of `{module}::consts::{name}` found"), None, "consider using the constant directly", @@ -110,16 +116,6 @@ impl ApproxConstant { impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]); -impl<'tcx> LateLintPass<'tcx> for ApproxConstant { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Lit(lit) = &e.kind { - self.check_lit(cx, &lit.node, e); - } - } - - extract_msrv_attr!(LateContext); -} - /// Returns `false` if the number of significant figures in `value` are /// less than `min_digits`; otherwise, returns true if `value` is equal /// to `constant`, rounded to the number of digits present in `value`. diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index 3e4bcfbfc190..5a26ba8bf932 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -1,8 +1,8 @@ +use std::sync::Arc; use super::MIXED_ATTRIBUTES_STYLE; use clippy_utils::diagnostics::span_lint; use rustc_ast::{AttrKind, AttrStyle, Attribute}; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync::Lrc; use rustc_lint::{EarlyContext, LintContext}; use rustc_span::source_map::SourceMap; use rustc_span::{SourceFile, Span, Symbol}; @@ -79,7 +79,7 @@ fn lint_mixed_attrs(cx: &EarlyContext<'_>, attrs: &[Attribute]) { ); } -fn attr_in_same_src_as_item(source_map: &SourceMap, item_src: &Lrc, attr_span: Span) -> bool { +fn attr_in_same_src_as_item(source_map: &SourceMap, item_src: &Arc, attr_span: Span) -> bool { let attr_src = source_map.lookup_source_file(attr_span.lo()); - Lrc::ptr_eq(item_src, &attr_src) + Arc::ptr_eq(item_src, &attr_src) } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 86bcf8edd578..9fbeab5bf2e1 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -292,6 +292,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::loops::MANUAL_FIND_INFO, crate::loops::MANUAL_FLATTEN_INFO, crate::loops::MANUAL_MEMCPY_INFO, + crate::loops::MANUAL_SLICE_FILL_INFO, crate::loops::MANUAL_WHILE_LET_SOME_INFO, crate::loops::MISSING_SPIN_LOOP_INFO, crate::loops::MUT_RANGE_BOUND_INFO, @@ -321,6 +322,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO, crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, + crate::manual_option_as_slice::MANUAL_OPTION_AS_SLICE_INFO, crate::manual_range_patterns::MANUAL_RANGE_PATTERNS_INFO, crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO, crate::manual_retain::MANUAL_RETAIN_INFO, @@ -463,6 +465,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::REPEAT_ONCE_INFO, crate::methods::RESULT_FILTER_MAP_INFO, crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, + crate::methods::RETURN_AND_THEN_INFO, crate::methods::SEARCH_IS_SOME_INFO, crate::methods::SEEK_FROM_CURRENT_INFO, crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO, @@ -626,6 +629,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO, crate::pointers_in_nomem_asm_block::POINTERS_IN_NOMEM_ASM_BLOCK_INFO, crate::precedence::PRECEDENCE_INFO, + crate::precedence::PRECEDENCE_BITS_INFO, crate::ptr::CMP_NULL_INFO, crate::ptr::INVALID_NULL_PTR_USAGE_INFO, crate::ptr::MUT_FROM_REF_INFO, diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 123e358d7c3d..233ebe00d8e7 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -291,10 +291,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { && let Some(ty) = use_node.defined_ty(cx) && TyCoercionStability::for_defined_ty(cx, ty, use_node.is_return()).is_deref_stable() { - self.state = Some((State::ExplicitDeref { mutability: None }, StateData { - first_expr: expr, - adjusted_ty, - })); + self.state = Some(( + State::ExplicitDeref { mutability: None }, + StateData { + first_expr: expr, + adjusted_ty, + }, + )); } }, RefOp::Method { mutbl, is_ufcs } @@ -456,10 +459,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { && next_adjust.is_none_or(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_))) && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_))) { - self.state = Some((State::Borrow { mutability }, StateData { - first_expr: expr, - adjusted_ty, - })); + self.state = Some(( + State::Borrow { mutability }, + StateData { + first_expr: expr, + adjusted_ty, + }, + )); } }, _ => {}, @@ -503,10 +509,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let stability = state.stability; report(cx, expr, State::DerefedBorrow(state), data, typeck); if stability.is_deref_stable() { - self.state = Some((State::Borrow { mutability }, StateData { - first_expr: expr, - adjusted_ty, - })); + self.state = Some(( + State::Borrow { mutability }, + StateData { + first_expr: expr, + adjusted_ty, + }, + )); } }, (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => { @@ -531,10 +540,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } else if stability.is_deref_stable() && let Some(parent) = get_parent_expr(cx, expr) { - self.state = Some((State::ExplicitDeref { mutability: None }, StateData { - first_expr: parent, - adjusted_ty, - })); + self.state = Some(( + State::ExplicitDeref { mutability: None }, + StateData { + first_expr: parent, + adjusted_ty, + }, + )); } }, @@ -1124,17 +1136,20 @@ impl<'tcx> Dereferencing<'tcx> { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref - if !matches!(cx.typeck_results().expr_adjustments(e), [ - Adjustment { - kind: Adjust::Deref(_), + if !matches!( + cx.typeck_results().expr_adjustments(e), + [ + Adjustment { + kind: Adjust::Deref(_), + .. + }, + Adjustment { + kind: Adjust::Deref(_), + .. + }, .. - }, - Adjustment { - kind: Adjust::Deref(_), - .. - }, - .. - ]) { + ] + ) { match get_parent_expr(cx, e) { // Field accesses are the same no matter the number of references. Some(Expr { diff --git a/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs index 099194d4e746..6e85c6af642b 100644 --- a/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs +++ b/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs @@ -233,10 +233,13 @@ fn check_gaps(cx: &LateContext<'_>, gaps: &[Gap<'_>]) -> bool { if let Some(owner) = cx.last_node_with_lint_attrs.as_owner() { let def_id = owner.to_def_id(); let def_descr = cx.tcx.def_descr(def_id); - diag.span_label(cx.tcx.def_span(def_id), match kind { - StopKind::Attr => format!("the attribute applies to this {def_descr}"), - StopKind::Doc(_) => format!("the comment documents this {def_descr}"), - }); + diag.span_label( + cx.tcx.def_span(def_id), + match kind { + StopKind::Attr => format!("the attribute applies to this {def_descr}"), + StopKind::Doc(_) => format!("the comment documents this {def_descr}"), + }, + ); } diag.multipart_suggestion_with_style( diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs index 9ba2723157ad..ce5beab24bfa 100644 --- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs +++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs @@ -1,10 +1,10 @@ use std::ops::Range; use std::{io, thread}; +use std::sync::Arc; use crate::doc::{NEEDLESS_DOCTEST_MAIN, TEST_ATTR_IN_DOCTEST}; use clippy_utils::diagnostics::span_lint; use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind}; -use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::HumanEmitter; use rustc_errors::{Diag, DiagCtxt}; use rustc_lint::LateContext; @@ -46,8 +46,8 @@ pub fn check( rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false); let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); - #[expect(clippy::arc_with_non_send_sync)] // `Lrc` is expected by with_dcx - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + #[expect(clippy::arc_with_non_send_sync)] // `Arc` is expected by with_dcx + let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let psess = ParseSess::with_dcx(dcx, sm); let mut parser = match new_parser_from_source_str(&psess, filename, code) { diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs index 29deaaf3bc7a..a7670ffce887 100644 --- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs @@ -6,6 +6,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; use rustc_span::Symbol; +use std::fmt::Write; declare_clippy_lint! { /// ### What it does @@ -183,7 +184,7 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix help_str.push_str("either of "); } - help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix))); + write!(help_str, "`{ty}::{}` ", lint.as_name(prefix)).unwrap(); if i != len && !only_one { help_str.push_str("or "); diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 52b699274bbb..f90bf9157aad 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher::VecArgs; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; use clippy_utils::{ get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, }; use rustc_errors::Applicability; -use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Safety, TyKind}; +use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ @@ -239,6 +239,11 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx && !cx.tcx.has_attr(method_def_id, sym::track_caller) && check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder()) { + let mut app = Applicability::MachineApplicable; + let generic_args = match path.args.and_then(GenericArgs::span_ext) { + Some(span) => format!("::{}", snippet_with_applicability(cx, span, "<..>", &mut app)), + None => String::new(), + }; span_lint_and_then( cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, @@ -251,8 +256,8 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx diag.span_suggestion( expr.span, "replace the closure with the method itself", - format!("{}::{}", type_name, path.ident.name), - Applicability::MachineApplicable, + format!("{}::{}{}", type_name, path.ident.name, generic_args), + app, ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs index 36567b3ded0b..1d3ae8949441 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs @@ -124,7 +124,9 @@ struct NestingVisitor<'conf, 'cx> { impl NestingVisitor<'_, '_> { fn check_indent(&mut self, span: Span, id: NodeId) -> bool { - if self.nest_level > self.conf.excessive_nesting_threshold && !span.in_external_macro(self.cx.sess().source_map()) { + if self.nest_level > self.conf.excessive_nesting_threshold + && !span.in_external_macro(self.cx.sess().source_map()) + { self.conf.nodes.insert(id); return true; diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs index 8b1f86cbb913..68cc50f39391 100644 --- a/src/tools/clippy/clippy_lints/src/format_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs @@ -11,7 +11,7 @@ declare_clippy_lint! { /// Detects cases where the result of a `format!` call is /// appended to an existing `String`. /// - /// ### Why restrict this? + /// ### Why is this bad? /// Introduces an extra, avoidable heap allocation. /// /// ### Known problems @@ -35,7 +35,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.62.0"] pub FORMAT_PUSH_STRING, - restriction, + pedantic, "`format!(..)` appended to existing `String`" } declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index 6f5c5d6b3ea3..c68499ce9f78 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -7,8 +7,8 @@ use clippy_utils::macros::macro_backtrace; use clippy_utils::source::snippet; use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty; +use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 4b700673d0f8..8887ab7ec0d7 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -217,6 +217,7 @@ mod manual_is_power_of_two; mod manual_let_else; mod manual_main_separator_str; mod manual_non_exhaustive; +mod manual_option_as_slice; mod manual_range_patterns; mod manual_rem_euclid; mod manual_retain; @@ -976,5 +977,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); + store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs new file mode 100644 index 000000000000..7c6564235796 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs @@ -0,0 +1,111 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; +use clippy_utils::macros::span_is_local; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::{HasSession, snippet_with_applicability}; +use clippy_utils::ty::implements_trait; +use clippy_utils::{higher, peel_blocks_with_stmt, span_contains_comment}; +use rustc_ast::ast::LitKind; +use rustc_ast::{RangeLimits, UnOp}; +use rustc_data_structures::packed::Pu128; +use rustc_errors::Applicability; +use rustc_hir::QPath::Resolved; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, Pat}; +use rustc_lint::LateContext; +use rustc_span::source_map::Spanned; +use rustc_span::sym; + +use super::MANUAL_SLICE_FILL; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, + msrv: &Msrv, +) { + if !msrv.meets(msrvs::SLICE_FILL) { + return; + } + + // `for _ in 0..slice.len() { slice[_] = value; }` + if let Some(higher::Range { + start: Some(start), + end: Some(end), + limits: RangeLimits::HalfOpen, + }) = higher::Range::hir(arg) + && let ExprKind::Lit(Spanned { + node: LitKind::Int(Pu128(0), _), + .. + }) = start.kind + && let ExprKind::Block(..) = body.kind + // Check if the body is an assignment to a slice element. + && let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind + && let ExprKind::Index(slice, _, _) = assignee.kind + // Check if `len()` is used for the range end. + && let ExprKind::MethodCall(path, recv,..) = end.kind + && path.ident.name == sym::len + // Check if the slice which is being assigned to is the same as the one being iterated over. + && let ExprKind::Path(Resolved(_, recv_path)) = recv.kind + && let ExprKind::Path(Resolved(_, slice_path)) = slice.kind + && recv_path.res == slice_path.res + && !assignval.span.from_expansion() + // It is generally not equivalent to use the `fill` method if `assignval` can have side effects + && switch_to_eager_eval(cx, assignval) + && span_is_local(assignval.span) + // The `fill` method requires that the slice's element type implements the `Clone` trait. + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, cx.typeck_results().expr_ty(slice), clone_trait, &[]) + { + sugg(cx, body, expr, slice.span, assignval.span); + } + // `for _ in &mut slice { *_ = value; }` + else if let ExprKind::AddrOf(_, _, recv) = arg.kind + // Check if the body is an assignment to a slice element. + && let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind + && let ExprKind::Unary(UnOp::Deref, slice_iter) = assignee.kind + && let ExprKind::Path(Resolved(_, recv_path)) = recv.kind + // Check if the slice which is being assigned to is the same as the one being iterated over. + && let ExprKind::Path(Resolved(_, slice_path)) = slice_iter.kind + && let Res::Local(local) = slice_path.res + && local == pat.hir_id + && !assignval.span.from_expansion() + && switch_to_eager_eval(cx, assignval) + && span_is_local(assignval.span) + // The `fill` method cannot be used if the slice's element type does not implement the `Clone` trait. + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, cx.typeck_results().expr_ty(recv), clone_trait, &[]) + { + sugg(cx, body, expr, recv_path.span, assignval.span); + } +} + +fn sugg<'tcx>( + cx: &LateContext<'tcx>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, + slice_span: rustc_span::Span, + assignval_span: rustc_span::Span, +) { + let mut app = if span_contains_comment(cx.sess().source_map(), body.span) { + Applicability::MaybeIncorrect // Comments may be informational. + } else { + Applicability::MachineApplicable + }; + + span_lint_and_sugg( + cx, + MANUAL_SLICE_FILL, + expr.span, + "manually filling a slice", + "try", + format!( + "{}.fill({});", + snippet_with_applicability(cx, slice_span, "..", &mut app), + snippet_with_applicability(cx, assignval_span, "..", &mut app), + ), + app, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index c5e75af2303c..cdc8c18c3b74 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -8,6 +8,7 @@ mod iter_next_loop; mod manual_find; mod manual_flatten; mod manual_memcpy; +mod manual_slice_fill; mod manual_while_let_some; mod missing_spin_loop; mod mut_range_bound; @@ -714,6 +715,31 @@ declare_clippy_lint! { "possibly unintended infinite loop" } +declare_clippy_lint! { + /// ### What it does + /// Checks for manually filling a slice with a value. + /// + /// ### Why is this bad? + /// Using the `fill` method is more idiomatic and concise. + /// + /// ### Example + /// ```no_run + /// let mut some_slice = [1, 2, 3, 4, 5]; + /// for i in 0..some_slice.len() { + /// some_slice[i] = 0; + /// } + /// ``` + /// Use instead: + /// ```no_run + /// let mut some_slice = [1, 2, 3, 4, 5]; + /// some_slice.fill(0); + /// ``` + #[clippy::version = "1.86.0"] + pub MANUAL_SLICE_FILL, + style, + "manually filling a slice with a value" +} + pub struct Loops { msrv: Msrv, enforce_iter_loop_reborrow: bool, @@ -750,6 +776,7 @@ impl_lint_pass!(Loops => [ MANUAL_WHILE_LET_SOME, UNUSED_ENUMERATE_INDEX, INFINITE_LOOP, + MANUAL_SLICE_FILL, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -823,6 +850,7 @@ impl Loops { ) { let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr); if !is_manual_memcpy_triggered { + manual_slice_fill::check(cx, pat, arg, body, expr, &self.msrv); needless_range_loop::check(cx, pat, arg, body, expr); explicit_counter_loop::check(cx, pat, arg, body, expr, label); } diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs index 816ca17b3d2d..04357cdd8f66 100644 --- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs @@ -181,13 +181,16 @@ fn build_suggestion( ExprKind::Lit(Spanned { node: LitKind::Int(_, LitIntType::Unsuffixed), .. - }) | ExprKind::Unary(UnOp::Neg, Expr { - kind: ExprKind::Lit(Spanned { - node: LitKind::Int(_, LitIntType::Unsuffixed), + }) | ExprKind::Unary( + UnOp::Neg, + Expr { + kind: ExprKind::Lit(Spanned { + node: LitKind::Int(_, LitIntType::Unsuffixed), + .. + }), .. - }), - .. - }) + } + ) ) { format!("_{}", cx.typeck_results().expr_ty(rhs)) } else { diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs new file mode 100644 index 000000000000..5c40c945c690 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs @@ -0,0 +1,225 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::impl_lint_pass; +use rustc_span::{Span, Symbol, sym}; + +declare_clippy_lint! { + /// ### What it does + /// This detects various manual reimplementations of `Option::as_slice`. + /// + /// ### Why is this bad? + /// Those implementations are both more complex than calling `as_slice` + /// and unlike that incur a branch, pessimizing performance and leading + /// to more generated code. + /// + /// ### Example + /// ```no_run + ///# let opt = Some(1); + /// _ = opt.as_ref().map_or(&[][..], std::slice::from_ref); + /// _ = match opt.as_ref() { + /// Some(f) => std::slice::from_ref(f), + /// None => &[], + /// }; + /// ``` + /// Use instead: + /// ```no_run + ///# let opt = Some(1); + /// _ = opt.as_slice(); + /// _ = opt.as_slice(); + /// ``` + #[clippy::version = "1.85.0"] + pub MANUAL_OPTION_AS_SLICE, + complexity, + "manual `Option::as_slice`" +} + +pub struct ManualOptionAsSlice { + msrv: msrvs::Msrv, +} + +impl ManualOptionAsSlice { + pub fn new(conf: &Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(ManualOptionAsSlice => [MANUAL_OPTION_AS_SLICE]); + +impl LateLintPass<'_> for ManualOptionAsSlice { + extract_msrv_attr!(LateContext); + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let span = expr.span; + if span.from_expansion() + || !self.msrv.meets(if clippy_utils::is_in_const_context(cx) { + msrvs::CONST_OPTION_AS_SLICE + } else { + msrvs::OPTION_AS_SLICE + }) + { + return; + } + match expr.kind { + ExprKind::Match(scrutinee, [arm1, arm2], _) => { + if is_none_arm(cx, arm2) && check_arms(cx, arm2, arm1) + || is_none_arm(cx, arm1) && check_arms(cx, arm1, arm2) + { + check_as_ref(cx, scrutinee, span); + } + }, + ExprKind::If(cond, then, Some(other)) => { + if let ExprKind::Let(let_expr) = cond.kind + && let Some(binding) = extract_ident_from_some_pat(cx, let_expr.pat) + && check_some_body(cx, binding, then) + && is_empty_slice(cx, other.peel_blocks()) + { + check_as_ref(cx, let_expr.init, span); + } + }, + ExprKind::MethodCall(seg, callee, [], _) => { + if seg.ident.name.as_str() == "unwrap_or_default" { + check_map(cx, callee, span); + } + }, + ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name.as_str() { + "unwrap_or" => { + if is_empty_slice(cx, or) { + check_map(cx, callee, span); + } + }, + "unwrap_or_else" => { + if returns_empty_slice(cx, or) { + check_map(cx, callee, span); + } + }, + _ => {}, + }, + ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name.as_str() { + "map_or" => { + if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { + check_as_ref(cx, callee, span); + } + }, + "map_or_else" => { + if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { + check_as_ref(cx, callee, span); + } + }, + _ => {}, + }, + _ => {}, + } + } +} + +fn check_map(cx: &LateContext<'_>, map: &Expr<'_>, span: Span) { + if let ExprKind::MethodCall(seg, callee, [mapping], _) = map.kind + && seg.ident.name == sym::map + && is_slice_from_ref(cx, mapping) + { + check_as_ref(cx, callee, span); + } +} + +fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span) { + if let ExprKind::MethodCall(seg, callee, [], _) = expr.kind + && seg.ident.name == sym::as_ref + && let ty::Adt(adtdef, ..) = cx.typeck_results().expr_ty(callee).kind() + && cx.tcx.is_diagnostic_item(sym::Option, adtdef.did()) + { + if let Some(snippet) = clippy_utils::source::snippet_opt(cx, callee.span) { + span_lint_and_sugg( + cx, + MANUAL_OPTION_AS_SLICE, + span, + "use `Option::as_slice`", + "use", + format!("{snippet}.as_slice()"), + Applicability::MachineApplicable, + ); + } else { + span_lint(cx, MANUAL_OPTION_AS_SLICE, span, "use `Option_as_slice`"); + } + } +} + +fn extract_ident_from_some_pat(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { + if let PatKind::TupleStruct(QPath::Resolved(None, path), [binding], _) = pat.kind + && let Res::Def(DefKind::Ctor(..), def_id) = path.res + && let PatKind::Binding(_mode, _hir_id, ident, _inner_pat) = binding.kind + && clippy_utils::is_lang_item_or_ctor(cx, def_id, LangItem::OptionSome) + { + Some(ident.name) + } else { + None + } +} + +/// Returns true if `expr` is `std::slice::from_ref()`. Used in `if let`s. +fn check_some_body(cx: &LateContext<'_>, name: Symbol, expr: &Expr<'_>) -> bool { + if let ExprKind::Call(slice_from_ref, [arg]) = expr.peel_blocks().kind + && is_slice_from_ref(cx, slice_from_ref) + && let ExprKind::Path(QPath::Resolved(None, path)) = arg.kind + && let [seg] = path.segments + { + seg.ident.name == name + } else { + false + } +} + +fn check_arms(cx: &LateContext<'_>, none_arm: &Arm<'_>, some_arm: &Arm<'_>) -> bool { + if none_arm.guard.is_none() + && some_arm.guard.is_none() + && is_empty_slice(cx, none_arm.body) + && let Some(name) = extract_ident_from_some_pat(cx, some_arm.pat) + { + check_some_body(cx, name, some_arm.body) + } else { + false + } +} + +fn returns_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Path(_) => clippy_utils::is_path_diagnostic_item(cx, expr, sym::default_fn), + ExprKind::Closure(cl) => is_empty_slice(cx, cx.tcx.hir().body(cl.body).value), + _ => false, + } +} + +/// Returns if expr returns an empty slice. If: +/// - An indexing operation to an empty array with a built-in range. `[][..]` +/// - An indexing operation with a zero-ended range. `expr[..0]` +/// - A reference to an empty array. `&[]` +/// - Or a call to `Default::default`. +fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr = peel_hir_expr_refs(expr.peel_blocks()).0; + match expr.kind { + ExprKind::Index(arr, range, _) => match arr.kind { + ExprKind::Array([]) => is_range_literal(range), + ExprKind::Array(_) => { + let Some(range) = clippy_utils::higher::Range::hir(range) else { + return false; + }; + range.end.is_some_and(|e| clippy_utils::is_integer_const(cx, e, 0)) + }, + _ => false, + }, + ExprKind::Array([]) => true, + ExprKind::Call(def, []) => clippy_utils::is_path_diagnostic_item(cx, def, sym::default_fn), + _ => false, + } +} + +fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + clippy_utils::is_expr_path_def_path(cx, expr, &["core", "slice", "raw", "from_ref"]) +} diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs index 7b95399c907c..87d2faa225c5 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs @@ -9,8 +9,8 @@ use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::IfLetOrMatch; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::implements_trait; -use clippy_utils::{is_default_equivalent, is_in_const_context, peel_blocks, span_contains_comment}; +use clippy_utils::ty::{expr_type_is_certain, implements_trait}; +use clippy_utils::{is_default_equivalent, is_in_const_context, path_res, peel_blocks, span_contains_comment}; declare_clippy_lint! { /// ### What it does @@ -158,6 +158,36 @@ fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, exp } else { Applicability::MachineApplicable }; + + // We now check if the condition is a None variant, in which case we need to specify the type + if path_res(cx, condition) + .opt_def_id() + .is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant()) + { + return span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + "replace it with", + format!("{receiver}::<{expr_type}>.unwrap_or_default()"), + applicability, + ); + } + + // We check if the expression type is still uncertain, in which case we ask the user to specify it + if !expr_type_is_certain(cx, condition) { + return span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), + format!("ascribe the type {expr_type} and replace your expression with"), + format!("{receiver}.unwrap_or_default()"), + Applicability::Unspecified, + ); + } + span_lint_and_sugg( cx, MANUAL_UNWRAP_OR_DEFAULT, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs index b1889d26c932..1cb4b512a30e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; +use clippy_utils::{is_none_arm, is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatExpr, PatExprKind, PatKind, QPath}; +use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -55,14 +55,6 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: } } -// Checks if arm has the form `None => None` -fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { - matches!( - arm.pat.kind, - PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone) - ) -} - // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 2f447775fa5b..f501cf060c2a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -95,6 +95,7 @@ mod readonly_write_lock; mod redundant_as_str; mod repeat_once; mod result_map_or_else_none; +mod return_and_then; mod search_is_some; mod seek_from_current; mod seek_to_start_instead_of_rewind; @@ -3517,7 +3518,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.73.0"] pub FORMAT_COLLECT, - perf, + pedantic, "`format!`ing every element in a collection, then collecting the strings into a new `String`" } @@ -4365,7 +4366,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for string slices immediantly followed by `as_bytes`. + /// Checks for string slices immediately followed by `as_bytes`. /// /// ### Why is this bad? /// It involves doing an unnecessary UTF-8 alignment check which is less efficient, and can cause a panic. @@ -4391,6 +4392,46 @@ declare_clippy_lint! { "slicing a string and immediately calling as_bytes is less efficient and can lead to panics" } +declare_clippy_lint! { + /// ### What it does + /// + /// Detect functions that end with `Option::and_then` or `Result::and_then`, and suggest using a question mark (`?`) instead. + /// + /// ### Why is this bad? + /// + /// The `and_then` method is used to chain a computation that returns an `Option` or a `Result`. + /// This can be replaced with the `?` operator, which is more concise and idiomatic. + /// + /// ### Example + /// + /// ```no_run + /// fn test(opt: Option) -> Option { + /// opt.and_then(|n| { + /// if n > 1 { + /// Some(n + 1) + /// } else { + /// None + /// } + /// }) + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn test(opt: Option) -> Option { + /// let n = opt?; + /// if n > 1 { + /// Some(n + 1) + /// } else { + /// None + /// } + /// } + /// ``` + #[clippy::version = "1.86.0"] + pub RETURN_AND_THEN, + restriction, + "using `Option::and_then` or `Result::and_then` to chain a computation that returns an `Option` or a `Result`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4560,6 +4601,7 @@ impl_lint_pass!(Methods => [ USELESS_NONZERO_NEW_UNCHECKED, MANUAL_REPEAT_N, SLICED_STRING_AS_BYTES, + RETURN_AND_THEN, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4789,7 +4831,10 @@ impl Methods { let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg); let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg); if !biom_option_linted && !biom_result_linted { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); + let ule_and_linted = unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); + if !ule_and_linted { + return_and_then::check(cx, expr, recv, arg); + } } }, ("any", [arg]) => { @@ -5003,7 +5048,9 @@ impl Methods { get_first::check(cx, expr, recv, arg); get_last_with_len::check(cx, expr, recv, arg); }, - ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), + ("get_or_insert_with", [arg]) => { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"); + }, ("hash", [arg]) => { unit_hash::check(cx, expr, recv, arg); }, @@ -5144,7 +5191,9 @@ impl Methods { }, _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, - ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), + ("ok_or_else", [arg]) => { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); + }, ("open", [_]) => { open_options::check(cx, expr, recv); }, @@ -5437,9 +5486,12 @@ impl ShouldImplTraitCase { fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { self.lint_explicit_lifetime || !impl_item.generics.params.iter().any(|p| { - matches!(p.kind, hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - }) + matches!( + p.kind, + hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + } + ) }) } } 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 index c41ce2481d74..88b9c69f6f94 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_take.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; @@ -10,13 +11,22 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' // 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) { if let Some(function_name) = source_of_temporary_value(recv) { - span_lint_and_note( + span_lint_and_then( cx, NEEDLESS_OPTION_TAKE, expr.span, "called `Option::take()` on a temporary value", - None, - format!("`{function_name}` creates a temporary value, so calling take() has no effect"), + |diag| { + diag.note(format!( + "`{function_name}` creates a temporary value, so calling take() has no effect" + )); + diag.span_suggestion( + expr.span.with_lo(recv.span.hi()), + "remove", + "", + Applicability::MachineApplicable, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs new file mode 100644 index 000000000000..7b1199ad1e2d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs @@ -0,0 +1,67 @@ +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, GenericArg, Ty}; +use rustc_span::sym; +use std::ops::ControlFlow; + +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; +use clippy_utils::ty::get_type_diagnostic_name; +use clippy_utils::visitors::for_each_unconsumed_temporary; +use clippy_utils::{is_expr_final_block_expr, peel_blocks}; + +use super::RETURN_AND_THEN; + +/// lint if `and_then` is the last expression in a block, and +/// there are no references or temporaries in the receiver +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + recv: &'tcx hir::Expr<'tcx>, + arg: &'tcx hir::Expr<'_>, +) { + if !is_expr_final_block_expr(cx.tcx, expr) { + return; + } + + let recv_type = cx.typeck_results().expr_ty(recv); + if !matches!(get_type_diagnostic_name(cx, recv_type), Some(sym::Option | sym::Result)) { + return; + } + + let has_ref_type = matches!(recv_type.kind(), ty::Adt(_, args) if args + .first() + .and_then(|arg0: &GenericArg<'tcx>| GenericArg::as_type(*arg0)) + .is_some_and(Ty::is_ref)); + let has_temporaries = for_each_unconsumed_temporary(cx, recv, |_| ControlFlow::Break(())).is_break(); + if has_ref_type && has_temporaries { + return; + } + + let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind else { + return; + }; + + let closure_arg = fn_decl.inputs[0]; + let closure_expr = peel_blocks(cx.tcx.hir().body(body).value); + + let mut applicability = Applicability::MachineApplicable; + let arg_snip = snippet_with_applicability(cx, closure_arg.span, "_", &mut applicability); + let recv_snip = snippet_with_applicability(cx, recv.span, "_", &mut applicability); + let body_snip = snippet_with_applicability(cx, closure_expr.span, "..", &mut applicability); + let inner = match body_snip.strip_prefix('{').and_then(|s| s.strip_suffix('}')) { + Some(s) => s.trim_start_matches('\n').trim_end(), + None => &body_snip, + }; + + let msg = "use the question mark operator instead of an `and_then` call"; + let sugg = format!( + "let {} = {}?;\n{}", + arg_snip, + recv_snip, + reindent_multiline(inner.into(), false, indent_of(cx, expr.span)) + ); + + span_lint_and_sugg(cx, RETURN_AND_THEN, expr.span, msg, "try", sugg, applicability); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index c27d1fb4903b..e7adf3b43ba5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -123,32 +123,60 @@ pub(super) fn check( if let hir::ExprKind::Lit(lit) = init.kind { match lit.node { ast::LitKind::Bool(false) => { - check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, Replacement { - method_name: "any", - has_args: true, - has_generic_return: false, - }); + check_fold_with_op( + cx, + expr, + acc, + fold_span, + hir::BinOpKind::Or, + Replacement { + method_name: "any", + has_args: true, + has_generic_return: false, + }, + ); }, ast::LitKind::Bool(true) => { - check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, Replacement { - method_name: "all", - has_args: true, - has_generic_return: false, - }); + check_fold_with_op( + cx, + expr, + acc, + fold_span, + hir::BinOpKind::And, + Replacement { + method_name: "all", + has_args: true, + has_generic_return: false, + }, + ); }, ast::LitKind::Int(Pu128(0), _) => { - check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, Replacement { - method_name: "sum", - has_args: false, - has_generic_return: needs_turbofish(cx, expr), - }); + check_fold_with_op( + cx, + expr, + acc, + fold_span, + hir::BinOpKind::Add, + Replacement { + method_name: "sum", + has_args: false, + has_generic_return: needs_turbofish(cx, expr), + }, + ); }, ast::LitKind::Int(Pu128(1), _) => { - check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, Replacement { - method_name: "product", - has_args: false, - has_generic_return: needs_turbofish(cx, expr), - }); + check_fold_with_op( + cx, + expr, + acc, + fold_span, + hir::BinOpKind::Mul, + Replacement { + method_name: "product", + has_args: false, + has_generic_return: needs_turbofish(cx, expr), + }, + ); }, _ => (), } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 1673a6f8b3a4..7af550fa7c68 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, simplify_using: &str, -) { +) -> bool { let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); let is_bool = cx.typeck_results().expr_ty(recv).is_bool(); @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>( let body_expr = &body.value; if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) { - return; + return false; } if eager_or_lazy::switch_to_eager_eval(cx, body_expr) { @@ -71,8 +71,10 @@ pub(super) fn check<'tcx>( applicability, ); }); + return true; } } } } + false } diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index fa0eb9a94b73..fca416d9e64c 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -168,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { TOPLEVEL_REF_ARG, arg.hir_id, arg.pat.span, - "`ref` directly on a function argument is ignored. \ + "`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \ Consider using a reference type instead", ); } diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 302db2c914ca..9acede4f32d6 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -152,16 +152,19 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => { if matches!( lhs.kind, - ExprKind::Path(QPath::Resolved(_, hir::Path { - res: Res::Def( - DefKind::Static { - mutability: Mutability::Mut, - .. - }, - _ - ), - .. - })) + ExprKind::Path(QPath::Resolved( + _, + hir::Path { + res: Res::Def( + DefKind::Static { + mutability: Mutability::Mut, + .. + }, + _ + ), + .. + } + )) ) { unsafe_ops.push(("modification of a mutable static occurs here", expr.span)); collect_unsafe_exprs(cx, rhs, unsafe_ops); diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 86c084423b71..49fd29d1dd6d 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -18,11 +18,14 @@ declare_clippy_lint! { /// /// On the other hand, `Mutex`es are, in general, easier to /// verify correctness. An atomic does not behave the same as - /// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s commentary for more details. + /// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s + /// commentary for more details. /// /// ### Known problems - /// This lint cannot detect if the mutex is actually used - /// for waiting before a critical section. + /// * This lint cannot detect if the mutex is actually used + /// for waiting before a critical section. + /// * This lint has a false positive that warns without considering the case + /// where `Mutex` is used together with `Condvar`. /// /// ### Example /// ```no_run @@ -48,14 +51,23 @@ declare_clippy_lint! { /// Checks for usage of `Mutex` where `X` is an integral /// type. /// - /// ### Why is this bad? + /// ### Why restrict this? /// Using a mutex just to make access to a plain integer /// sequential is /// shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster. /// + /// On the other hand, `Mutex`es are, in general, easier to + /// verify correctness. An atomic does not behave the same as + /// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s + /// commentary for more details. + /// /// ### Known problems - /// This lint cannot detect if the mutex is actually used - /// for waiting before a critical section. + /// * This lint cannot detect if the mutex is actually used + /// for waiting before a critical section. + /// * This lint has a false positive that warns without considering the case + /// where `Mutex` is used together with `Condvar`. + /// * This lint suggest using `AtomicU64` instead of `Mutex`, but + /// `AtomicU64` is not available on some 32-bit platforms. /// /// ### Example /// ```no_run @@ -70,7 +82,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub MUTEX_INTEGER, - nursery, + restriction, "using a mutex for an integer type" } @@ -108,7 +120,7 @@ fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { UintTy::U32 => Some("AtomicU32"), UintTy::U64 => Some("AtomicU64"), UintTy::Usize => Some("AtomicUsize"), - // There's no `AtomicU128`. + // `AtomicU128` is unstable and only available on a few platforms: https://github.com/rust-lang/rust/issues/99069 UintTy::U128 => None, } }, @@ -119,7 +131,7 @@ fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { IntTy::I32 => Some("AtomicI32"), IntTy::I64 => Some("AtomicI64"), IntTy::Isize => Some("AtomicIsize"), - // There's no `AtomicI128`. + // `AtomicU128` is unstable and only available on a few platforms: https://github.com/rust-lang/rust/issues/99069 IntTy::I128 => None, } }, diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 30846fb46ac1..2855703b9d56 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -181,9 +181,14 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { && !is_copy(cx, ty) && ty.is_sized(cx.tcx, cx.typing_env()) && !allowed_traits.iter().any(|&t| { - implements_trait_with_env_from_iter(cx.tcx, cx.typing_env(), ty, t, None, [None::< - ty::GenericArg<'tcx>, - >]) + implements_trait_with_env_from_iter( + cx.tcx, + cx.typing_env(), + ty, + t, + None, + [None::>], + ) }) && !implements_borrow_trait && !all_borrowable_trait diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index d9845bc3b0f7..9ad32c2bd396 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -265,9 +265,6 @@ declare_clippy_lint! { /// `x.trailing_zeros() >= 4` is much clearer than `x & 15 /// == 0` /// - /// ### Known problems - /// llvm generates better code for `x & 15 == 0` on x86 - /// /// ### Example /// ```no_run /// # let x = 1; diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs index 421b2b747555..ec6835db897e 100644 --- a/src/tools/clippy/clippy_lints/src/precedence.rs +++ b/src/tools/clippy/clippy_lints/src/precedence.rs @@ -3,17 +3,14 @@ use clippy_utils::source::snippet_with_applicability; use rustc_ast::ast::BinOpKind::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub}; use rustc_ast::ast::{BinOpKind, Expr, ExprKind}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; declare_clippy_lint! { /// ### What it does - /// Checks for operations where precedence may be unclear - /// and suggests to add parentheses. Currently it catches the following: - /// * mixed usage of arithmetic and bit shifting/combining operators without - /// parentheses - /// * mixed usage of bitmasking and bit shifting operators without parentheses + /// Checks for operations where precedence may be unclear and suggests to add parentheses. + /// It catches a mixed usage of arithmetic and bit shifting/combining operators without parentheses /// /// ### Why is this bad? /// Not everyone knows the precedence of those operators by @@ -21,15 +18,32 @@ declare_clippy_lint! { /// code. /// /// ### Example - /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7 - /// * `0x2345 & 0xF000 >> 12` equals 5, while `(0x2345 & 0xF000) >> 12` equals 2 + /// `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7 #[clippy::version = "pre 1.29.0"] pub PRECEDENCE, complexity, "operations where precedence may be unclear" } -declare_lint_pass!(Precedence => [PRECEDENCE]); +declare_clippy_lint! { + /// ### What it does + /// Checks for bit shifting operations combined with bit masking/combining operators + /// and suggest using parentheses. + /// + /// ### Why restrict this? + /// Not everyone knows the precedence of those operators by + /// heart, so expressions like these may trip others trying to reason about the + /// code. + /// + /// ### Example + /// `0x2345 & 0xF000 >> 12` equals 5, while `(0x2345 & 0xF000) >> 12` equals 2 + #[clippy::version = "1.86.0"] + pub PRECEDENCE_BITS, + restriction, + "operations mixing bit shifting with bit combining/masking" +} + +declare_lint_pass!(Precedence => [PRECEDENCE, PRECEDENCE_BITS]); impl EarlyLintPass for Precedence { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { @@ -38,10 +52,10 @@ impl EarlyLintPass for Precedence { } if let ExprKind::Binary(Spanned { node: op, .. }, ref left, ref right) = expr.kind { - let span_sugg = |expr: &Expr, sugg, appl| { + let span_sugg = |lint: &'static Lint, expr: &Expr, sugg, appl| { span_lint_and_sugg( cx, - PRECEDENCE, + lint, expr.span, "operator precedence might not be obvious", "consider parenthesizing your expression", @@ -57,37 +71,41 @@ impl EarlyLintPass for Precedence { match (op, get_bin_opt(left), get_bin_opt(right)) { ( BitAnd | BitOr | BitXor, - Some(Shl | Shr | Add | Div | Mul | Rem | Sub), - Some(Shl | Shr | Add | Div | Mul | Rem | Sub), + Some(left_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)), + Some(right_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)), ) - | (Shl | Shr, Some(Add | Div | Mul | Rem | Sub), Some(Add | Div | Mul | Rem | Sub)) => { + | ( + Shl | Shr, + Some(left_op @ (Add | Div | Mul | Rem | Sub)), + Some(right_op @ (Add | Div | Mul | Rem | Sub)), + ) => { let sugg = format!( "({}) {} ({})", snippet_with_applicability(cx, left.span, "..", &mut applicability), op.as_str(), snippet_with_applicability(cx, right.span, "..", &mut applicability) ); - span_sugg(expr, sugg, applicability); + span_sugg(lint_for(&[op, left_op, right_op]), expr, sugg, applicability); }, - (BitAnd | BitOr | BitXor, Some(Shl | Shr | Add | Div | Mul | Rem | Sub), _) - | (Shl | Shr, Some(Add | Div | Mul | Rem | Sub), _) => { + (BitAnd | BitOr | BitXor, Some(side_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)), _) + | (Shl | Shr, Some(side_op @ (Add | Div | Mul | Rem | Sub)), _) => { let sugg = format!( "({}) {} {}", snippet_with_applicability(cx, left.span, "..", &mut applicability), op.as_str(), snippet_with_applicability(cx, right.span, "..", &mut applicability) ); - span_sugg(expr, sugg, applicability); + span_sugg(lint_for(&[op, side_op]), expr, sugg, applicability); }, - (BitAnd | BitOr | BitXor, _, Some(Shl | Shr | Add | Div | Mul | Rem | Sub)) - | (Shl | Shr, _, Some(Add | Div | Mul | Rem | Sub)) => { + (BitAnd | BitOr | BitXor, _, Some(side_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub))) + | (Shl | Shr, _, Some(side_op @ (Add | Div | Mul | Rem | Sub))) => { let sugg = format!( "{} {} ({})", snippet_with_applicability(cx, left.span, "..", &mut applicability), op.as_str(), snippet_with_applicability(cx, right.span, "..", &mut applicability) ); - span_sugg(expr, sugg, applicability); + span_sugg(lint_for(&[op, side_op]), expr, sugg, applicability); }, _ => (), } @@ -106,3 +124,11 @@ fn get_bin_opt(expr: &Expr) -> Option { fn is_bit_op(op: BinOpKind) -> bool { matches!(op, BitXor | BitAnd | BitOr | Shl | Shr) } + +fn lint_for(ops: &[BinOpKind]) -> &'static Lint { + if ops.iter().all(|op| is_bit_op(*op)) { + PRECEDENCE_BITS + } else { + PRECEDENCE + } +} diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 506adf0f2cc5..0b67594a9b19 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -1,5 +1,6 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::SpanRangeExt; +use clippy_utils::sugg::Sugg; 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, std_or_core}; use hir::LifetimeName; @@ -250,15 +251,24 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, l, r) = expr.kind { - if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) { - span_lint( - cx, - CMP_NULL, - expr.span, - "comparing with null is better expressed by the `.is_null()` method", - ); - } + if let ExprKind::Binary(op, l, r) = expr.kind + && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) + { + let non_null_path_snippet = match (is_null_path(cx, l), is_null_path(cx, r)) { + (true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_par(), + (false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_par(), + _ => return, + }; + + span_lint_and_sugg( + cx, + CMP_NULL, + expr.span, + "comparing with null is better expressed by the `.is_null()` method", + "try", + format!("{non_null_path_snippet}.is_null()"), + Applicability::MachineApplicable, + ); } else { check_invalid_ptr_usage(cx, expr); } diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index b9e0106fc86b..fb1bc494bd94 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -349,10 +349,14 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, local_use_locs: _, local_consume_or_mutate_locs: clone_consume_or_mutate_locs, }, - )) = visit_local_usage(&[cloned, clone], mir, mir::Location { - block: bb, - statement_index: mir.basic_blocks[bb].statements.len(), - }) + )) = visit_local_usage( + &[cloned, clone], + mir, + mir::Location { + block: bb, + statement_index: mir.basic_blocks[bb].statements.len(), + }, + ) .map(|mut vec| (vec.remove(0), vec.remove(0))) { CloneUsage { diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs index 3476f56cf338..a1b5a3aff323 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_else.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs @@ -1,8 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_ast::visit::{Visitor, walk_expr}; +use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; +use rustc_span::Span; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -75,13 +79,27 @@ impl EarlyLintPass for RedundantElse { _ => break, } } - span_lint_and_help( + + let mut app = Applicability::MachineApplicable; + if let ExprKind::Block(block, _) = &els.kind { + for stmt in &block.stmts { + // If the `else` block contains a local binding or a macro invocation, Clippy shouldn't auto-fix it + if matches!(&stmt.kind, StmtKind::Let(_) | StmtKind::MacCall(_)) { + app = Applicability::Unspecified; + break; + } + } + } + + // FIXME: The indentation of the suggestion would be the same as the one of the macro invocation in this implementation, see https://github.com/rust-lang/rust-clippy/pull/13936#issuecomment-2569548202 + span_lint_and_sugg( cx, REDUNDANT_ELSE, - els.span, + els.span.with_lo(then.span.hi()), "redundant else block", - None, "remove the `else` block and move the contents out", + make_sugg(cx, els.span, "..", Some(expr.span)).to_string(), + app, ); } } @@ -136,3 +154,23 @@ impl BreakVisitor { self.check(stmt, Self::visit_stmt) } } + +// Extract the inner contents of an `else` block str +// e.g. `{ foo(); bar(); }` -> `foo(); bar();` +fn extract_else_block(mut block: &str) -> String { + block = block.strip_prefix("{").unwrap_or(block); + block = block.strip_suffix("}").unwrap_or(block); + block.trim_end().to_string() +} + +fn make_sugg<'a>( + cx: &EarlyContext<'_>, + els_span: Span, + default: &'a str, + indent_relative_to: Option, +) -> Cow<'a, str> { + let extracted = extract_else_block(&snippet(cx, els_span, default)); + let indent = indent_relative_to.and_then(|s| indent_of(cx, s)); + + reindent_multiline(extracted.into(), false, indent) +} 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 8d31641d4836..29914d4379fe 100644 --- a/src/tools/clippy/clippy_lints/src/same_name_method.rs +++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs @@ -62,10 +62,13 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { && 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(), - }); + map.insert( + *res, + ExistingName { + impl_methods: BTreeMap::new(), + trait_methods: BTreeMap::new(), + }, + ); } let existing_name = map.get_mut(res).unwrap(); diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs index f72ff10dd43c..b22c638fc363 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs @@ -63,8 +63,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { - const METHODS: [&str; 11] = [ - "write_bytes", + const METHODS: [&str; 10] = [ "copy_to", "copy_from", "copy_to_nonoverlapping", @@ -79,7 +78,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( if let ExprKind::Call(func, [.., count]) = expr.kind // Find calls to ptr::{copy, copy_nonoverlapping} - // and ptr::{swap_nonoverlapping, write_bytes}, + // and ptr::swap_nonoverlapping, && let ExprKind::Path(ref func_qpath) = func.kind && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() && matches!(cx.tcx.get_diagnostic_name(def_id), Some( @@ -88,7 +87,6 @@ fn get_pointee_ty_and_count_expr<'tcx>( | sym::ptr_slice_from_raw_parts | sym::ptr_slice_from_raw_parts_mut | sym::ptr_swap_nonoverlapping - | sym::ptr_write_bytes | sym::slice_from_raw_parts | sym::slice_from_raw_parts_mut )) @@ -99,7 +97,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( return Some((pointee_ty, count)); } if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind - // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods + // Find calls to copy_{from,to}{,_nonoverlapping} && let method_ident = method_path.ident.as_str() && METHODS.iter().any(|m| *m == method_ident) @@ -121,6 +119,8 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { instead of a count of elements of `T`"; if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr) + // Using a number of bytes for a byte type isn't suspicious + && pointee_ty != cx.tcx.types.u8 // Find calls to functions with an element count parameter and get // the pointee type and count parameter expression diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs index 569812d81065..9993e6ae18b9 100644 --- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs @@ -55,13 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id) && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id() - && match_def_path(cx, to_digits_def_id, &[ - "core", - "char", - "methods", - "", - "to_digit", - ]) + && match_def_path( + cx, + to_digits_def_id, + &["core", "char", "methods", "", "to_digit"], + ) { Some((false, char_arg, radix_arg)) } else { diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 391c36df492c..579cbf447a2b 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -386,22 +386,30 @@ impl<'tcx> LateLintPass<'tcx> for Types { let is_exported = cx.effective_visibilities.is_exported(def_id); - self.check_fn_decl(cx, decl, CheckTyContext { - is_in_trait_impl, - in_body: matches!(fn_kind, FnKind::Closure), - is_exported, - ..CheckTyContext::default() - }); + self.check_fn_decl( + cx, + decl, + CheckTyContext { + is_in_trait_impl, + in_body: matches!(fn_kind, FnKind::Closure), + is_exported, + ..CheckTyContext::default() + }, + ); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id); match item.kind { - ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty(cx, ty, CheckTyContext { - is_exported, - ..CheckTyContext::default() - }), + ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty( + cx, + ty, + CheckTyContext { + is_exported, + ..CheckTyContext::default() + }, + ), // functions, enums, structs, impls and traits are covered _ => (), } @@ -419,10 +427,14 @@ impl<'tcx> LateLintPass<'tcx> for Types { false }; - self.check_ty(cx, ty, CheckTyContext { - is_in_trait_impl, - ..CheckTyContext::default() - }); + self.check_ty( + cx, + ty, + CheckTyContext { + is_in_trait_impl, + ..CheckTyContext::default() + }, + ); }, // Methods are covered by check_fn. // Type aliases are ignored because oftentimes it's impossible to @@ -438,10 +450,14 @@ impl<'tcx> LateLintPass<'tcx> for Types { let is_exported = cx.effective_visibilities.is_exported(field.def_id); - self.check_ty(cx, field.ty, CheckTyContext { - is_exported, - ..CheckTyContext::default() - }); + self.check_ty( + cx, + field.ty, + CheckTyContext { + is_exported, + ..CheckTyContext::default() + }, + ); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'tcx>) { @@ -469,10 +485,14 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if let Some(ty) = local.ty { - self.check_ty(cx, ty, CheckTyContext { - in_body: true, - ..CheckTyContext::default() - }); + self.check_ty( + cx, + ty, + CheckTyContext { + in_body: true, + ..CheckTyContext::default() + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index 3fc08e8192d4..207f2ef4563a 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -257,10 +257,13 @@ fn is_default_method_on_current_ty<'tcx>(tcx: TyCtxt<'tcx>, qpath: QPath<'tcx>, } if matches!( ty.kind, - TyKind::Path(QPath::Resolved(_, hir::Path { - res: Res::SelfTyAlias { .. }, - .. - },)) + TyKind::Path(QPath::Resolved( + _, + hir::Path { + res: Res::SelfTyAlias { .. }, + .. + }, + )) ) { return true; } 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 5e5d6a9e333b..b3d269080930 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,3 +1,4 @@ +use std::sync::Arc; use std::ops::ControlFlow; use clippy_config::Conf; @@ -6,7 +7,6 @@ use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; use clippy_utils::visitors::{Descend, for_each_expr}; use hir::HirId; -use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource}; use rustc_lexer::{TokenKind, tokenize}; @@ -480,7 +480,7 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf if let Some(comment_start) = comment_start && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) && let Ok(comment_start_line) = source_map.lookup_line(comment_start) - && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && Arc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { return if comment_start_line.line >= unsafe_line.line { @@ -520,7 +520,7 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H if let Some(comment_start) = comment_start && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) && let Ok(comment_start_line) = source_map.lookup_line(comment_start) - && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && Arc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { return if comment_start_line.line >= unsafe_line.line { @@ -580,7 +580,7 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span // ^--------------------------------------------^ if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo()) - && Lrc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) + && Arc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { if macro_line.line < unsafe_line.line { @@ -641,7 +641,7 @@ fn span_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) && let Some(body_span) = walk_span_to_context(search_span, SyntaxContext::root()) && let Ok(body_line) = source_map.lookup_line(body_span.lo()) - && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf) + && Arc::ptr_eq(&unsafe_line.sf, &body_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { // Get the text from the start of function body to the unsafe block. diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs b/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs index efbc536dcb4d..e5267620c4fb 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { #[derive(Default)] pub struct UnnecessarySemicolon { - last_statements: Vec, + last_statements: Vec<(HirId, bool)>, } impl_lint_pass!(UnnecessarySemicolon => [UNNECESSARY_SEMICOLON]); @@ -45,27 +45,25 @@ impl_lint_pass!(UnnecessarySemicolon => [UNNECESSARY_SEMICOLON]); impl UnnecessarySemicolon { /// Enter or leave a block, remembering the last statement of the block. fn handle_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>, enter: bool) { - // Up to edition 2021, removing the semicolon of the last statement of a block - // may result in the scrutinee temporary values to live longer than the block - // variables. To avoid this problem, we do not lint the last statement of an - // expressionless block. - if cx.tcx.sess.edition() <= Edition2021 - && block.expr.is_none() + // The last statement of an expressionless block deserves a special treatment. + if block.expr.is_none() && let Some(last_stmt) = block.stmts.last() { if enter { - self.last_statements.push(last_stmt.hir_id); + let block_ty = cx.typeck_results().node_type(block.hir_id); + self.last_statements.push((last_stmt.hir_id, block_ty.is_unit())); } else { self.last_statements.pop(); } } } - /// Checks if `stmt` is the last statement in an expressionless block for edition ≤ 2021. - fn is_last_in_block(&self, stmt: &Stmt<'_>) -> bool { + /// Checks if `stmt` is the last statement in an expressionless block. In this case, + /// return `Some` with a boolean which is `true` if the block type is `()`. + fn is_last_in_block(&self, stmt: &Stmt<'_>) -> Option { self.last_statements .last() - .is_some_and(|last_stmt_id| last_stmt_id == &stmt.hir_id) + .and_then(|&(stmt_id, is_unit)| (stmt_id == stmt.hir_id).then_some(is_unit)) } } @@ -90,8 +88,22 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessarySemicolon { ) && cx.typeck_results().expr_ty(expr) == cx.tcx.types.unit { - if self.is_last_in_block(stmt) && leaks_droppable_temporary_with_limited_lifetime(cx, expr) { - return; + if let Some(block_is_unit) = self.is_last_in_block(stmt) { + if cx.tcx.sess.edition() <= Edition2021 && leaks_droppable_temporary_with_limited_lifetime(cx, expr) { + // The expression contains temporaries with limited lifetimes in edition lower than 2024. Those may + // survive until after the end of the current scope instead of until the end of the statement, so do + // not lint this situation. + return; + } + + if !block_is_unit { + // Although the expression returns `()`, the block doesn't. This may happen if the expression + // returns early in all code paths, such as a `return value` in the condition of an `if` statement, + // in which case the block type would be `!`. Do not lint in this case, as the statement would + // become the block expression; the block type would become `()` and this may not type correctly + // if the expected type for the block is not `()`. + return; + } } let semi_span = expr.span.shrink_to_hi().to(stmt.span.shrink_to_hi()); diff --git a/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs b/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs index 40ba70d451db..a74eab8b6ae5 100644 --- a/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs @@ -32,7 +32,7 @@ declare_clippy_lint! { /// None => 0, /// }; /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.86.0"] pub UNNEEDED_STRUCT_PATTERN, style, "using struct pattern to match against unit variant" diff --git a/src/tools/clippy/clippy_lints/src/utils/attr_collector.rs b/src/tools/clippy/clippy_lints/src/utils/attr_collector.rs index 1522553bbf52..169429811d32 100644 --- a/src/tools/clippy/clippy_lints/src/utils/attr_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/attr_collector.rs @@ -1,14 +1,13 @@ use std::mem; -use std::sync::OnceLock; +use std::sync::{Arc, OnceLock}; use rustc_ast::{Attribute, Crate}; -use rustc_data_structures::sync::Lrc; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; #[derive(Clone, Default)] -pub struct AttrStorage(pub Lrc>>); +pub struct AttrStorage(pub Arc>>); pub struct AttrCollector { storage: AttrStorage, diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index 31ae002e47d9..11c14c147776 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -522,7 +522,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { let replacement = match (format_string_is_raw, replace_raw) { (false, false) => Some(replacement), - (false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")), + (false, true) => Some(replacement.replace('\\', "\\\\").replace('"', "\\\"")), (true, false) => match conservative_unescape(&replacement) { Ok(unescaped) => Some(unescaped), Err(UnescapeErr::Lint) => None, diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 251e3dfe41be..41f3b1cbd507 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-01-28 +nightly-2025-02-06 ``` diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 798f4575c2e1..ab5f97199ce3 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -662,12 +662,12 @@ pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool { && eq_ext(&l.ext, &r.ext) } +#[expect(clippy::ref_option, reason = "This is the type how it is stored in the AST")] pub fn eq_opt_fn_contract(l: &Option>, r: &Option>) -> bool { match (l, r) { (Some(l), Some(r)) => { - eq_expr_opt(l.requires.as_ref(), r.requires.as_ref()) - && eq_expr_opt(l.ensures.as_ref(), r.ensures.as_ref()) - } + eq_expr_opt(l.requires.as_ref(), r.requires.as_ref()) && eq_expr_opt(l.ensures.as_ref(), r.ensures.as_ref()) + }, (None, None) => true, (Some(_), None) | (None, Some(_)) => false, } diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index a660623f4185..fab1db05d2e5 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -4,13 +4,14 @@ //! executable MIR bodies, so we have to do this instead. #![allow(clippy::float_cmp)] +use std::sync::Arc; + use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Half, Quad}; 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, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, @@ -37,7 +38,7 @@ pub enum Constant<'tcx> { /// A `String` (e.g., "abc"). Str(String), /// A binary string (e.g., `b"abc"`). - Binary(Lrc<[u8]>), + Binary(Arc<[u8]>), /// A single `char` (e.g., `'a'`). Char(char), /// An integer's bit representation. @@ -305,7 +306,7 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option>) -> Constan match *lit { LitKind::Str(ref is, _) => Constant::Str(is.to_string()), LitKind::Byte(b) => Constant::Int(u128::from(b)), - LitKind::ByteStr(ref s, _) | LitKind::CStr(ref s, _) => Constant::Binary(Lrc::clone(s)), + LitKind::ByteStr(ref s, _) | LitKind::CStr(ref s, _) => Constant::Binary(Arc::clone(s)), LitKind::Char(c) => Constant::Char(c), LitKind::Int(n, _) => Constant::Int(n.get()), LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 5a5227af9074..337684b68f86 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -341,6 +341,15 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) } +// Checks if arm has the form `None => None` +pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { + matches!( + arm.pat.kind, + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) + if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone) + ) +} + /// Checks if the given `QPath` belongs to a type alias. pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { match *qpath { diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index f4c730ef118b..30fd48fc0605 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -1,12 +1,14 @@ #![allow(clippy::similar_names)] // `expr` and `expn` +use std::sync::Arc; + use crate::get_unique_attr; use crate::visitors::{Descend, for_each_expr_without_closures}; use arrayvec::ArrayVec; use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::{Lrc, OnceLock}; +use rustc_data_structures::sync::OnceLock; use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_span::def_id::DefId; @@ -393,7 +395,7 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> /// Stores AST [`FormatArgs`] nodes for use in late lint passes, as they are in a desugared form in /// the HIR #[derive(Default, Clone)] -pub struct FormatArgsStorage(Lrc>>); +pub struct FormatArgsStorage(Arc>>); impl FormatArgsStorage { /// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs index ccbbccd0dbff..85250f81dc47 100644 --- a/src/tools/clippy/clippy_utils/src/mir/mod.rs +++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs @@ -112,10 +112,14 @@ pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { /// Convenience wrapper around `visit_local_usage`. pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option { - visit_local_usage(&[local], mir, Location { - block: START_BLOCK, - statement_index: 0, - }) + visit_local_usage( + &[local], + mir, + Location { + block: START_BLOCK, + statement_index: 0, + }, + ) .map(|mut vec| { let LocalUsage { local_use_locs, .. } = vec.remove(0); let mut locations = local_use_locs diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index d73cb7e35611..24f73b9df268 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -1,4 +1,5 @@ use rustc_ast::attr::AttributeExt; + use rustc_attr_parsing::{RustcVersion, parse_version}; use rustc_session::Session; use rustc_span::{Symbol, sym}; @@ -18,12 +19,14 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { + 1,84,0 { CONST_OPTION_AS_SLICE } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION } 1,80,0 { BOX_INTO_ITER, LAZY_CELL } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } + 1,75,0 { OPTION_AS_SLICE } 1,74,0 { REPR_RUST } 1,73,0 { MANUAL_DIV_CEIL } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } @@ -40,7 +43,7 @@ msrv_aliases! { 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR } 1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST } 1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS } - 1,50,0 { BOOL_THEN, CLAMP } + 1,50,0 { BOOL_THEN, CLAMP, SLICE_FILL } 1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } 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 5a3a3d0cedc4..c7890f33f27e 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 @@ -179,7 +179,10 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, _) + Rvalue::NullaryOp( + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, + _, + ) | Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index eecbfb3936ac..c6a038f7e0cf 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -2,8 +2,9 @@ #![allow(clippy::module_name_repetitions)] +use std::sync::Arc; + use rustc_ast::{LitKind, StrStyle}; -use rustc_data_structures::sync::Lrc; use rustc_errors::Applicability; use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; use rustc_lint::{EarlyContext, LateContext}; @@ -204,7 +205,7 @@ impl fmt::Display for SourceText { fn get_source_range(sm: &SourceMap, sp: Range) -> Option { let start = sm.lookup_byte_offset(sp.start); let end = sm.lookup_byte_offset(sp.end); - if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos { + if !Arc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos { return None; } sm.ensure_source_file_source_present(&start.sf); @@ -277,7 +278,7 @@ fn trim_start(sm: &SourceMap, sp: Range) -> Range { } pub struct SourceFileRange { - pub sf: Lrc, + pub sf: Arc, pub range: Range, } impl SourceFileRange { @@ -726,12 +727,15 @@ pub fn str_literal_to_char_literal( &snip[1..(snip.len() - 1)] }; - let hint = format!("'{}'", match ch { - "'" => "\\'", - r"\" => "\\\\", - "\\\"" => "\"", // no need to escape `"` in `'"'` - _ => ch, - }); + let hint = format!( + "'{}'", + match ch { + "'" => "\\'", + r"\" => "\\\\", + "\\\"" => "\"", // no need to escape `"` in `'"'` + _ => ch, + } + ); Some(hint) } else { diff --git a/src/tools/clippy/clippy_utils/src/str_utils.rs b/src/tools/clippy/clippy_utils/src/str_utils.rs index 1588ee452dae..421b25a77fe8 100644 --- a/src/tools/clippy/clippy_utils/src/str_utils.rs +++ b/src/tools/clippy/clippy_utils/src/str_utils.rs @@ -370,11 +370,9 @@ mod test { assert_eq!(camel_case_split("AbcDef"), vec!["Abc", "Def"]); assert_eq!(camel_case_split("Abc"), vec!["Abc"]); assert_eq!(camel_case_split("abcDef"), vec!["abc", "Def"]); - assert_eq!(camel_case_split("\u{f6}\u{f6}AabABcd"), vec![ - "\u{f6}\u{f6}", - "Aab", - "A", - "Bcd" - ]); + assert_eq!( + camel_case_split("\u{f6}\u{f6}AabABcd"), + vec!["\u{f6}\u{f6}", "Aab", "A", "Bcd"] + ); } } diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs index af243f94274d..83c3d7aba021 100644 --- a/src/tools/clippy/lintcheck/src/config.rs +++ b/src/tools/clippy/lintcheck/src/config.rs @@ -2,6 +2,7 @@ use clap::{Parser, Subcommand, ValueEnum}; use std::num::NonZero; use std::path::PathBuf; +#[allow(clippy::struct_excessive_bools)] #[derive(Parser, Clone, Debug)] #[command(args_conflicts_with_subcommands = true)] pub(crate) struct LintcheckConfig { @@ -11,6 +12,9 @@ pub(crate) struct LintcheckConfig { short = 'j', value_name = "N", default_value_t = 0, + default_value_if("perf", "true", Some("1")), // Limit jobs to 1 when benchmarking + conflicts_with("perf"), + required = false, hide_default_value = true )] pub max_jobs: usize, @@ -46,6 +50,11 @@ pub(crate) struct LintcheckConfig { /// Run clippy on the dependencies of crates specified in crates-toml #[clap(long, conflicts_with("max_jobs"))] pub recursive: bool, + /// Also produce a `perf.data` file, implies --jobs=1, + /// the `perf.data` file can be found at + /// `target/lintcheck/sources/-/perf.data` + #[clap(long)] + pub perf: bool, #[command(subcommand)] pub subcommand: Option, } diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index e88d9f427bec..8d0d41ab9450 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -116,7 +116,25 @@ impl Crate { clippy_args.extend(lint_levels_args.iter().map(String::as_str)); - let mut cmd = Command::new("cargo"); + let mut cmd; + + if config.perf { + cmd = Command::new("perf"); + cmd.args(&[ + "record", + "-e", + "instructions", // Only count instructions + "-g", // Enable call-graph, useful for flamegraphs and produces richer reports + "--quiet", // Do not tamper with lintcheck's normal output + "-o", + "perf.data", + "--", + "cargo", + ]); + } else { + cmd = Command::new("cargo"); + } + cmd.arg(if config.fix { "fix" } else { "check" }) .arg("--quiet") .current_dir(&self.path) @@ -234,12 +252,22 @@ fn normalize_diag( } /// Builds clippy inside the repo to make sure we have a clippy executable we can use. -fn build_clippy() -> String { - let output = Command::new("cargo") - .args(["run", "--bin=clippy-driver", "--", "--version"]) - .stderr(Stdio::inherit()) - .output() - .unwrap(); +fn build_clippy(release_build: bool) -> String { + let mut build_cmd = Command::new("cargo"); + build_cmd.args([ + "run", + "--bin=clippy-driver", + if release_build { "-r" } else { "" }, + "--", + "--version", + ]); + + if release_build { + build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true"); + } + + let output = build_cmd.stderr(Stdio::inherit()).output().unwrap(); + if !output.status.success() { eprintln!("Error: Failed to compile Clippy!"); std::process::exit(1); @@ -270,13 +298,18 @@ fn main() { #[allow(clippy::too_many_lines)] fn lintcheck(config: LintcheckConfig) { - let clippy_ver = build_clippy(); - let clippy_driver_path = fs::canonicalize(format!("target/debug/clippy-driver{EXE_SUFFIX}")).unwrap(); + let clippy_ver = build_clippy(config.perf); + let clippy_driver_path = fs::canonicalize(format!( + "target/{}/clippy-driver{EXE_SUFFIX}", + if config.perf { "release" } else { "debug" } + )) + .unwrap(); // assert that clippy is found assert!( clippy_driver_path.is_file(), - "target/debug/clippy-driver binary not found! {}", + "target/{}/clippy-driver binary not found! {}", + if config.perf { "release" } else { "debug" }, clippy_driver_path.display() ); diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index c15d1fe6cd34..ab760287e83a 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-01-28" +channel = "nightly-2025-02-06" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/ui/cmp_null.fixed b/src/tools/clippy/tests/ui/cmp_null.fixed new file mode 100644 index 000000000000..e5ab765bc861 --- /dev/null +++ b/src/tools/clippy/tests/ui/cmp_null.fixed @@ -0,0 +1,32 @@ +#![warn(clippy::cmp_null)] +#![allow(unused_mut)] + +use std::ptr; + +fn main() { + let x = 0; + let p: *const usize = &x; + if p.is_null() { + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method + //~| NOTE: `-D clippy::cmp-null` implied by `-D warnings` + println!("This is surprising!"); + } + if p.is_null() { + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method + println!("This is surprising!"); + } + + let mut y = 0; + let mut m: *mut usize = &mut y; + if m.is_null() { + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method + println!("This is surprising, too!"); + } + if m.is_null() { + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method + println!("This is surprising, too!"); + } + + let _ = (x as *const ()).is_null(); + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method +} diff --git a/src/tools/clippy/tests/ui/cmp_null.rs b/src/tools/clippy/tests/ui/cmp_null.rs index ef1d93940aa6..257f7ba26627 100644 --- a/src/tools/clippy/tests/ui/cmp_null.rs +++ b/src/tools/clippy/tests/ui/cmp_null.rs @@ -11,10 +11,22 @@ fn main() { //~| NOTE: `-D clippy::cmp-null` implied by `-D warnings` println!("This is surprising!"); } + if ptr::null() == p { + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method + println!("This is surprising!"); + } + let mut y = 0; let mut m: *mut usize = &mut y; if m == ptr::null_mut() { //~^ ERROR: comparing with null is better expressed by the `.is_null()` method println!("This is surprising, too!"); } + if ptr::null_mut() == m { + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method + println!("This is surprising, too!"); + } + + let _ = x as *const () == ptr::null(); + //~^ ERROR: comparing with null is better expressed by the `.is_null()` method } diff --git a/src/tools/clippy/tests/ui/cmp_null.stderr b/src/tools/clippy/tests/ui/cmp_null.stderr index 8362904a5ba9..f3b35f3afba8 100644 --- a/src/tools/clippy/tests/ui/cmp_null.stderr +++ b/src/tools/clippy/tests/ui/cmp_null.stderr @@ -2,16 +2,34 @@ error: comparing with null is better expressed by the `.is_null()` method --> tests/ui/cmp_null.rs:9:8 | LL | if p == ptr::null() { - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: try: `p.is_null()` | = note: `-D clippy::cmp-null` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cmp_null)]` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:16:8 + --> tests/ui/cmp_null.rs:14:8 + | +LL | if ptr::null() == p { + | ^^^^^^^^^^^^^^^^ help: try: `p.is_null()` + +error: comparing with null is better expressed by the `.is_null()` method + --> tests/ui/cmp_null.rs:21:8 | LL | if m == ptr::null_mut() { - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()` -error: aborting due to 2 previous errors +error: comparing with null is better expressed by the `.is_null()` method + --> tests/ui/cmp_null.rs:25:8 + | +LL | if ptr::null_mut() == m { + | ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()` + +error: comparing with null is better expressed by the `.is_null()` method + --> tests/ui/cmp_null.rs:30:13 + | +LL | let _ = x as *const () == ptr::null(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(x as *const ()).is_null()` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed index f1baf28200e9..abccc30ef87d 100644 --- a/src/tools/clippy/tests/ui/eta.fixed +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -116,6 +116,11 @@ fn test_redundant_closures_containing_method_calls() { t.iter().filter(|x| x.trait_foo_ref()); t.iter().map(|x| x.trait_foo_ref()); } + + fn issue14096() { + let x = Some("42"); + let _ = x.map(str::parse::); + } } struct Thunk(Box T>); diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs index c52a51880bfc..9bcee4eba340 100644 --- a/src/tools/clippy/tests/ui/eta.rs +++ b/src/tools/clippy/tests/ui/eta.rs @@ -116,6 +116,11 @@ fn test_redundant_closures_containing_method_calls() { t.iter().filter(|x| x.trait_foo_ref()); t.iter().map(|x| x.trait_foo_ref()); } + + fn issue14096() { + let x = Some("42"); + let _ = x.map(|x| x.parse::()); + } } struct Thunk(Box T>); diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr index 1731a4377f53..ac58e87bc5ec 100644 --- a/src/tools/clippy/tests/ui/eta.stderr +++ b/src/tools/clippy/tests/ui/eta.stderr @@ -71,142 +71,148 @@ LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_as | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> tests/ui/eta.rs:169:22 + --> tests/ui/eta.rs:122:23 + | +LL | let _ = x.map(|x| x.parse::()); + | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `str::parse::` + +error: redundant closure + --> tests/ui/eta.rs:174:22 | LL | requires_fn_once(|| x()); | ^^^^^^ help: replace the closure with the function itself: `x` error: redundant closure - --> tests/ui/eta.rs:176:27 + --> tests/ui/eta.rs:181:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> tests/ui/eta.rs:181:27 + --> tests/ui/eta.rs:186:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` error: redundant closure - --> tests/ui/eta.rs:213:28 + --> tests/ui/eta.rs:218:28 | LL | x.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> tests/ui/eta.rs:214:28 + --> tests/ui/eta.rs:219:28 | LL | y.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> tests/ui/eta.rs:215:28 + --> tests/ui/eta.rs:220:28 | LL | z.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res` error: redundant closure - --> tests/ui/eta.rs:222:21 + --> tests/ui/eta.rs:227:21 | LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> tests/ui/eta.rs:226:21 + --> tests/ui/eta.rs:231:21 | LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` error: redundant closure - --> tests/ui/eta.rs:319:18 + --> tests/ui/eta.rs:324:18 | LL | takes_fn_mut(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> tests/ui/eta.rs:322:19 + --> tests/ui/eta.rs:327:19 | LL | takes_fn_once(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> tests/ui/eta.rs:326:26 + --> tests/ui/eta.rs:331:26 | LL | move || takes_fn_mut(|| f_used_once()) | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` error: redundant closure - --> tests/ui/eta.rs:338:19 + --> tests/ui/eta.rs:343:19 | LL | array_opt.map(|a| a.as_slice()); | ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice` error: redundant closure - --> tests/ui/eta.rs:341:19 + --> tests/ui/eta.rs:346:19 | LL | slice_opt.map(|s| s.len()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len` error: redundant closure - --> tests/ui/eta.rs:344:17 + --> tests/ui/eta.rs:349:17 | LL | ptr_opt.map(|p| p.is_null()); | ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null` error: redundant closure - --> tests/ui/eta.rs:348:17 + --> tests/ui/eta.rs:353:17 | LL | dyn_opt.map(|d| d.method_on_dyn()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `::method_on_dyn` error: redundant closure - --> tests/ui/eta.rs:408:19 + --> tests/ui/eta.rs:413:19 | LL | let _ = f(&0, |x, y| f2(x, y)); | ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2` error: redundant closure - --> tests/ui/eta.rs:436:22 + --> tests/ui/eta.rs:441:22 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Test::method` error: redundant closure - --> tests/ui/eta.rs:440:22 + --> tests/ui/eta.rs:445:22 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `super::Outer::method` error: redundant closure - --> tests/ui/eta.rs:453:18 + --> tests/ui/eta.rs:458:18 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `test_mod::Test::method` error: redundant closure - --> tests/ui/eta.rs:460:30 + --> tests/ui/eta.rs:465:30 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method` error: redundant closure - --> tests/ui/eta.rs:479:38 + --> tests/ui/eta.rs:484:38 | LL | let x = Box::new(|| None.map(|x| f(x))); | ^^^^^^^^ help: replace the closure with the function itself: `&f` error: redundant closure - --> tests/ui/eta.rs:483:38 + --> tests/ui/eta.rs:488:38 | LL | let x = Box::new(|| None.map(|x| f(x))); | ^^^^^^^^ help: replace the closure with the function itself: `f` error: redundant closure - --> tests/ui/eta.rs:500:35 + --> tests/ui/eta.rs:505:35 | LL | let _field = bind.or_else(|| get_default()).unwrap(); | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default` -error: aborting due to 34 previous errors +error: aborting due to 35 previous errors diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed index fde404373098..118f0b488952 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed @@ -21,12 +21,15 @@ fn main() { let _ = foo().map_err(|()| todo!()); //~^ ERROR: matching over `()` is more explicit - println!("{:?}", match foo() { - Ok(()) => {}, - //~^ ERROR: matching over `()` is more explicit - Err(()) => {}, - //~^ ERROR: matching over `()` is more explicit - }); + println!( + "{:?}", + match foo() { + Ok(()) => {}, + //~^ ERROR: matching over `()` is more explicit + Err(()) => {}, + //~^ ERROR: matching over `()` is more explicit + } + ); } // ignored_unit_patterns in derive macro should be ok diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs index 528844d76e05..92feb9e6c281 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs @@ -21,12 +21,15 @@ fn main() { let _ = foo().map_err(|_| todo!()); //~^ ERROR: matching over `()` is more explicit - println!("{:?}", match foo() { - Ok(_) => {}, - //~^ ERROR: matching over `()` is more explicit - Err(_) => {}, - //~^ ERROR: matching over `()` is more explicit - }); + println!( + "{:?}", + match foo() { + Ok(_) => {}, + //~^ ERROR: matching over `()` is more explicit + Err(_) => {}, + //~^ ERROR: matching over `()` is more explicit + } + ); } // ignored_unit_patterns in derive macro should be ok diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr index 54ff4454d6bd..00a254e39192 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr @@ -26,31 +26,31 @@ LL | let _ = foo().map_err(|_| todo!()); | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:25:12 + --> tests/ui/ignored_unit_patterns.rs:27:16 | -LL | Ok(_) => {}, - | ^ help: use `()` instead of `_`: `()` +LL | Ok(_) => {}, + | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:27:13 + --> tests/ui/ignored_unit_patterns.rs:29:17 | -LL | Err(_) => {}, - | ^ help: use `()` instead of `_`: `()` +LL | Err(_) => {}, + | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:38:9 + --> tests/ui/ignored_unit_patterns.rs:41:9 | LL | let _ = foo().unwrap(); | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:47:13 + --> tests/ui/ignored_unit_patterns.rs:50:13 | LL | (1, _) => unimplemented!(), | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:54:13 + --> tests/ui/ignored_unit_patterns.rs:57:13 | LL | for (x, _) in v { | ^ help: use `()` instead of `_`: `()` diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs index c917fa7f2d0b..2f8640cd3f50 100644 --- a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs +++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.rs @@ -1,5 +1,10 @@ #![warn(clippy::manual_memcpy)] -#![allow(clippy::assigning_clones, clippy::useless_vec, clippy::needless_range_loop)] +#![allow( + clippy::assigning_clones, + clippy::useless_vec, + clippy::needless_range_loop, + clippy::manual_slice_fill +)] //@no-rustfix const LOOP_OFFSET: usize = 5000; diff --git a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr index 803053b2db2e..c881e3fac769 100644 --- a/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr +++ b/src/tools/clippy/tests/ui/manual_memcpy/without_loop_counters.stderr @@ -1,5 +1,5 @@ error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:9:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:14:5 | LL | / for i in 0..src.len() { LL | | @@ -12,7 +12,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::manual_memcpy)]` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:16:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:21:5 | LL | / for i in 0..src.len() { LL | | @@ -21,7 +21,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].copy_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:22:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:27:5 | LL | / for i in 0..src.len() { LL | | @@ -30,7 +30,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[10..(src.len() + 10)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:28:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:33:5 | LL | / for i in 11..src.len() { LL | | @@ -39,7 +39,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[11..src.len()].copy_from_slice(&src[(11 - 10)..(src.len() - 10)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:34:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:39:5 | LL | / for i in 0..dst.len() { LL | | @@ -48,7 +48,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..dst.len()]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:48:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:53:5 | LL | / for i in 10..256 { LL | | @@ -64,7 +64,7 @@ LL + dst2[(10 + 500)..(256 + 500)].copy_from_slice(&src[10..256]); | error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:61:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:66:5 | LL | / for i in 10..LOOP_OFFSET { LL | | @@ -73,7 +73,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].copy_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:75:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:80:5 | LL | / for i in 0..src_vec.len() { LL | | @@ -82,7 +82,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].copy_from_slice(&src_vec[..]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:105:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:110:5 | LL | / for i in from..from + src.len() { LL | | @@ -91,7 +91,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].copy_from_slice(&src[..(from + src.len() - from)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:110:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:115:5 | LL | / for i in from..from + 3 { LL | | @@ -100,7 +100,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].copy_from_slice(&src[..(from + 3 - from)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:116:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:121:5 | LL | / for i in 0..5 { LL | | @@ -109,7 +109,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:122:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:127:5 | LL | / for i in 0..0 { LL | | @@ -118,7 +118,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..0].copy_from_slice(&src[..0]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:146:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:151:5 | LL | / for i in 0..4 { LL | | @@ -127,7 +127,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..4]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:152:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:157:5 | LL | / for i in 0..5 { LL | | @@ -136,7 +136,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:158:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:163:5 | LL | / for i in 0..5 { LL | | @@ -145,7 +145,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:205:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:210:5 | LL | / for i in 0..5 { LL | | @@ -154,7 +154,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[0]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:211:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:216:5 | LL | / for i in 0..5 { LL | | @@ -163,7 +163,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[0][1]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/without_loop_counters.rs:219:5 + --> tests/ui/manual_memcpy/without_loop_counters.rs:224:5 | LL | / for i in 0..src.len() { LL | | diff --git a/src/tools/clippy/tests/ui/manual_option_as_slice.fixed b/src/tools/clippy/tests/ui/manual_option_as_slice.fixed new file mode 100644 index 000000000000..48337d7654de --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_option_as_slice.fixed @@ -0,0 +1,62 @@ +#![warn(clippy::manual_option_as_slice)] +#![allow(clippy::redundant_closure, clippy::unwrap_or_default)] + +fn check(x: Option) { + _ = x.as_slice(); + + _ = x.as_slice(); + + _ = x.as_slice(); + //~^ manual_option_as_slice + + _ = x.as_slice(); + //~^ manual_option_as_slice + + _ = x.as_slice(); + //~^ manual_option_as_slice + + _ = x.as_slice(); + //~^ manual_option_as_slice + + { + use std::slice::from_ref; + _ = x.as_slice(); + //~^ manual_option_as_slice + } + + // possible false positives + let y = x.as_ref(); + _ = match y { + // as_ref outside + Some(f) => &[f][..], + None => &[][..], + }; + _ = match x.as_ref() { + Some(f) => std::slice::from_ref(f), + None => &[0], + }; + _ = match x.as_ref() { + Some(42) => &[23], + Some(f) => std::slice::from_ref(f), + None => &[], + }; + let b = &[42]; + _ = if let Some(_f) = x.as_ref() { + std::slice::from_ref(b) + } else { + &[] + }; + _ = x.as_ref().map_or(&[42][..], std::slice::from_ref); + _ = x.as_ref().map_or_else(|| &[42][..1], std::slice::from_ref); + _ = x.as_ref().map(|f| std::slice::from_ref(f)).unwrap_or_default(); +} + +#[clippy::msrv = "1.74"] +fn check_msrv(x: Option) { + _ = x.as_ref().map_or(&[][..], std::slice::from_ref); +} + +fn main() { + check(Some(1)); + check_msrv(Some(175)); +} diff --git a/src/tools/clippy/tests/ui/manual_option_as_slice.rs b/src/tools/clippy/tests/ui/manual_option_as_slice.rs new file mode 100644 index 000000000000..561a8b534014 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_option_as_slice.rs @@ -0,0 +1,71 @@ +#![warn(clippy::manual_option_as_slice)] +#![allow(clippy::redundant_closure, clippy::unwrap_or_default)] + +fn check(x: Option) { + _ = match x.as_ref() { + //~^ manual_option_as_slice + Some(f) => std::slice::from_ref(f), + None => &[], + }; + + _ = if let Some(f) = x.as_ref() { + //~^ manual_option_as_slice + std::slice::from_ref(f) + } else { + &[] + }; + + _ = x.as_ref().map_or(&[][..], std::slice::from_ref); + //~^ manual_option_as_slice + + _ = x.as_ref().map_or_else(Default::default, std::slice::from_ref); + //~^ manual_option_as_slice + + _ = x.as_ref().map(std::slice::from_ref).unwrap_or_default(); + //~^ manual_option_as_slice + + _ = x.as_ref().map_or_else(|| &[42][..0], std::slice::from_ref); + //~^ manual_option_as_slice + + { + use std::slice::from_ref; + _ = x.as_ref().map_or_else(<&[_]>::default, from_ref); + //~^ manual_option_as_slice + } + + // possible false positives + let y = x.as_ref(); + _ = match y { + // as_ref outside + Some(f) => &[f][..], + None => &[][..], + }; + _ = match x.as_ref() { + Some(f) => std::slice::from_ref(f), + None => &[0], + }; + _ = match x.as_ref() { + Some(42) => &[23], + Some(f) => std::slice::from_ref(f), + None => &[], + }; + let b = &[42]; + _ = if let Some(_f) = x.as_ref() { + std::slice::from_ref(b) + } else { + &[] + }; + _ = x.as_ref().map_or(&[42][..], std::slice::from_ref); + _ = x.as_ref().map_or_else(|| &[42][..1], std::slice::from_ref); + _ = x.as_ref().map(|f| std::slice::from_ref(f)).unwrap_or_default(); +} + +#[clippy::msrv = "1.74"] +fn check_msrv(x: Option) { + _ = x.as_ref().map_or(&[][..], std::slice::from_ref); +} + +fn main() { + check(Some(1)); + check_msrv(Some(175)); +} diff --git a/src/tools/clippy/tests/ui/manual_option_as_slice.stderr b/src/tools/clippy/tests/ui/manual_option_as_slice.stderr new file mode 100644 index 000000000000..569269d3e2b7 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_option_as_slice.stderr @@ -0,0 +1,58 @@ +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:5:9 + | +LL | _ = match x.as_ref() { + | _________^ +LL | | +LL | | Some(f) => std::slice::from_ref(f), +LL | | None => &[], +LL | | }; + | |_____^ help: use: `x.as_slice()` + | + = note: `-D clippy::manual-option-as-slice` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_option_as_slice)]` + +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:11:9 + | +LL | _ = if let Some(f) = x.as_ref() { + | _________^ +LL | | +LL | | std::slice::from_ref(f) +LL | | } else { +LL | | &[] +LL | | }; + | |_____^ help: use: `x.as_slice()` + +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:18:9 + | +LL | _ = x.as_ref().map_or(&[][..], std::slice::from_ref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:21:9 + | +LL | _ = x.as_ref().map_or_else(Default::default, std::slice::from_ref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:24:9 + | +LL | _ = x.as_ref().map(std::slice::from_ref).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:27:9 + | +LL | _ = x.as_ref().map_or_else(|| &[42][..0], std::slice::from_ref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + +error: use `Option::as_slice` + --> tests/ui/manual_option_as_slice.rs:32:13 + | +LL | _ = x.as_ref().map_or_else(<&[_]>::default, from_ref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.fixed b/src/tools/clippy/tests/ui/manual_slice_fill.fixed new file mode 100644 index 000000000000..397a156a2dc7 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_slice_fill.fixed @@ -0,0 +1,101 @@ +#![warn(clippy::manual_slice_fill)] +#![allow(clippy::needless_range_loop)] + +macro_rules! assign_element { + ($slice:ident, $index:expr) => { + $slice[$index] = 0; + }; +} + +macro_rules! assign_element_2 { + ($i:expr) => { + $i = 0; + }; +} + +struct NoClone; + +fn num() -> usize { + 5 +} + +fn should_lint() { + let mut some_slice = [1, 2, 3, 4, 5]; + + some_slice.fill(0); + + let x = 5; + some_slice.fill(x); + + some_slice.fill(0); + + // This should trigger `manual_slice_fill`, but the applicability is `MaybeIncorrect` since comments + // within the loop might be purely informational. + some_slice.fill(0); +} + +fn should_not_lint() { + let mut some_slice = [1, 2, 3, 4, 5]; + + // Should not lint because we can't determine if the scope of the loop is intended to access all the + // elements of the slice. + for i in 0..5 { + some_slice[i] = 0; + } + + // Should not lint, as using a function to assign values to elements might be + // intentional. For example, the contents of `num()` could be temporary and subject to change + // later. + for i in 0..some_slice.len() { + some_slice[i] = num(); + } + + // Should not lint because this loop isn't equivalent to `fill`. + for i in 0..some_slice.len() { + some_slice[i] = 0; + println!("foo"); + } + + // Should not lint because it may be intentional to use a macro to perform an operation equivalent + // to `fill`. + for i in 0..some_slice.len() { + assign_element!(some_slice, i); + } + + let another_slice = [1, 2, 3]; + // Should not lint because the range is not for `some_slice`. + for i in 0..another_slice.len() { + some_slice[i] = 0; + } + + let mut vec: Vec> = Vec::with_capacity(5); + // Should not lint because `NoClone` does not have `Clone` trait. + for i in 0..vec.len() { + vec[i] = None; + } + + // Should not lint, as using a function to assign values to elements might be + // intentional. For example, the contents of `num()` could be temporary and subject to change + // later. + for i in &mut some_slice { + *i = num(); + } + + // Should not lint because this loop isn't equivalent to `fill`. + for i in &mut some_slice { + *i = 0; + println!("foo"); + } + + // Should not lint because it may be intentional to use a macro to perform an operation equivalent + // to `fill`. + for i in &mut some_slice { + assign_element_2!(*i); + } + + let mut vec: Vec> = Vec::with_capacity(5); + // Should not lint because `NoClone` does not have `Clone` trait. + for i in &mut vec { + *i = None; + } +} diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.rs b/src/tools/clippy/tests/ui/manual_slice_fill.rs new file mode 100644 index 000000000000..c25127ca613a --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_slice_fill.rs @@ -0,0 +1,110 @@ +#![warn(clippy::manual_slice_fill)] +#![allow(clippy::needless_range_loop)] + +macro_rules! assign_element { + ($slice:ident, $index:expr) => { + $slice[$index] = 0; + }; +} + +macro_rules! assign_element_2 { + ($i:expr) => { + $i = 0; + }; +} + +struct NoClone; + +fn num() -> usize { + 5 +} + +fn should_lint() { + let mut some_slice = [1, 2, 3, 4, 5]; + + for i in 0..some_slice.len() { + some_slice[i] = 0; + } + + let x = 5; + for i in 0..some_slice.len() { + some_slice[i] = x; + } + + for i in &mut some_slice { + *i = 0; + } + + // This should trigger `manual_slice_fill`, but the applicability is `MaybeIncorrect` since comments + // within the loop might be purely informational. + for i in 0..some_slice.len() { + some_slice[i] = 0; + // foo + } +} + +fn should_not_lint() { + let mut some_slice = [1, 2, 3, 4, 5]; + + // Should not lint because we can't determine if the scope of the loop is intended to access all the + // elements of the slice. + for i in 0..5 { + some_slice[i] = 0; + } + + // Should not lint, as using a function to assign values to elements might be + // intentional. For example, the contents of `num()` could be temporary and subject to change + // later. + for i in 0..some_slice.len() { + some_slice[i] = num(); + } + + // Should not lint because this loop isn't equivalent to `fill`. + for i in 0..some_slice.len() { + some_slice[i] = 0; + println!("foo"); + } + + // Should not lint because it may be intentional to use a macro to perform an operation equivalent + // to `fill`. + for i in 0..some_slice.len() { + assign_element!(some_slice, i); + } + + let another_slice = [1, 2, 3]; + // Should not lint because the range is not for `some_slice`. + for i in 0..another_slice.len() { + some_slice[i] = 0; + } + + let mut vec: Vec> = Vec::with_capacity(5); + // Should not lint because `NoClone` does not have `Clone` trait. + for i in 0..vec.len() { + vec[i] = None; + } + + // Should not lint, as using a function to assign values to elements might be + // intentional. For example, the contents of `num()` could be temporary and subject to change + // later. + for i in &mut some_slice { + *i = num(); + } + + // Should not lint because this loop isn't equivalent to `fill`. + for i in &mut some_slice { + *i = 0; + println!("foo"); + } + + // Should not lint because it may be intentional to use a macro to perform an operation equivalent + // to `fill`. + for i in &mut some_slice { + assign_element_2!(*i); + } + + let mut vec: Vec> = Vec::with_capacity(5); + // Should not lint because `NoClone` does not have `Clone` trait. + for i in &mut vec { + *i = None; + } +} diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.stderr b/src/tools/clippy/tests/ui/manual_slice_fill.stderr new file mode 100644 index 000000000000..3aa980f69191 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_slice_fill.stderr @@ -0,0 +1,38 @@ +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:25:5 + | +LL | / for i in 0..some_slice.len() { +LL | | some_slice[i] = 0; +LL | | } + | |_____^ help: try: `some_slice.fill(0);` + | + = note: `-D clippy::manual-slice-fill` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_slice_fill)]` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:30:5 + | +LL | / for i in 0..some_slice.len() { +LL | | some_slice[i] = x; +LL | | } + | |_____^ help: try: `some_slice.fill(x);` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:34:5 + | +LL | / for i in &mut some_slice { +LL | | *i = 0; +LL | | } + | |_____^ help: try: `some_slice.fill(0);` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:40:5 + | +LL | / for i in 0..some_slice.len() { +LL | | some_slice[i] = 0; +LL | | // foo +LL | | } + | |_____^ help: try: `some_slice.fill(0);` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default_unfixable.rs b/src/tools/clippy/tests/ui/manual_unwrap_or_default_unfixable.rs new file mode 100644 index 000000000000..acc54b52816c --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default_unfixable.rs @@ -0,0 +1,15 @@ +//@no-rustfix +fn issue_12670() { + // no auto: type not found + #[allow(clippy::match_result_ok)] + let _ = if let Some(x) = "1".parse().ok() { + x + } else { + i32::default() + }; + let _ = if let Some(x) = None { x } else { i32::default() }; + // auto fix with unwrap_or_default + let a: Option = None; + let _ = if let Some(x) = a { x } else { i32::default() }; + let _ = if let Some(x) = Some(99) { x } else { i32::default() }; +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default_unfixable.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or_default_unfixable.stderr new file mode 100644 index 000000000000..3849d33cf254 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default_unfixable.stderr @@ -0,0 +1,34 @@ +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default_unfixable.rs:5:13 + | +LL | let _ = if let Some(x) = "1".parse().ok() { + | _____________^ +LL | | x +LL | | } else { +LL | | i32::default() +LL | | }; + | |_____^ help: ascribe the type i32 and replace your expression with: `"1".parse().ok().unwrap_or_default()` + | + = note: `-D clippy::manual-unwrap-or-default` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_unwrap_or_default)]` + +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default_unfixable.rs:10:13 + | +LL | let _ = if let Some(x) = None { x } else { i32::default() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `None::.unwrap_or_default()` + +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default_unfixable.rs:13:13 + | +LL | let _ = if let Some(x) = a { x } else { i32::default() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.unwrap_or_default()` + +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default_unfixable.rs:14:13 + | +LL | let _ = if let Some(x) = Some(99) { x } else { i32::default() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(99).unwrap_or_default()` + +error: aborting due to 4 previous errors + 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 000000000000..6514b67ef7a7 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_option_take.fixed @@ -0,0 +1,58 @@ +struct MyStruct; + +impl MyStruct { + pub fn get_option() -> Option { + todo!() + } +} + +fn return_option() -> Option { + todo!() +} + +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(); + + let x = Some(3); + x.as_ref(); + //~^ ERROR: called `Option::take()` on a temporary value + + println!("Testing non erroneous option_take_on_temporary"); + let mut x = Some(3); + let y = x.as_mut(); + + let mut x = Some(3); + let y = x.as_mut(); + //~^ ERROR: called `Option::take()` on a temporary value + let y = x.replace(289); + //~^ ERROR: called `Option::take()` on a temporary value + + let y = Some(3).as_mut(); + //~^ ERROR: called `Option::take()` on a temporary value + + let y = Option::as_mut(&mut x); + //~^ ERROR: called `Option::take()` on a temporary value + + let x = return_option(); + let x = return_option(); + //~^ ERROR: called `Option::take()` on a temporary value + + let x = MyStruct::get_option(); + let x = MyStruct::get_option(); + //~^ ERROR: called `Option::take()` on a temporary value + + let mut my_vec = vec![1, 2, 3]; + my_vec.push(4); + let y = my_vec.first(); + let y = my_vec.first(); + //~^ ERROR: called `Option::take()` on a temporary value + + let y = my_vec.first(); + //~^ ERROR: called `Option::take()` on a temporary value +} diff --git a/src/tools/clippy/tests/ui/needless_option_take.stderr b/src/tools/clippy/tests/ui/needless_option_take.stderr index e036bd53170a..3fc339ed79e2 100644 --- a/src/tools/clippy/tests/ui/needless_option_take.stderr +++ b/src/tools/clippy/tests/ui/needless_option_take.stderr @@ -2,7 +2,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:23:5 | LL | x.as_ref().take(); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^------- + | | + | help: remove | = note: `as_ref` creates a temporary value, so calling take() has no effect = note: `-D clippy::needless-option-take` implied by `-D warnings` @@ -12,7 +14,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:31:13 | LL | let y = x.as_mut().take(); - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^------- + | | + | help: remove | = note: `as_mut` creates a temporary value, so calling take() has no effect @@ -20,7 +24,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:33:13 | LL | let y = x.replace(289).take(); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `replace` creates a temporary value, so calling take() has no effect @@ -28,7 +34,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:36:13 | LL | let y = Some(3).as_mut().take(); - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `as_mut` creates a temporary value, so calling take() has no effect @@ -36,7 +44,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:39:13 | LL | let y = Option::as_mut(&mut x).take(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `as_mut` creates a temporary value, so calling take() has no effect @@ -44,7 +54,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:43:13 | LL | let x = return_option().take(); - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `return_option` creates a temporary value, so calling take() has no effect @@ -52,7 +64,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:47:13 | LL | let x = MyStruct::get_option().take(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `get_option` creates a temporary value, so calling take() has no effect @@ -60,7 +74,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:53:13 | LL | let y = my_vec.first().take(); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `first` creates a temporary value, so calling take() has no effect @@ -68,7 +84,9 @@ error: called `Option::take()` on a temporary value --> tests/ui/needless_option_take.rs:56:13 | LL | let y = my_vec.first().take(); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^------- + | | + | help: remove | = note: `first` creates a temporary value, so calling take() has no effect diff --git a/src/tools/clippy/tests/ui/needless_range_loop.rs b/src/tools/clippy/tests/ui/needless_range_loop.rs index 3f2421953301..75f1896eded1 100644 --- a/src/tools/clippy/tests/ui/needless_range_loop.rs +++ b/src/tools/clippy/tests/ui/needless_range_loop.rs @@ -2,7 +2,8 @@ #![allow( clippy::uninlined_format_args, clippy::unnecessary_literal_unwrap, - clippy::useless_vec + clippy::useless_vec, + clippy::manual_slice_fill )] //@no-rustfix static STATIC: [usize; 4] = [0, 1, 8, 16]; diff --git a/src/tools/clippy/tests/ui/needless_range_loop.stderr b/src/tools/clippy/tests/ui/needless_range_loop.stderr index dc2cf437e02e..503d796e5e8d 100644 --- a/src/tools/clippy/tests/ui/needless_range_loop.stderr +++ b/src/tools/clippy/tests/ui/needless_range_loop.stderr @@ -1,5 +1,5 @@ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:15:14 + --> tests/ui/needless_range_loop.rs:16:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | for in &vec { | ~~~~~~ ~~~~ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:26:14 + --> tests/ui/needless_range_loop.rs:27:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | for in &vec { | ~~~~~~ ~~~~ error: the loop variable `j` is only used to index `STATIC` - --> tests/ui/needless_range_loop.rs:32:14 + --> tests/ui/needless_range_loop.rs:33:14 | LL | for j in 0..4 { | ^^^^ @@ -34,7 +34,7 @@ LL | for in &STATIC { | ~~~~~~ ~~~~~~~ error: the loop variable `j` is only used to index `CONST` - --> tests/ui/needless_range_loop.rs:37:14 + --> tests/ui/needless_range_loop.rs:38:14 | LL | for j in 0..4 { | ^^^^ @@ -45,7 +45,7 @@ LL | for in &CONST { | ~~~~~~ ~~~~~~ error: the loop variable `i` is used to index `vec` - --> tests/ui/needless_range_loop.rs:42:14 + --> tests/ui/needless_range_loop.rs:43:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | for (i, ) in vec.iter().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec2` - --> tests/ui/needless_range_loop.rs:51:14 + --> tests/ui/needless_range_loop.rs:52:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL | for in vec2.iter().take(vec.len()) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:56:14 + --> tests/ui/needless_range_loop.rs:57:14 | LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ @@ -78,7 +78,7 @@ LL | for in vec.iter().skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:61:14 + --> tests/ui/needless_range_loop.rs:62:14 | LL | for i in 0..MAX_LEN { | ^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | for in vec.iter().take(MAX_LEN) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:66:14 + --> tests/ui/needless_range_loop.rs:67:14 | LL | for i in 0..=MAX_LEN { | ^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | for in vec.iter().take(MAX_LEN + 1) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:71:14 + --> tests/ui/needless_range_loop.rs:72:14 | LL | for i in 5..10 { | ^^^^^ @@ -111,7 +111,7 @@ LL | for in vec.iter().take(10).skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is only used to index `vec` - --> tests/ui/needless_range_loop.rs:76:14 + --> tests/ui/needless_range_loop.rs:77:14 | LL | for i in 5..=10 { | ^^^^^^ @@ -122,7 +122,7 @@ LL | for in vec.iter().take(10 + 1).skip(5) { | ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> tests/ui/needless_range_loop.rs:81:14 + --> tests/ui/needless_range_loop.rs:82:14 | LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL | for (i, ) in vec.iter().enumerate().skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> tests/ui/needless_range_loop.rs:86:14 + --> tests/ui/needless_range_loop.rs:87:14 | LL | for i in 5..10 { | ^^^^^ @@ -144,7 +144,7 @@ LL | for (i, ) in vec.iter().enumerate().take(10).skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: the loop variable `i` is used to index `vec` - --> tests/ui/needless_range_loop.rs:92:14 + --> tests/ui/needless_range_loop.rs:93:14 | LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/precedence.fixed b/src/tools/clippy/tests/ui/precedence.fixed index 9864dd2550b6..52144a18bac0 100644 --- a/src/tools/clippy/tests/ui/precedence.fixed +++ b/src/tools/clippy/tests/ui/precedence.fixed @@ -20,10 +20,10 @@ fn main() { 1 ^ (1 - 1); 3 | (2 - 1); 3 & (5 - 2); - 0x0F00 & (0x00F0 << 4); - 0x0F00 & (0xF000 >> 4); - (0x0F00 << 1) ^ 3; - (0x0F00 << 1) | 2; + 0x0F00 & 0x00F0 << 4; + 0x0F00 & 0xF000 >> 4; + 0x0F00 << 1 ^ 3; + 0x0F00 << 1 | 2; let b = 3; trip!(b * 8); diff --git a/src/tools/clippy/tests/ui/precedence.stderr b/src/tools/clippy/tests/ui/precedence.stderr index 329422cb8a69..68ad5cb4829a 100644 --- a/src/tools/clippy/tests/ui/precedence.stderr +++ b/src/tools/clippy/tests/ui/precedence.stderr @@ -43,29 +43,5 @@ error: operator precedence might not be obvious LL | 3 & 5 - 2; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)` -error: operator precedence might not be obvious - --> tests/ui/precedence.rs:23:5 - | -LL | 0x0F00 & 0x00F0 << 4; - | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)` - -error: operator precedence might not be obvious - --> tests/ui/precedence.rs:24:5 - | -LL | 0x0F00 & 0xF000 >> 4; - | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)` - -error: operator precedence might not be obvious - --> tests/ui/precedence.rs:25:5 - | -LL | 0x0F00 << 1 ^ 3; - | ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3` - -error: operator precedence might not be obvious - --> tests/ui/precedence.rs:26:5 - | -LL | 0x0F00 << 1 | 2; - | ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) | 2` - -error: aborting due to 11 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/precedence_bits.fixed b/src/tools/clippy/tests/ui/precedence_bits.fixed new file mode 100644 index 000000000000..82fea0d14e43 --- /dev/null +++ b/src/tools/clippy/tests/ui/precedence_bits.fixed @@ -0,0 +1,35 @@ +#![warn(clippy::precedence_bits)] +#![allow( + unused_must_use, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::precedence +)] +#![allow(clippy::identity_op)] +#![allow(clippy::eq_op)] + +macro_rules! trip { + ($a:expr) => { + match $a & 0b1111_1111u8 { + 0 => println!("a is zero ({})", $a), + _ => println!("a is {}", $a), + } + }; +} + +fn main() { + 1 << 2 + 3; + 1 + 2 << 3; + 4 >> 1 + 1; + 1 + 3 >> 2; + 1 ^ 1 - 1; + 3 | 2 - 1; + 3 & 5 - 2; + 0x0F00 & (0x00F0 << 4); + 0x0F00 & (0xF000 >> 4); + (0x0F00 << 1) ^ 3; + (0x0F00 << 1) | 2; + + let b = 3; + trip!(b * 8); +} diff --git a/src/tools/clippy/tests/ui/precedence_bits.rs b/src/tools/clippy/tests/ui/precedence_bits.rs new file mode 100644 index 000000000000..9b353308b6ee --- /dev/null +++ b/src/tools/clippy/tests/ui/precedence_bits.rs @@ -0,0 +1,35 @@ +#![warn(clippy::precedence_bits)] +#![allow( + unused_must_use, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::precedence +)] +#![allow(clippy::identity_op)] +#![allow(clippy::eq_op)] + +macro_rules! trip { + ($a:expr) => { + match $a & 0b1111_1111u8 { + 0 => println!("a is zero ({})", $a), + _ => println!("a is {}", $a), + } + }; +} + +fn main() { + 1 << 2 + 3; + 1 + 2 << 3; + 4 >> 1 + 1; + 1 + 3 >> 2; + 1 ^ 1 - 1; + 3 | 2 - 1; + 3 & 5 - 2; + 0x0F00 & 0x00F0 << 4; + 0x0F00 & 0xF000 >> 4; + 0x0F00 << 1 ^ 3; + 0x0F00 << 1 | 2; + + let b = 3; + trip!(b * 8); +} diff --git a/src/tools/clippy/tests/ui/precedence_bits.stderr b/src/tools/clippy/tests/ui/precedence_bits.stderr new file mode 100644 index 000000000000..f468186b363c --- /dev/null +++ b/src/tools/clippy/tests/ui/precedence_bits.stderr @@ -0,0 +1,29 @@ +error: operator precedence might not be obvious + --> tests/ui/precedence_bits.rs:28:5 + | +LL | 0x0F00 & 0x00F0 << 4; + | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)` + | + = note: `-D clippy::precedence-bits` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::precedence_bits)]` + +error: operator precedence might not be obvious + --> tests/ui/precedence_bits.rs:29:5 + | +LL | 0x0F00 & 0xF000 >> 4; + | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)` + +error: operator precedence might not be obvious + --> tests/ui/precedence_bits.rs:30:5 + | +LL | 0x0F00 << 1 ^ 3; + | ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3` + +error: operator precedence might not be obvious + --> tests/ui/precedence_bits.rs:31:5 + | +LL | 0x0F00 << 1 | 2; + | ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) | 2` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/print_literal.fixed b/src/tools/clippy/tests/ui/print_literal.fixed index 1705a7ff01bb..328e9a9b999f 100644 --- a/src/tools/clippy/tests/ui/print_literal.fixed +++ b/src/tools/clippy/tests/ui/print_literal.fixed @@ -66,3 +66,17 @@ fn main() { println!("mixed: {{hello}} {world}"); } + +fn issue_13959() { + println!("\""); + println!( + " + foo + \\ + \\\\ + \" + \\\" + bar +" + ); +} diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs index d10b26b5887c..3130d0b6998e 100644 --- a/src/tools/clippy/tests/ui/print_literal.rs +++ b/src/tools/clippy/tests/ui/print_literal.rs @@ -66,3 +66,18 @@ fn main() { println!("mixed: {} {world}", "{hello}"); } + +fn issue_13959() { + println!("{}", r#"""#); + println!( + "{}", + r#" + foo + \ + \\ + " + \" + bar +"# + ); +} diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr index c4cbb8bed707..d967b7c24070 100644 --- a/src/tools/clippy/tests/ui/print_literal.stderr +++ b/src/tools/clippy/tests/ui/print_literal.stderr @@ -192,5 +192,41 @@ LL - println!("mixed: {} {world}", "{hello}"); LL + println!("mixed: {{hello}} {world}"); | -error: aborting due to 16 previous errors +error: literal with an empty format string + --> tests/ui/print_literal.rs:71:20 + | +LL | println!("{}", r#"""#); + | ^^^^^^ + | +help: try + | +LL - println!("{}", r#"""#); +LL + println!("\""); + | + +error: literal with an empty format string + --> tests/ui/print_literal.rs:74:9 + | +LL | / r#" +LL | | foo +LL | | \ +LL | | \\ +... | +LL | | bar +LL | | "# + | |__^ + | +help: try + | +LL ~ " +LL + foo +LL + \\ +LL + \\\\ +LL + \" +LL + \\\" +LL + bar +LL ~ " + | + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_else.fixed b/src/tools/clippy/tests/ui/redundant_else.fixed new file mode 100644 index 000000000000..47aa79302d2c --- /dev/null +++ b/src/tools/clippy/tests/ui/redundant_else.fixed @@ -0,0 +1,154 @@ +#![warn(clippy::redundant_else)] +#![allow(clippy::needless_return, clippy::if_same_then_else, clippy::needless_late_init)] + +fn main() { + loop { + // break + if foo() { + println!("Love your neighbor;"); + break; + } + //~^ ERROR: redundant else block + println!("yet don't pull down your hedge."); + // continue + if foo() { + println!("He that lies down with Dogs,"); + continue; + } + //~^ ERROR: redundant else block + println!("shall rise up with fleas."); + // match block + if foo() { + match foo() { + 1 => break, + _ => return, + } + } + //~^ ERROR: redundant else block + println!("You may delay, but time will not."); + } + // else if + if foo() { + return; + } else if foo() { + return; + } + //~^ ERROR: redundant else block + println!("A fat kitchen makes a lean will."); + // let binding outside of block + let _ = { + if foo() { + return; + } + //~^ ERROR: redundant else block + 1 + }; + // else if with let binding outside of block + let _ = { + if foo() { + return; + } else if foo() { + return; + } + //~^ ERROR: redundant else block + 2 + }; + // inside if let + let _ = if let Some(1) = foo() { + let _ = 1; + if foo() { + return; + } + //~^ ERROR: redundant else block + 1 + } else { + 1 + }; + + // + // non-lint cases + // + + // sanity check + if foo() { + let _ = 1; + } else { + println!("Who is wise? He that learns from every one."); + } + // else if without else + if foo() { + return; + } else if foo() { + foo() + }; + // nested if return + if foo() { + if foo() { + return; + } + } else { + foo() + }; + // match with non-breaking branch + if foo() { + match foo() { + 1 => foo(), + _ => return, + } + } else { + println!("Three may keep a secret, if two of them are dead."); + } + // let binding + let _ = if foo() { + return; + } else { + 1 + }; + // assign + let mut a; + a = if foo() { + return; + } else { + 1 + }; + // assign-op + a += if foo() { + return; + } else { + 1 + }; + // if return else if else + if foo() { + return; + } else if foo() { + 1 + } else { + 2 + }; + // if else if return else + if foo() { + 1 + } else if foo() { + return; + } else { + 2 + }; + // else if with let binding + let _ = if foo() { + return; + } else if foo() { + return; + } else { + 2 + }; + // inside function call + Box::new(if foo() { + return; + } else { + 1 + }); +} + +fn foo() -> T { + unimplemented!("I'm not Santa Claus") +} diff --git a/src/tools/clippy/tests/ui/redundant_else.stderr b/src/tools/clippy/tests/ui/redundant_else.stderr index b649a210b5fa..ecc16f7cda5e 100644 --- a/src/tools/clippy/tests/ui/redundant_else.stderr +++ b/src/tools/clippy/tests/ui/redundant_else.stderr @@ -1,88 +1,123 @@ error: redundant else block - --> tests/ui/redundant_else.rs:10:16 + --> tests/ui/redundant_else.rs:10:10 | LL | } else { - | ________________^ + | __________^ LL | | LL | | println!("yet don't pull down your hedge."); LL | | } | |_________^ | - = help: remove the `else` block and move the contents out = note: `-D clippy::redundant-else` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::redundant_else)]` +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + println!("yet don't pull down your hedge."); + | error: redundant else block - --> tests/ui/redundant_else.rs:18:16 + --> tests/ui/redundant_else.rs:18:10 | LL | } else { - | ________________^ + | __________^ LL | | LL | | println!("shall rise up with fleas."); LL | | } | |_________^ | - = help: remove the `else` block and move the contents out +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + println!("shall rise up with fleas."); + | error: redundant else block - --> tests/ui/redundant_else.rs:28:16 + --> tests/ui/redundant_else.rs:28:10 | LL | } else { - | ________________^ + | __________^ LL | | LL | | println!("You may delay, but time will not."); LL | | } | |_________^ | - = help: remove the `else` block and move the contents out +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + println!("You may delay, but time will not."); + | error: redundant else block - --> tests/ui/redundant_else.rs:38:12 + --> tests/ui/redundant_else.rs:38:6 | LL | } else { - | ____________^ + | ______^ LL | | LL | | println!("A fat kitchen makes a lean will."); LL | | } | |_____^ | - = help: remove the `else` block and move the contents out +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + println!("A fat kitchen makes a lean will."); + | error: redundant else block - --> tests/ui/redundant_else.rs:46:16 + --> tests/ui/redundant_else.rs:46:10 | LL | } else { - | ________________^ + | __________^ LL | | LL | | 1 LL | | } | |_________^ | - = help: remove the `else` block and move the contents out +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + 1 + | error: redundant else block - --> tests/ui/redundant_else.rs:57:16 + --> tests/ui/redundant_else.rs:57:10 | LL | } else { - | ________________^ + | __________^ LL | | LL | | 2 LL | | } | |_________^ | - = help: remove the `else` block and move the contents out +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + 2 + | error: redundant else block - --> tests/ui/redundant_else.rs:67:16 + --> tests/ui/redundant_else.rs:67:10 | LL | } else { - | ________________^ + | __________^ LL | | LL | | 1 LL | | } | |_________^ | - = help: remove the `else` block and move the contents out +help: remove the `else` block and move the contents out + | +LL ~ } +LL + +LL + 1 + | error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/return_and_then.fixed b/src/tools/clippy/tests/ui/return_and_then.fixed new file mode 100644 index 000000000000..9736a51ac868 --- /dev/null +++ b/src/tools/clippy/tests/ui/return_and_then.fixed @@ -0,0 +1,67 @@ +#![warn(clippy::return_and_then)] + +fn main() { + fn test_opt_block(opt: Option) -> Option { + let n = opt?; + let mut ret = n + 1; + ret += n; + if n > 1 { Some(ret) } else { None } + } + + fn test_opt_func(opt: Option) -> Option { + let n = opt?; + test_opt_block(Some(n)) + } + + fn test_call_chain() -> Option { + let n = gen_option(1)?; + test_opt_block(Some(n)) + } + + fn test_res_block(opt: Result) -> Result { + let n = opt?; + if n > 1 { Ok(n + 1) } else { Err(n) } + } + + fn test_res_func(opt: Result) -> Result { + let n = opt?; + test_res_block(Ok(n)) + } + + fn test_ref_only() -> Option { + // ref: empty string + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + } + + fn test_tmp_only() -> Option { + // unused temporary: vec![1, 2, 4] + let x = Some(match (vec![1, 2, 3], vec![1, 2, 4]) { + (a, _) if a.len() > 1 => a, + (_, b) => b, + })?; + if x.len() > 2 { Some(3) } else { None } + } + + // should not lint + fn test_tmp_ref() -> Option { + String::from("") + .strip_prefix("<") + .and_then(|s| s.strip_suffix(">").map(String::from)) + } + + // should not lint + fn test_unconsumed_tmp() -> Option { + [1, 2, 3] + .iter() + .map(|x| x + 1) + .collect::>() // temporary Vec created here + .as_slice() // creates temporary slice + .first() // creates temporary reference + .and_then(|x| test_opt_block(Some(*x))) + } +} + +fn gen_option(n: i32) -> Option { + Some(n) +} diff --git a/src/tools/clippy/tests/ui/return_and_then.rs b/src/tools/clippy/tests/ui/return_and_then.rs new file mode 100644 index 000000000000..8bcbdfc3a632 --- /dev/null +++ b/src/tools/clippy/tests/ui/return_and_then.rs @@ -0,0 +1,63 @@ +#![warn(clippy::return_and_then)] + +fn main() { + fn test_opt_block(opt: Option) -> Option { + opt.and_then(|n| { + let mut ret = n + 1; + ret += n; + if n > 1 { Some(ret) } else { None } + }) + } + + fn test_opt_func(opt: Option) -> Option { + opt.and_then(|n| test_opt_block(Some(n))) + } + + fn test_call_chain() -> Option { + gen_option(1).and_then(|n| test_opt_block(Some(n))) + } + + fn test_res_block(opt: Result) -> Result { + opt.and_then(|n| if n > 1 { Ok(n + 1) } else { Err(n) }) + } + + fn test_res_func(opt: Result) -> Result { + opt.and_then(|n| test_res_block(Ok(n))) + } + + fn test_ref_only() -> Option { + // ref: empty string + Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) + } + + fn test_tmp_only() -> Option { + // unused temporary: vec![1, 2, 4] + Some(match (vec![1, 2, 3], vec![1, 2, 4]) { + (a, _) if a.len() > 1 => a, + (_, b) => b, + }) + .and_then(|x| if x.len() > 2 { Some(3) } else { None }) + } + + // should not lint + fn test_tmp_ref() -> Option { + String::from("") + .strip_prefix("<") + .and_then(|s| s.strip_suffix(">").map(String::from)) + } + + // should not lint + fn test_unconsumed_tmp() -> Option { + [1, 2, 3] + .iter() + .map(|x| x + 1) + .collect::>() // temporary Vec created here + .as_slice() // creates temporary slice + .first() // creates temporary reference + .and_then(|x| test_opt_block(Some(*x))) + } +} + +fn gen_option(n: i32) -> Option { + Some(n) +} diff --git a/src/tools/clippy/tests/ui/return_and_then.stderr b/src/tools/clippy/tests/ui/return_and_then.stderr new file mode 100644 index 000000000000..b2e8bf2ca45a --- /dev/null +++ b/src/tools/clippy/tests/ui/return_and_then.stderr @@ -0,0 +1,101 @@ +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:5:9 + | +LL | / opt.and_then(|n| { +LL | | let mut ret = n + 1; +LL | | ret += n; +LL | | if n > 1 { Some(ret) } else { None } +LL | | }) + | |__________^ + | + = note: `-D clippy::return-and-then` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::return_and_then)]` +help: try + | +LL ~ let n = opt?; +LL + let mut ret = n + 1; +LL + ret += n; +LL + if n > 1 { Some(ret) } else { None } + | + +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:13:9 + | +LL | opt.and_then(|n| test_opt_block(Some(n))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let n = opt?; +LL + test_opt_block(Some(n)) + | + +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:17:9 + | +LL | gen_option(1).and_then(|n| test_opt_block(Some(n))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let n = gen_option(1)?; +LL + test_opt_block(Some(n)) + | + +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:21:9 + | +LL | opt.and_then(|n| if n > 1 { Ok(n + 1) } else { Err(n) }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let n = opt?; +LL + if n > 1 { Ok(n + 1) } else { Err(n) } + | + +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:25:9 + | +LL | opt.and_then(|n| test_res_block(Ok(n))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let n = opt?; +LL + test_res_block(Ok(n)) + | + +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:30:9 + | +LL | Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } + | + +error: use the question mark operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:35:9 + | +LL | / Some(match (vec![1, 2, 3], vec![1, 2, 4]) { +LL | | (a, _) if a.len() > 1 => a, +LL | | (_, b) => b, +LL | | }) +LL | | .and_then(|x| if x.len() > 2 { Some(3) } else { None }) + | |_______________________________________________________________^ + | +help: try + | +LL ~ let x = Some(match (vec![1, 2, 3], vec![1, 2, 4]) { +LL + (a, _) if a.len() > 1 => a, +LL + (_, b) => b, +LL + })?; +LL + if x.len() > 2 { Some(3) } else { None } + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.rs b/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.rs index 91b7ea3922c5..f405ba200acd 100644 --- a/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.rs +++ b/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.rs @@ -8,11 +8,11 @@ fn main() { const SIZE: usize = 128; const HALF_SIZE: usize = SIZE / 2; const DOUBLE_SIZE: usize = SIZE * 2; - let mut x = [2u8; SIZE]; - let mut y = [2u8; SIZE]; + let mut x = [2u16; SIZE]; + let mut y = [2u16; SIZE]; // Count expression involving multiplication of size_of (Should trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` // Count expression involving nested multiplications of size_of (Should trigger the lint) @@ -20,22 +20,19 @@ fn main() { //~^ ERROR: found a count of bytes instead of a count of elements of `T` // Count expression involving divisions of size_of (Should trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` // Count expression involving divisions by size_of (Should not trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / size_of::()) }; // Count expression involving divisions by multiple size_of (Should not trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 * size_of::())) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 * size_of::())) }; // Count expression involving recursive divisions by size_of (Should trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::())) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::())) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` // No size_of calls (Should not trigger the lint) unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; - - // Different types for pointee and size_of (Should not trigger the lint) - unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() / 2 * SIZE) }; } diff --git a/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.stderr b/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.stderr index 6396afd7f395..74be0d7773df 100644 --- a/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.stderr +++ b/src/tools/clippy/tests/ui/size_of_in_element_count/expressions.stderr @@ -1,8 +1,8 @@ error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/expressions.rs:15:62 | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` @@ -19,16 +19,16 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * si error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/expressions.rs:23:47 | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/expressions.rs:33:47 | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::())) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::())) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type diff --git a/src/tools/clippy/tests/ui/size_of_in_element_count/functions.rs b/src/tools/clippy/tests/ui/size_of_in_element_count/functions.rs index 3501cbdf81cf..af18136a1dbe 100644 --- a/src/tools/clippy/tests/ui/size_of_in_element_count/functions.rs +++ b/src/tools/clippy/tests/ui/size_of_in_element_count/functions.rs @@ -11,57 +11,52 @@ fn main() { const SIZE: usize = 128; const HALF_SIZE: usize = SIZE / 2; const DOUBLE_SIZE: usize = SIZE * 2; - let mut x = [2u8; SIZE]; - let mut y = [2u8; SIZE]; + let mut x = [2u16; SIZE]; + let mut y = [2u16; SIZE]; // Count is size_of (Should trigger the lint) - unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; - //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + //~^ ERROR: found a count of bytes instead of a count of elements of `T` + slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); //~^ ERROR: found a count of bytes instead of a count of elements of `T` - slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { y.as_mut_ptr().sub(size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + y.as_ptr().wrapping_sub(size_of::()); //~^ ERROR: found a count of bytes instead of a count of elements of `T` - - unsafe { y.as_mut_ptr().sub(size_of::()) }; + unsafe { y.as_ptr().add(size_of::()) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - y.as_ptr().wrapping_sub(size_of::()); + y.as_mut_ptr().wrapping_add(size_of::()); //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { y.as_ptr().add(size_of::()) }; + unsafe { y.as_ptr().offset(size_of::() as isize) }; //~^ ERROR: found a count of bytes instead of a count of elements of `T` - y.as_mut_ptr().wrapping_add(size_of::()); - //~^ ERROR: found a count of bytes instead of a count of elements of `T` - unsafe { y.as_ptr().offset(size_of::() as isize) }; - //~^ ERROR: found a count of bytes instead of a count of elements of `T` - y.as_mut_ptr().wrapping_offset(size_of::() as isize); + y.as_mut_ptr().wrapping_offset(size_of::() as isize); //~^ ERROR: found a count of bytes instead of a count of elements of `T` } diff --git a/src/tools/clippy/tests/ui/size_of_in_element_count/functions.stderr b/src/tools/clippy/tests/ui/size_of_in_element_count/functions.stderr index abde7dc7cd2d..de54789b2251 100644 --- a/src/tools/clippy/tests/ui/size_of_in_element_count/functions.stderr +++ b/src/tools/clippy/tests/ui/size_of_in_element_count/functions.stderr @@ -1,8 +1,8 @@ error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:18:68 + --> tests/ui/size_of_in_element_count/functions.rs:18:69 | -LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` @@ -19,40 +19,40 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/functions.rs:23:49 | -LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/functions.rs:25:64 | -LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/functions.rs:27:51 | -LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/functions.rs:29:66 | -LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` --> tests/ui/size_of_in_element_count/functions.rs:32:47 | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type @@ -65,108 +65,92 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:37:46 + --> tests/ui/size_of_in_element_count/functions.rs:37:66 | -LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:39:47 + --> tests/ui/size_of_in_element_count/functions.rs:40:46 | -LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:42:66 + --> tests/ui/size_of_in_element_count/functions.rs:42:38 | -LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:45:46 + --> tests/ui/size_of_in_element_count/functions.rs:45:49 | -LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:47:38 + --> tests/ui/size_of_in_element_count/functions.rs:47:41 | -LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:50:49 + --> tests/ui/size_of_in_element_count/functions.rs:50:33 | -LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:52:41 + --> tests/ui/size_of_in_element_count/functions.rs:52:29 | -LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | y.as_ptr().wrapping_sub(size_of::()); + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:55:33 + --> tests/ui/size_of_in_element_count/functions.rs:54:29 | -LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { y.as_ptr().add(size_of::()) }; + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:57:29 + --> tests/ui/size_of_in_element_count/functions.rs:56:33 | -LL | y.as_ptr().wrapping_sub(size_of::()); - | ^^^^^^^^^^^^^^^ +LL | y.as_mut_ptr().wrapping_add(size_of::()); + | ^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:59:29 + --> tests/ui/size_of_in_element_count/functions.rs:58:32 | -LL | unsafe { y.as_ptr().add(size_of::()) }; - | ^^^^^^^^^^^^^^^ +LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:61:33 + --> tests/ui/size_of_in_element_count/functions.rs:60:36 | -LL | y.as_mut_ptr().wrapping_add(size_of::()); - | ^^^^^^^^^^^^^^^ +LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:63:32 - | -LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> tests/ui/size_of_in_element_count/functions.rs:65:36 - | -LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: aborting due to 21 previous errors +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr index fb8fb1a00901..26166e2fc8da 100644 --- a/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr +++ b/src/tools/clippy/tests/ui/toplevel_ref_arg_non_rustfix.stderr @@ -1,4 +1,4 @@ -error: `ref` directly on a function argument is ignored. Consider using a reference type instead +error: `ref` directly on a function parameter does not prevent taking ownership of the passed argument. Consider using a reference type instead --> tests/ui/toplevel_ref_arg_non_rustfix.rs:9:15 | LL | fn the_answer(ref mut x: u8) { @@ -7,7 +7,7 @@ LL | fn the_answer(ref mut x: u8) { = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::toplevel_ref_arg)]` -error: `ref` directly on a function argument is ignored. Consider using a reference type instead +error: `ref` directly on a function parameter does not prevent taking ownership of the passed argument. Consider using a reference type instead --> tests/ui/toplevel_ref_arg_non_rustfix.rs:20:24 | LL | fn fun_example(ref _x: usize) {} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed index 7a3b79553dea..343c88b98155 100644 --- a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed @@ -55,3 +55,9 @@ fn no_borrow_issue(a: u32, b: u32) { None => {}, } } + +fn issue14100() -> bool { + // Removing the `;` would make the block type be `()` instead of `!`, and this could no longer be + // cast into the `bool` function return type. + if return true {}; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed index d186d5e7ebc4..1cba5760eb0a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed @@ -55,3 +55,9 @@ fn no_borrow_issue(a: u32, b: u32) { None => {}, } } + +fn issue14100() -> bool { + // Removing the `;` would make the block type be `()` instead of `!`, and this could no longer be + // cast into the `bool` function return type. + if return true {}; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.rs b/src/tools/clippy/tests/ui/unnecessary_semicolon.rs index 3028c5b27b34..6abbbd79aaf2 100644 --- a/src/tools/clippy/tests/ui/unnecessary_semicolon.rs +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.rs @@ -55,3 +55,9 @@ fn no_borrow_issue(a: u32, b: u32) { None => {}, }; } + +fn issue14100() -> bool { + // Removing the `;` would make the block type be `()` instead of `!`, and this could no longer be + // cast into the `bool` function return type. + if return true {}; +} diff --git a/src/tools/clippy/tests/ui/write_literal.fixed b/src/tools/clippy/tests/ui/write_literal.fixed index 3d216b76cbf3..f1def776e1bc 100644 --- a/src/tools/clippy/tests/ui/write_literal.fixed +++ b/src/tools/clippy/tests/ui/write_literal.fixed @@ -62,3 +62,19 @@ fn main() { writeln!(v, "hello {0} {1}, world {2}", 2, 3, 4); //~^ ERROR: literal with an empty format string } + +fn issue_13959() { + let mut v = Vec::new(); + writeln!(v, "\""); + writeln!( + v, + " + foo + \\ + \\\\ + \" + \\\" + bar +" + ); +} diff --git a/src/tools/clippy/tests/ui/write_literal.rs b/src/tools/clippy/tests/ui/write_literal.rs index 79d6daa2e3b5..1b7df91b47e1 100644 --- a/src/tools/clippy/tests/ui/write_literal.rs +++ b/src/tools/clippy/tests/ui/write_literal.rs @@ -62,3 +62,20 @@ fn main() { writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4); //~^ ERROR: literal with an empty format string } + +fn issue_13959() { + let mut v = Vec::new(); + writeln!(v, "{}", r#"""#); + writeln!( + v, + "{}", + r#" + foo + \ + \\ + " + \" + bar +"# + ); +} diff --git a/src/tools/clippy/tests/ui/write_literal.stderr b/src/tools/clippy/tests/ui/write_literal.stderr index 9f4cdfd91e8a..35c93d567cd3 100644 --- a/src/tools/clippy/tests/ui/write_literal.stderr +++ b/src/tools/clippy/tests/ui/write_literal.stderr @@ -144,5 +144,41 @@ LL - writeln!(v, "{0} {1} {2}, {3} {4}", "hello", 2, 3, "world", 4); LL + writeln!(v, "hello {0} {1}, world {2}", 2, 3, 4); | -error: aborting due to 12 previous errors +error: literal with an empty format string + --> tests/ui/write_literal.rs:68:23 + | +LL | writeln!(v, "{}", r#"""#); + | ^^^^^^ + | +help: try + | +LL - writeln!(v, "{}", r#"""#); +LL + writeln!(v, "\""); + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:72:9 + | +LL | / r#" +LL | | foo +LL | | \ +LL | | \\ +... | +LL | | bar +LL | | "# + | |__^ + | +help: try + | +LL ~ " +LL + foo +LL + \\ +LL + \\\\ +LL + \" +LL + \\\" +LL + bar +LL ~ " + | + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html index deb0ef0b4996..a9b646280030 100644 --- a/src/tools/clippy/util/gh-pages/index_template.html +++ b/src/tools/clippy/util/gh-pages/index_template.html @@ -24,14 +24,16 @@ Otherwise, have a great day =^.^= {# #} {# #} {# #} + {# #} + {# #} + {# #} {# #} {# #} - {# #}
{# #} {# #}
{# #}
Theme
{# #} - {# #} {# #} {# #} {# #} @@ -39,11 +41,12 @@ Otherwise, have a great day =^.^= {# #} {# #} {# #}
{# #}
{# #} + {# #}
{# #} {# #}
{# #}
{# #} - {# #} - {# #}
{# #} @@ -145,13 +148,13 @@ Otherwise, have a great day =^.^= {% for lint in lints %}
{# #} {# #} -