diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 9eb02be2f155b..8477686d4f577 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1640,7 +1640,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } TerminatorKind::UnwindTerminate(_) => { if !is_cleanup { - span_mirbug!(self, block_data, "abort on non-cleanup block!") + span_mirbug!(self, block_data, "terminate on non-cleanup block!") } } TerminatorKind::Return => { diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 9771f44f62cfb..63562d335089b 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -430,47 +430,11 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { } } - // Note: must be kept in sync with get_caller_location from cg_ssa - pub(crate) fn get_caller_location(&mut self, mut source_info: mir::SourceInfo) -> CValue<'tcx> { - let span_to_caller_location = |fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span| { - use rustc_session::RemapFileNameExt; - let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); - let caller = fx.tcx.sess.source_map().lookup_char_pos(topmost.lo()); - let const_loc = fx.tcx.const_caller_location(( - rustc_span::symbol::Symbol::intern( - &caller.file.name.for_codegen(&fx.tcx.sess).to_string_lossy(), - ), - caller.line as u32, - caller.col_display as u32 + 1, - )); - crate::constant::codegen_const_value(fx, const_loc, fx.tcx.caller_location_ty()) - }; - - // Walk up the `SourceScope`s, in case some of them are from MIR inlining. - // If so, the starting `source_info.span` is in the innermost inlined - // function, and will be replaced with outer callsite spans as long - // as the inlined functions were `#[track_caller]`. - loop { - let scope_data = &self.mir.source_scopes[source_info.scope]; - - if let Some((callee, callsite_span)) = scope_data.inlined { - // Stop inside the most nested non-`#[track_caller]` function, - // before ever reaching its caller (which is irrelevant). - if !callee.def.requires_caller_location(self.tcx) { - return span_to_caller_location(self, source_info.span); - } - source_info.span = callsite_span; - } - - // Skip past all of the parents with `inlined: None`. - match scope_data.inlined_parent_scope { - Some(parent) => source_info.scope = parent, - None => break, - } - } - - // No inlined `SourceScope`s, or all of them were `#[track_caller]`. - self.caller_location.unwrap_or_else(|| span_to_caller_location(self, source_info.span)) + pub(crate) fn get_caller_location(&mut self, source_info: mir::SourceInfo) -> CValue<'tcx> { + self.mir.caller_location_span(source_info, self.caller_location, self.tcx, |span| { + let const_loc = self.tcx.span_as_caller_location(span); + crate::constant::codegen_const_value(self, const_loc, self.tcx.caller_location_ty()) + }) } pub(crate) fn anonymous_str(&mut self, msg: &str) -> Value { diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index caade768795f6..22afb4063267e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1449,47 +1449,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn get_caller_location( &mut self, bx: &mut Bx, - mut source_info: mir::SourceInfo, + source_info: mir::SourceInfo, ) -> OperandRef<'tcx, Bx::Value> { - let tcx = bx.tcx(); - - let mut span_to_caller_location = |span: Span| { - use rustc_session::RemapFileNameExt; - let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); - let caller = tcx.sess.source_map().lookup_char_pos(topmost.lo()); - let const_loc = tcx.const_caller_location(( - Symbol::intern(&caller.file.name.for_codegen(self.cx.sess()).to_string_lossy()), - caller.line as u32, - caller.col_display as u32 + 1, - )); + self.mir.caller_location_span(source_info, self.caller_location, bx.tcx(), |span: Span| { + let const_loc = bx.tcx().span_as_caller_location(span); OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty()) - }; - - // Walk up the `SourceScope`s, in case some of them are from MIR inlining. - // If so, the starting `source_info.span` is in the innermost inlined - // function, and will be replaced with outer callsite spans as long - // as the inlined functions were `#[track_caller]`. - loop { - let scope_data = &self.mir.source_scopes[source_info.scope]; - - if let Some((callee, callsite_span)) = scope_data.inlined { - // Stop inside the most nested non-`#[track_caller]` function, - // before ever reaching its caller (which is irrelevant). - if !callee.def.requires_caller_location(tcx) { - return span_to_caller_location(source_info.span); - } - source_info.span = callsite_span; - } - - // Skip past all of the parents with `inlined: None`. - match scope_data.inlined_parent_scope { - Some(parent) => source_info.scope = parent, - None => break, - } - } - - // No inlined `SourceScope`s, or all of them were `#[track_caller]`. - self.caller_location.unwrap_or_else(|| span_to_caller_location(source_info.span)) + }) } fn get_personality_slot(&mut self, bx: &mut Bx) -> PlaceRef<'tcx, Bx::Value> { diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 46e00d0f176b4..669838308a558 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -90,7 +90,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( /// that inform us about the generic bounds of the constant. E.g., using an associated constant /// of a function's generic parameter will require knowledge about the bounds on the generic /// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument. -pub(super) fn mk_eval_cx<'mir, 'tcx>( +pub(crate) fn mk_eval_cx<'mir, 'tcx>( tcx: TyCtxt<'tcx>, root_span: Span, param_env: ty::ParamEnv<'tcx>, diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 0d0ebe6f390b0..4b447229c5f61 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -4,6 +4,7 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::PointerArithmetic; use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout}; use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::Span; use std::borrow::Borrow; use std::hash::Hash; use std::ops::ControlFlow; @@ -181,6 +182,24 @@ impl interpret::MayLeak for ! { } impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { + fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) { + let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); + let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); + + use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt}; + ( + Symbol::intern( + &caller + .file + .name + .for_scope(&self.tcx.sess, RemapPathScopeComponents::DIAGNOSTICS) + .to_string_lossy(), + ), + u32::try_from(caller.line).unwrap(), + u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(), + ) + } + /// "Intercept" a function call, because we have something special to do for it. /// All `#[rustc_do_not_const_check]` functions should be hooked here. /// If this returns `Some` function, which may be `instance` or a different function with diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index bcbe996be7d88..d6ee6975cddd5 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -1,12 +1,12 @@ // Not in interpret to make sure we do not use private implementation details use crate::errors::MaxNumNodesInConstErr; -use crate::interpret::{intern_const_alloc_recursive, InternKind, InterpCx, Scalar}; +use crate::interpret::InterpCx; use rustc_middle::mir; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; +use rustc_span::source_map::DUMMY_SP; mod error; mod eval_queries; @@ -20,20 +20,6 @@ pub use fn_queries::*; pub use machine::*; pub(crate) use valtrees::{const_to_valtree_inner, valtree_to_const_value}; -pub(crate) fn const_caller_location( - tcx: TyCtxt<'_>, - (file, line, col): (Symbol, u32, u32), -) -> mir::ConstValue<'_> { - trace!("const_caller_location: {}:{}:{}", file, line, col); - let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No); - - let loc_place = ecx.alloc_caller_location(file, line, col); - if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() { - bug!("intern_const_alloc_recursive should not error in this case") - } - mir::ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx)) -} - // We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes. const VALTREE_MAX_NODES: usize = 100000; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index ec0af79459cbf..07cab5e3400ef 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -595,6 +595,50 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a + /// frame which is not `#[track_caller]`. This is the fancy version of `cur_span`. + pub(crate) fn find_closest_untracked_caller_location(&self) -> Span { + for frame in self.stack().iter().rev() { + debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance); + + // Assert that the frame we look at is actually executing code currently + // (`loc` is `Right` when we are unwinding and the frame does not require cleanup). + let loc = frame.loc.left().unwrap(); + + // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all + // (such as `box`). Use the normal span by default. + let mut source_info = *frame.body.source_info(loc); + + // If this is a `Call` terminator, use the `fn_span` instead. + let block = &frame.body.basic_blocks[loc.block]; + if loc.statement_index == block.statements.len() { + debug!( + "find_closest_untracked_caller_location: got terminator {:?} ({:?})", + block.terminator(), + block.terminator().kind, + ); + if let mir::TerminatorKind::Call { fn_span, .. } = block.terminator().kind { + source_info.span = fn_span; + } + } + + let caller_location = if frame.instance.def.requires_caller_location(*self.tcx) { + // We use `Err(())` as indication that we should continue up the call stack since + // this is a `#[track_caller]` function. + Some(Err(())) + } else { + None + }; + if let Ok(span) = + frame.body.caller_location_span(source_info, caller_location, *self.tcx, Ok) + { + return span; + } + } + + span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found") + } + #[inline(always)] pub fn layout_of_local( &self, diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index c48857fcf3cd2..b23cafc193a05 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -22,8 +22,6 @@ use super::{ use crate::fluent_generated as fluent; -mod caller_location; - fn numeric_intrinsic(name: Symbol, bits: u128, kind: Primitive) -> Scalar { let size = match kind { Primitive::Int(integer, _) => integer.size(), @@ -130,8 +128,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { match intrinsic_name { sym::caller_location => { let span = self.find_closest_untracked_caller_location(); - let location = self.alloc_caller_location_for_span(span); - self.write_immediate(location.to_ref(self), dest)?; + let val = self.tcx.span_as_caller_location(span); + let val = + self.const_val_to_op(val, self.tcx.caller_location_ty(), Some(dest.layout))?; + self.copy_op(&val, dest, /* allow_transmute */ false)?; } sym::min_align_of_val | sym::size_of_val => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs deleted file mode 100644 index 16b7decf9c43c..0000000000000 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs +++ /dev/null @@ -1,136 +0,0 @@ -use rustc_ast::Mutability; -use rustc_hir::lang_items::LangItem; -use rustc_middle::mir::TerminatorKind; -use rustc_middle::ty::layout::LayoutOf; -use rustc_span::{Span, Symbol}; - -use crate::interpret::{ - intrinsics::{InterpCx, Machine}, - MPlaceTy, MemoryKind, Scalar, -}; - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a - /// frame which is not `#[track_caller]`. - pub(crate) fn find_closest_untracked_caller_location(&self) -> Span { - for frame in self.stack().iter().rev() { - debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance); - - // Assert that the frame we look at is actually executing code currently - // (`loc` is `Right` when we are unwinding and the frame does not require cleanup). - let loc = frame.loc.left().unwrap(); - - // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all - // (such as `box`). Use the normal span by default. - let mut source_info = *frame.body.source_info(loc); - - // If this is a `Call` terminator, use the `fn_span` instead. - let block = &frame.body.basic_blocks[loc.block]; - if loc.statement_index == block.statements.len() { - debug!( - "find_closest_untracked_caller_location: got terminator {:?} ({:?})", - block.terminator(), - block.terminator().kind - ); - if let TerminatorKind::Call { fn_span, .. } = block.terminator().kind { - source_info.span = fn_span; - } - } - - // Walk up the `SourceScope`s, in case some of them are from MIR inlining. - // If so, the starting `source_info.span` is in the innermost inlined - // function, and will be replaced with outer callsite spans as long - // as the inlined functions were `#[track_caller]`. - loop { - let scope_data = &frame.body.source_scopes[source_info.scope]; - - if let Some((callee, callsite_span)) = scope_data.inlined { - // Stop inside the most nested non-`#[track_caller]` function, - // before ever reaching its caller (which is irrelevant). - if !callee.def.requires_caller_location(*self.tcx) { - return source_info.span; - } - source_info.span = callsite_span; - } - - // Skip past all of the parents with `inlined: None`. - match scope_data.inlined_parent_scope { - Some(parent) => source_info.scope = parent, - None => break, - } - } - - // Stop inside the most nested non-`#[track_caller]` function, - // before ever reaching its caller (which is irrelevant). - if !frame.instance.def.requires_caller_location(*self.tcx) { - return source_info.span; - } - } - - span_bug!(self.cur_span(), "no non-`#[track_caller]` frame found") - } - - /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers. - pub(crate) fn alloc_caller_location( - &mut self, - filename: Symbol, - line: u32, - col: u32, - ) -> MPlaceTy<'tcx, M::Provenance> { - let loc_details = self.tcx.sess.opts.unstable_opts.location_detail; - // This can fail if rustc runs out of memory right here. Trying to emit an error would be - // pointless, since that would require allocating more memory than these short strings. - let file = if loc_details.file { - self.allocate_str(filename.as_str(), MemoryKind::CallerLocation, Mutability::Not) - .unwrap() - } else { - // FIXME: This creates a new allocation each time. It might be preferable to - // perform this allocation only once, and re-use the `MPlaceTy`. - // See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398 - self.allocate_str("", MemoryKind::CallerLocation, Mutability::Not).unwrap() - }; - let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) }; - let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) }; - - // Allocate memory for `CallerLocation` struct. - let loc_ty = self - .tcx - .type_of(self.tcx.require_lang_item(LangItem::PanicLocation, None)) - .instantiate(*self.tcx, self.tcx.mk_args(&[self.tcx.lifetimes.re_erased.into()])); - let loc_layout = self.layout_of(loc_ty).unwrap(); - let location = self.allocate(loc_layout, MemoryKind::CallerLocation).unwrap(); - - // Initialize fields. - self.write_immediate(file.to_ref(self), &self.project_field(&location, 0).unwrap()) - .expect("writing to memory we just allocated cannot fail"); - self.write_scalar(line, &self.project_field(&location, 1).unwrap()) - .expect("writing to memory we just allocated cannot fail"); - self.write_scalar(col, &self.project_field(&location, 2).unwrap()) - .expect("writing to memory we just allocated cannot fail"); - - location - } - - pub(crate) fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) { - let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); - let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); - - use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt}; - ( - Symbol::intern( - &caller - .file - .name - .for_scope(&self.tcx.sess, RemapPathScopeComponents::DIAGNOSTICS) - .to_string_lossy(), - ), - u32::try_from(caller.line).unwrap(), - u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(), - ) - } - - pub fn alloc_caller_location_for_span(&mut self, span: Span) -> MPlaceTy<'tcx, M::Provenance> { - let (file, line, column) = self.location_triple_for_span(span); - self.alloc_caller_location(file, line, column) - } -} diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 1fd5723f2775e..7fc87ed888102 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -49,7 +49,7 @@ pub fn provide(providers: &mut Providers) { const_eval::provide(providers); providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider; providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider; - providers.const_caller_location = const_eval::const_caller_location; + providers.const_caller_location = util::caller_location::const_caller_location_provider; providers.eval_to_valtree = |tcx, param_env_and_value| { let (param_env, raw) = param_env_and_value.into_parts(); const_eval::eval_to_valtree(tcx, param_env, raw) diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs new file mode 100644 index 0000000000000..d67863d75d668 --- /dev/null +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -0,0 +1,63 @@ +use rustc_hir::LangItem; +use rustc_middle::mir; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::{source_map::DUMMY_SP, symbol::Symbol}; +use rustc_type_ir::Mutability; + +use crate::const_eval::{mk_eval_cx, CanAccessStatics, CompileTimeEvalContext}; +use crate::interpret::*; + +/// Allocate a `const core::panic::Location` with the provided filename and line/column numbers. +fn alloc_caller_location<'mir, 'tcx>( + ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, + filename: Symbol, + line: u32, + col: u32, +) -> MPlaceTy<'tcx> { + let loc_details = ecx.tcx.sess.opts.unstable_opts.location_detail; + // This can fail if rustc runs out of memory right here. Trying to emit an error would be + // pointless, since that would require allocating more memory than these short strings. + let file = if loc_details.file { + ecx.allocate_str(filename.as_str(), MemoryKind::CallerLocation, Mutability::Not).unwrap() + } else { + // FIXME: This creates a new allocation each time. It might be preferable to + // perform this allocation only once, and re-use the `MPlaceTy`. + // See https://github.com/rust-lang/rust/pull/89920#discussion_r730012398 + ecx.allocate_str("", MemoryKind::CallerLocation, Mutability::Not).unwrap() + }; + let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) }; + let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) }; + + // Allocate memory for `CallerLocation` struct. + let loc_ty = ecx + .tcx + .type_of(ecx.tcx.require_lang_item(LangItem::PanicLocation, None)) + .instantiate(*ecx.tcx, ecx.tcx.mk_args(&[ecx.tcx.lifetimes.re_erased.into()])); + let loc_layout = ecx.layout_of(loc_ty).unwrap(); + let location = ecx.allocate(loc_layout, MemoryKind::CallerLocation).unwrap(); + + // Initialize fields. + ecx.write_immediate(file.to_ref(ecx), &ecx.project_field(&location, 0).unwrap()) + .expect("writing to memory we just allocated cannot fail"); + ecx.write_scalar(line, &ecx.project_field(&location, 1).unwrap()) + .expect("writing to memory we just allocated cannot fail"); + ecx.write_scalar(col, &ecx.project_field(&location, 2).unwrap()) + .expect("writing to memory we just allocated cannot fail"); + + location +} + +pub(crate) fn const_caller_location_provider( + tcx: TyCtxt<'_>, + (file, line, col): (Symbol, u32, u32), +) -> mir::ConstValue<'_> { + trace!("const_caller_location: {}:{}:{}", file, line, col); + let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No); + + let loc_place = alloc_caller_location(&mut ecx, file, line, col); + if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() { + bug!("intern_const_alloc_recursive should not error in this case") + } + mir::ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx)) +} diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 040b3071e6f87..1e58bd645cdde 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -1,6 +1,7 @@ use rustc_middle::mir; mod alignment; +pub(crate) mod caller_location; mod check_validity_requirement; mod compare_types; mod type_name; diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 6b31fb079e01b..355855b8e2b3e 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1154,7 +1154,7 @@ impl EarlyLintPass for UnusedParens { fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { if let StmtKind::Local(ref local) = s.kind { - self.check_unused_parens_pat(cx, &local.pat, true, false, (false, false)); + self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false)); } ::check_stmt(self, cx, s) diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 37ff5bcf1e2da..27d555d7e26c7 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -12,6 +12,8 @@ middle_assert_coroutine_resume_after_return = coroutine resumed after completion middle_assert_divide_by_zero = attempt to divide `{$val}` by zero +middle_assert_gen_resume_after_panic = `gen` fn or block cannot be further iterated on after it panicked + middle_assert_misaligned_ptr_deref = misaligned pointer dereference: address must be a multiple of {$required} but is {$found} diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 3cb2e349ce084..c3300753fd16b 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -3,6 +3,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use rustc_hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir}; +use rustc_session::RemapFileNameExt; use rustc_span::Span; use rustc_target::abi::{HasDataLayout, Size}; @@ -579,3 +580,20 @@ impl<'tcx> Display for Const<'tcx> { } } } + +/////////////////////////////////////////////////////////////////////////// +/// Const-related utilities + +impl<'tcx> TyCtxt<'tcx> { + pub fn span_as_caller_location(self, span: Span) -> ConstValue<'tcx> { + let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); + let caller = self.sess.source_map().lookup_char_pos(topmost.lo()); + self.const_caller_location(( + rustc_span::symbol::Symbol::intern( + &caller.file.name.for_codegen(&self.sess).to_string_lossy(), + ), + caller.line as u32, + caller.col_display as u32 + 1, + )) + } +} diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index a85af7c3fb511..7054cede2d87c 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -578,6 +578,40 @@ impl<'tcx> Body<'tcx> { pub fn is_custom_mir(&self) -> bool { self.injection_phase.is_some() } + + /// For a `Location` in this scope, determine what the "caller location" at that point is. This + /// is interesting because of inlining: the `#[track_caller]` attribute of inlined functions + /// must be honored. Falls back to the `tracked_caller` value for `#[track_caller]` functions, + /// or the function's scope. + pub fn caller_location_span( + &self, + mut source_info: SourceInfo, + caller_location: Option, + tcx: TyCtxt<'tcx>, + from_span: impl FnOnce(Span) -> T, + ) -> T { + loop { + let scope_data = &self.source_scopes[source_info.scope]; + + if let Some((callee, callsite_span)) = scope_data.inlined { + // Stop inside the most nested non-`#[track_caller]` function, + // before ever reaching its caller (which is irrelevant). + if !callee.def.requires_caller_location(tcx) { + return from_span(source_info.span); + } + source_info.span = callsite_span; + } + + // Skip past all of the parents with `inlined: None`. + match scope_data.inlined_parent_scope { + Some(parent) => source_info.scope = parent, + None => break, + } + } + + // No inlined `SourceScope`s, or all of them were `#[track_caller]`. + caller_location.unwrap_or_else(|| from_span(source_info.span)) + } } #[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 12057f5e1cb66..5f4ff22bc49ca 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -785,7 +785,7 @@ impl<'tcx> TerminatorKind<'tcx> { CoroutineDrop => write!(fmt, "coroutine_drop"), UnwindResume => write!(fmt, "resume"), UnwindTerminate(reason) => { - write!(fmt, "abort({})", reason.as_short_str()) + write!(fmt, "terminate({})", reason.as_short_str()) } Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"), Unreachable => write!(fmt, "unreachable"), diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index c2aa015f4b7e7..9dfbe1733cc51 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -250,8 +250,7 @@ impl AssertKind { middle_assert_coroutine_resume_after_return } ResumedAfterPanic(CoroutineKind::Async(_)) => middle_assert_async_resume_after_panic, - // FIXME(gen_blocks): custom error message for `gen` blocks - ResumedAfterPanic(CoroutineKind::Gen(_)) => middle_assert_async_resume_after_panic, + ResumedAfterPanic(CoroutineKind::Gen(_)) => middle_assert_gen_resume_after_panic, ResumedAfterPanic(CoroutineKind::Coroutine) => { middle_assert_coroutine_resume_after_panic } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 9bedd7b8a338a..9ba1f0a5df914 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -278,8 +278,8 @@ parse_found_expr_would_be_stmt = expected expression, found `{$token}` parse_function_body_equals_expr = function body cannot be `= expression;` .suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;` -parse_gen_block = `gen` blocks are not yet implemented - .help = only the keyword is reserved for now +parse_gen_fn = `gen` functions are not yet implemented + .help = for now you can use `gen {"{}"}` blocks and return `impl Iterator` instead parse_generic_args_in_pat_require_turbofish_syntax = generic args in patterns require the turbofish syntax diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index c0e94d15da0bf..e5cd91a32a7bf 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -521,9 +521,9 @@ pub(crate) struct CatchAfterTry { } #[derive(Diagnostic)] -#[diag(parse_gen_block)] +#[diag(parse_gen_fn)] #[help] -pub(crate) struct GenBlock { +pub(crate) struct GenFn { #[primary_span] pub span: Span, } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index a9f456d64cd11..55ad3f42938b4 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2372,7 +2372,7 @@ impl<'a> Parser<'a> { } if let Gen::Yes { span, .. } = genness { - self.sess.emit_err(errors::GenBlock { span }); + self.sess.emit_err(errors::GenFn { span }); } if !self.eat_keyword_case(kw::Fn, case) { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 026186cbe6cd3..a0a98dd853666 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -778,6 +778,8 @@ passes_unused_var_maybe_capture_ref = unused variable: `{$name}` passes_unused_var_remove_field = unused variable: `{$name}` passes_unused_var_remove_field_suggestion = try removing the field +passes_unused_variable_args_in_macro = `{$name}` is captured in macro and introduced a unused variable + passes_unused_variable_try_ignore = unused variable: `{$name}` .suggestion = try ignoring the field @@ -785,6 +787,7 @@ passes_unused_variable_try_prefix = unused variable: `{$name}` .label = unused variable .suggestion = if this is intentional, prefix it with an underscore + passes_used_compiler_linker = `used(compiler)` and `used(linker)` can't be used together diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index eca0fb7748b15..bc31b5db021d0 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1768,15 +1768,24 @@ pub struct UnusedVariableTryPrefix { #[subdiagnostic] pub string_interp: Vec, #[subdiagnostic] - pub sugg: UnusedVariableTryPrefixSugg, + pub sugg: UnusedVariableSugg, + pub name: String, } #[derive(Subdiagnostic)] -#[multipart_suggestion(passes_suggestion, applicability = "machine-applicable")] -pub struct UnusedVariableTryPrefixSugg { - #[suggestion_part(code = "_{name}")] - pub spans: Vec, - pub name: String, +pub enum UnusedVariableSugg { + #[multipart_suggestion(passes_suggestion, applicability = "machine-applicable")] + TryPrefixSugg { + #[suggestion_part(code = "_{name}")] + spans: Vec, + name: String, + }, + #[help(passes_unused_variable_args_in_macro)] + NoSugg { + #[primary_span] + span: Span, + name: String, + }, } pub struct UnusedVariableStringInterp { diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index d068fe62473cd..b73fb984c0eac 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1580,7 +1580,6 @@ impl<'tcx> Liveness<'_, 'tcx> { opt_body: Option<&hir::Body<'_>>, ) { let first_hir_id = hir_ids_and_spans[0].0; - if let Some(name) = self.should_warn(var).filter(|name| name != "self") { // annoying: for parameters in funcs like `fn(x: i32) // {ret}`, there is only one node, so asking about @@ -1652,11 +1651,29 @@ impl<'tcx> Liveness<'_, 'tcx> { }, ); } else { + // #117284, when `pat_span` and `ident_span` have different contexts + // we can't provide a good suggestion, instead we pointed out the spans from macro + let from_macro = non_shorthands + .iter() + .find(|(_, pat_span, ident_span)| { + pat_span.ctxt() != ident_span.ctxt() && pat_span.from_expansion() + }) + .map(|(_, pat_span, _)| *pat_span); let non_shorthands = non_shorthands .into_iter() .map(|(_, _, ident_span)| ident_span) .collect::>(); + let suggestions = self.string_interp_suggestions(&name, opt_body); + let sugg = if let Some(span) = from_macro { + errors::UnusedVariableSugg::NoSugg { span, name: name.clone() } + } else { + errors::UnusedVariableSugg::TryPrefixSugg { + spans: non_shorthands, + name: name.clone(), + } + }; + self.ir.tcx.emit_spanned_lint( lint::builtin::UNUSED_VARIABLES, first_hir_id, @@ -1666,10 +1683,8 @@ impl<'tcx> Liveness<'_, 'tcx> { .collect::>(), errors::UnusedVariableTryPrefix { label: if !suggestions.is_empty() { Some(pat.span) } else { None }, - sugg: errors::UnusedVariableTryPrefixSugg { - spans: non_shorthands, - name, - }, + name, + sugg, string_interp: suggestions, }, ); diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index f7d54566cb704..9242a1a751bc7 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -121,6 +121,7 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { } impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> { + #[instrument(skip(self), ret, level = "trace")] fn visit(&mut self, span: Span, value: impl TypeVisitable>) -> ControlFlow { self.visit_spanned(span, value); ControlFlow::Continue(()) diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index 1ab39974e0f2a..ccdc6120196ac 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -4,7 +4,7 @@ use std::ops::ControlFlow; use rustc_hir::{def::DefKind, def_id::LocalDefId}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_span::Span; use rustc_type_ir::visit::TypeVisitable; @@ -25,24 +25,9 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( let kind = tcx.def_kind(item); trace!(?kind); match kind { - DefKind::Coroutine => { - match tcx.type_of(item).instantiate_identity().kind() { - ty::Coroutine(_, args, _) => visitor.visit(tcx.def_span(item), args.as_coroutine().sig())?, - _ => bug!(), - } - for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { - visitor.visit(span, pred)?; - } - } - // Walk over the signature of the function-like - DefKind::Closure | DefKind::AssocFn | DefKind::Fn => { - let ty_sig = match kind { - DefKind::Closure => match tcx.type_of(item).instantiate_identity().kind() { - ty::Closure(_, args) => args.as_closure().sig(), - _ => bug!(), - }, - _ => tcx.fn_sig(item).instantiate_identity(), - }; + // Walk over the signature of the function + DefKind::AssocFn | DefKind::Fn => { + let ty_sig = tcx.fn_sig(item).instantiate_identity(); let hir_sig = tcx.hir().get_by_def_id(item).fn_decl().unwrap(); // Walk over the inputs and outputs manually in order to get good spans for them. visitor.visit(hir_sig.output.span(), ty_sig.output()); @@ -61,7 +46,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( Some(ty) => ty.span, _ => tcx.def_span(item), }; - visitor.visit(span, tcx.type_of(item).instantiate_identity()); + visitor.visit(span, tcx.type_of(item).instantiate_identity()); for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { visitor.visit(span, pred)?; } @@ -74,13 +59,15 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( // Look at field types DefKind::Struct | DefKind::Union | DefKind::Enum => { let span = tcx.def_ident_span(item).unwrap(); - visitor.visit(span, tcx.type_of(item).instantiate_identity()); + visitor.visit(span, tcx.type_of(item).instantiate_identity()); for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { visitor.visit(span, pred)?; } } - // Does not have a syntactical signature - DefKind::InlineConst => {} + // These are not part of a public API, they can only appear as hidden types, and there + // the interesting parts are solely in the signature of the containing item's opaque type + // or dyn type. + DefKind::InlineConst | DefKind::Closure | DefKind::Coroutine => {} DefKind::Impl { of_trait } => { if of_trait { let span = tcx.hir().get_by_def_id(item).expect_item().expect_impl().of_trait.unwrap().path.span; @@ -92,15 +79,11 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( _ => tcx.def_span(item), }; visitor.visit(span, tcx.type_of(item).instantiate_identity()); - for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { - visitor.visit(span, pred)?; - }} - DefKind::Trait => { for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { visitor.visit(span, pred)?; } } - DefKind::TraitAlias => { + DefKind::TraitAlias | DefKind::Trait => { for (pred, span) in tcx.predicates_of(item).instantiate_identity(tcx) { visitor.visit(span, pred)?; } diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 75d263d7794f4..9d03350c7232e 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -72,16 +72,27 @@ fn install_sh( let prefix = default_path(&builder.config.prefix, "/usr/local"); let sysconfdir = prefix.join(default_path(&builder.config.sysconfdir, "/etc")); + let destdir_env = env::var_os("DESTDIR").map(PathBuf::from); - // Sanity check for the user write access on prefix and sysconfdir - assert!( - is_dir_writable_for_user(&prefix), - "User doesn't have write access on `install.prefix` path in the `config.toml`.", - ); - assert!( - is_dir_writable_for_user(&sysconfdir), - "User doesn't have write access on `install.sysconfdir` path in `config.toml`." - ); + // Sanity checks on the write access of user. + // + // When the `DESTDIR` environment variable is present, there is no point to + // check write access for `prefix` and `sysconfdir` individually, as they + // are combined with the path from the `DESTDIR` environment variable. In + // this case, we only need to check the `DESTDIR` path, disregarding the + // `prefix` and `sysconfdir` paths. + if let Some(destdir) = &destdir_env { + assert!(is_dir_writable_for_user(destdir), "User doesn't have write access on DESTDIR."); + } else { + assert!( + is_dir_writable_for_user(&prefix), + "User doesn't have write access on `install.prefix` path in the `config.toml`.", + ); + assert!( + is_dir_writable_for_user(&sysconfdir), + "User doesn't have write access on `install.sysconfdir` path in `config.toml`." + ); + } let datadir = prefix.join(default_path(&builder.config.datadir, "share")); let docdir = prefix.join(default_path(&builder.config.docdir, "share/doc/rust")); @@ -95,13 +106,13 @@ fn install_sh( let mut cmd = Command::new(SHELL); cmd.current_dir(&empty_dir) .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh"))) - .arg(format!("--prefix={}", prepare_dir(prefix))) - .arg(format!("--sysconfdir={}", prepare_dir(sysconfdir))) - .arg(format!("--datadir={}", prepare_dir(datadir))) - .arg(format!("--docdir={}", prepare_dir(docdir))) - .arg(format!("--bindir={}", prepare_dir(bindir))) - .arg(format!("--libdir={}", prepare_dir(libdir))) - .arg(format!("--mandir={}", prepare_dir(mandir))) + .arg(format!("--prefix={}", prepare_dir(&destdir_env, prefix))) + .arg(format!("--sysconfdir={}", prepare_dir(&destdir_env, sysconfdir))) + .arg(format!("--datadir={}", prepare_dir(&destdir_env, datadir))) + .arg(format!("--docdir={}", prepare_dir(&destdir_env, docdir))) + .arg(format!("--bindir={}", prepare_dir(&destdir_env, bindir))) + .arg(format!("--libdir={}", prepare_dir(&destdir_env, libdir))) + .arg(format!("--mandir={}", prepare_dir(&destdir_env, mandir))) .arg("--disable-ldconfig"); builder.run(&mut cmd); t!(fs::remove_dir_all(&empty_dir)); @@ -111,19 +122,16 @@ fn default_path(config: &Option, default: &str) -> PathBuf { config.as_ref().cloned().unwrap_or_else(|| PathBuf::from(default)) } -fn prepare_dir(mut path: PathBuf) -> String { +fn prepare_dir(destdir_env: &Option, mut path: PathBuf) -> String { // The DESTDIR environment variable is a standard way to install software in a subdirectory // while keeping the original directory structure, even if the prefix or other directories // contain absolute paths. // // More information on the environment variable is available here: // https://www.gnu.org/prep/standards/html_node/DESTDIR.html - if let Some(destdir) = env::var_os("DESTDIR").map(PathBuf::from) { - // Sanity check for the user write access on DESTDIR - assert!(is_dir_writable_for_user(&destdir), "User doesn't have write access on DESTDIR."); - + if let Some(destdir) = destdir_env { let without_destdir = path.clone(); - path = destdir; + path = destdir.clone(); // Custom .join() which ignores disk roots. for part in without_destdir.components() { if let Component::Normal(s) = part { diff --git a/tests/ui/coroutine/gen_block_panic.rs b/tests/ui/coroutine/gen_block_panic.rs new file mode 100644 index 0000000000000..33e2b4b265a11 --- /dev/null +++ b/tests/ui/coroutine/gen_block_panic.rs @@ -0,0 +1,25 @@ +//compile-flags: --edition 2024 -Zunstable-options +// run-pass +#![feature(gen_blocks)] + +fn main() { + let mut iter = gen { + yield 42; + panic!("foo"); + yield 69; //~ WARN: unreachable statement + }; + assert_eq!(iter.next(), Some(42)); + let mut tmp = std::panic::AssertUnwindSafe(&mut iter); + match std::panic::catch_unwind(move || tmp.next()) { + Ok(_) => unreachable!(), + Err(err) => assert_eq!(*err.downcast::<&'static str>().unwrap(), "foo"), + } + + match std::panic::catch_unwind(move || iter.next()) { + Ok(_) => unreachable!(), + Err(err) => assert_eq!( + *err.downcast::<&'static str>().unwrap(), + "`gen fn` should just keep returning `None` after panicking", + ), + } +} diff --git a/tests/ui/coroutine/gen_block_panic.stderr b/tests/ui/coroutine/gen_block_panic.stderr new file mode 100644 index 0000000000000..33d2a95cab8bc --- /dev/null +++ b/tests/ui/coroutine/gen_block_panic.stderr @@ -0,0 +1,12 @@ +warning: unreachable statement + --> $DIR/gen_block_panic.rs:9:9 + | +LL | panic!("foo"); + | ------------- any code following this expression is unreachable +LL | yield 69; + | ^^^^^^^^^ unreachable statement + | + = note: `#[warn(unreachable_code)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/coroutine/gen_fn.e2024.stderr b/tests/ui/coroutine/gen_fn.e2024.stderr index 41bd04d976912..388e10fd65e44 100644 --- a/tests/ui/coroutine/gen_fn.e2024.stderr +++ b/tests/ui/coroutine/gen_fn.e2024.stderr @@ -1,10 +1,10 @@ -error: `gen` blocks are not yet implemented +error: `gen` functions are not yet implemented --> $DIR/gen_fn.rs:4:1 | LL | gen fn foo() {} | ^^^ | - = help: only the keyword is reserved for now + = help: for now you can use `gen {}` blocks and return `impl Iterator` instead error: aborting due to previous error diff --git a/tests/ui/coroutine/gen_fn.rs b/tests/ui/coroutine/gen_fn.rs index 9566660dfb566..da515f263b00a 100644 --- a/tests/ui/coroutine/gen_fn.rs +++ b/tests/ui/coroutine/gen_fn.rs @@ -3,6 +3,6 @@ gen fn foo() {} //[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen` -//[e2024]~^^ ERROR: `gen` blocks are not yet implemented +//[e2024]~^^ ERROR: `gen` functions are not yet implemented fn main() {} diff --git a/tests/ui/lint/lint-unnecessary-parens.fixed b/tests/ui/lint/lint-unnecessary-parens.fixed index bafac05d8daa7..b17914da6e6a3 100644 --- a/tests/ui/lint/lint-unnecessary-parens.fixed +++ b/tests/ui/lint/lint-unnecessary-parens.fixed @@ -84,6 +84,14 @@ fn main() { _a = 0; //~ ERROR unnecessary parentheses around assigned value _a += 1; //~ ERROR unnecessary parentheses around assigned value + let mut _a = 3; //~ ERROR unnecessary parentheses around pattern + let mut _a = 3; //~ ERROR unnecessary parentheses around pattern + let mut _a = 3; //~ ERROR unnecessary parentheses around pattern + + let _a = 3; //~ ERROR unnecessary parentheses around pattern + let _a = 3; //~ ERROR unnecessary parentheses around pattern + let _a = 3; //~ ERROR unnecessary parentheses around pattern + let _a = baz!(3, 4); let _b = baz!(3); } diff --git a/tests/ui/lint/lint-unnecessary-parens.rs b/tests/ui/lint/lint-unnecessary-parens.rs index ce537a4dc1da0..4cbd6562cd395 100644 --- a/tests/ui/lint/lint-unnecessary-parens.rs +++ b/tests/ui/lint/lint-unnecessary-parens.rs @@ -84,6 +84,14 @@ fn main() { _a = (0); //~ ERROR unnecessary parentheses around assigned value _a += (1); //~ ERROR unnecessary parentheses around assigned value + let(mut _a) = 3; //~ ERROR unnecessary parentheses around pattern + let (mut _a) = 3; //~ ERROR unnecessary parentheses around pattern + let( mut _a) = 3; //~ ERROR unnecessary parentheses around pattern + + let(_a) = 3; //~ ERROR unnecessary parentheses around pattern + let (_a) = 3; //~ ERROR unnecessary parentheses around pattern + let( _a) = 3; //~ ERROR unnecessary parentheses around pattern + let _a = baz!(3, 4); let _b = baz!(3); } diff --git a/tests/ui/lint/lint-unnecessary-parens.stderr b/tests/ui/lint/lint-unnecessary-parens.stderr index 2ad07530f8c8d..ba7a78b8da1a9 100644 --- a/tests/ui/lint/lint-unnecessary-parens.stderr +++ b/tests/ui/lint/lint-unnecessary-parens.stderr @@ -267,5 +267,77 @@ LL - _a += (1); LL + _a += 1; | -error: aborting due to 22 previous errors +error: unnecessary parentheses around pattern + --> $DIR/lint-unnecessary-parens.rs:87:8 + | +LL | let(mut _a) = 3; + | ^ ^ + | +help: remove these parentheses + | +LL - let(mut _a) = 3; +LL + let mut _a = 3; + | + +error: unnecessary parentheses around pattern + --> $DIR/lint-unnecessary-parens.rs:88:9 + | +LL | let (mut _a) = 3; + | ^ ^ + | +help: remove these parentheses + | +LL - let (mut _a) = 3; +LL + let mut _a = 3; + | + +error: unnecessary parentheses around pattern + --> $DIR/lint-unnecessary-parens.rs:89:8 + | +LL | let( mut _a) = 3; + | ^^ ^ + | +help: remove these parentheses + | +LL - let( mut _a) = 3; +LL + let mut _a = 3; + | + +error: unnecessary parentheses around pattern + --> $DIR/lint-unnecessary-parens.rs:91:8 + | +LL | let(_a) = 3; + | ^ ^ + | +help: remove these parentheses + | +LL - let(_a) = 3; +LL + let _a = 3; + | + +error: unnecessary parentheses around pattern + --> $DIR/lint-unnecessary-parens.rs:92:9 + | +LL | let (_a) = 3; + | ^ ^ + | +help: remove these parentheses + | +LL - let (_a) = 3; +LL + let _a = 3; + | + +error: unnecessary parentheses around pattern + --> $DIR/lint-unnecessary-parens.rs:93:8 + | +LL | let( _a) = 3; + | ^^ ^ + | +help: remove these parentheses + | +LL - let( _a) = 3; +LL + let _a = 3; + | + +error: aborting due to 28 previous errors diff --git a/tests/ui/lint/unused/issue-117284-arg-in-macro.rs b/tests/ui/lint/unused/issue-117284-arg-in-macro.rs new file mode 100644 index 0000000000000..eea0f4c594dc8 --- /dev/null +++ b/tests/ui/lint/unused/issue-117284-arg-in-macro.rs @@ -0,0 +1,17 @@ +#![deny(unused_variables)] +macro_rules! make_var { + ($struct:ident, $var:ident) => { + let $var = $struct.$var; + }; +} + +#[allow(unused)] +struct MyStruct { + var: i32, +} + +fn main() { + let s = MyStruct { var: 42 }; + make_var!(s, var); //~ ERROR unused variable: `var` + let a = 1; //~ ERROR unused variable: `a` +} diff --git a/tests/ui/lint/unused/issue-117284-arg-in-macro.stderr b/tests/ui/lint/unused/issue-117284-arg-in-macro.stderr new file mode 100644 index 0000000000000..84efaa4f3687b --- /dev/null +++ b/tests/ui/lint/unused/issue-117284-arg-in-macro.stderr @@ -0,0 +1,29 @@ +error: unused variable: `var` + --> $DIR/issue-117284-arg-in-macro.rs:15:18 + | +LL | make_var!(s, var); + | ^^^ + | +help: `var` is captured in macro and introduced a unused variable + --> $DIR/issue-117284-arg-in-macro.rs:4:13 + | +LL | let $var = $struct.$var; + | ^^^^ +... +LL | make_var!(s, var); + | ----------------- in this macro invocation +note: the lint level is defined here + --> $DIR/issue-117284-arg-in-macro.rs:1:9 + | +LL | #![deny(unused_variables)] + | ^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `make_var` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unused variable: `a` + --> $DIR/issue-117284-arg-in-macro.rs:16:9 + | +LL | let a = 1; + | ^ help: if this is intentional, prefix it with an underscore: `_a` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/type-alias-impl-trait/nested_impl_trait_in_assoc_ty.rs b/tests/ui/type-alias-impl-trait/nested_impl_trait_in_assoc_ty.rs new file mode 100644 index 0000000000000..5f3dbaa179891 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/nested_impl_trait_in_assoc_ty.rs @@ -0,0 +1,44 @@ +//! This test checks that we do not walk types in async blocks for +//! determining the opaque types that appear in a signature. async blocks, +//! all other coroutines and closures are always private and not part of +//! a signature. They become part of a signature via `dyn Trait` or `impl Trait`, +//! which is something that we process abstractly without looking at its hidden +//! types. +// edition: 2021 +// check-pass + +#![feature(impl_trait_in_assoc_type)] + +use std::future::Future; + +pub struct MemtableLocalStateStore { + mem_table: MemTable, +} + +impl LocalStateStore for MemtableLocalStateStore { + type IterStream<'a> = impl Sized + 'a where Self: 'a; + + fn iter(&self) -> impl Future> + '_ { + async move { merge_stream(self.mem_table.iter()) } + } +} + +trait LocalStateStore { + type IterStream<'a> + where + Self: 'a; + + fn iter(&self) -> impl Future> + '_; +} + +struct MemTable; + +impl MemTable { + fn iter<'a>(&'a self) -> impl Iterator { + std::iter::empty() + } +} + +pub(crate) async fn merge_stream<'a>(mem_table_iter: impl Iterator) {} + +fn main() {}