diff --git a/Cargo.lock b/Cargo.lock index 1948e6c2e5ef6..5093b097fc3c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3797,7 +3797,6 @@ dependencies = [ name = "rustc_expand" version = "0.0.0" dependencies = [ - "crossbeam-channel", "rustc_ast", "rustc_ast_passes", "rustc_ast_pretty", diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs index 6230ca15d6e10..d1b21d0a0b6c5 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs @@ -68,15 +68,7 @@ impl DebugContext { // In order to have a good line stepping behavior in debugger, we overwrite debug // locations of macro expansions with that of the outermost expansion site (when the macro is // annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided). - let span = if tcx.should_collapse_debuginfo(span) { - span - } else { - // Walk up the macro expansion chain until we reach a non-expanded span. - // We also stop at the function body level because no line stepping can occur - // at the level above that. - rustc_span::hygiene::walk_chain(span, function_span.ctxt()) - }; - + let span = tcx.collapsed_debuginfo(span, function_span); match tcx.sess.source_map().lookup_line(span.lo()) { Ok(SourceFileAndLine { sf: file, line }) => { let line_pos = file.lines()[line]; diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 14915e816ee9b..48f3f4f25228a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -228,21 +228,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// In order to have a good line stepping behavior in debugger, we overwrite debug /// locations of macro expansions with that of the outermost expansion site (when the macro is /// annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided). - fn adjust_span_for_debugging(&self, mut span: Span) -> Span { + fn adjust_span_for_debugging(&self, span: Span) -> Span { // Bail out if debug info emission is not enabled. if self.debug_context.is_none() { return span; } - - if self.cx.tcx().should_collapse_debuginfo(span) { - // Walk up the macro expansion chain until we reach a non-expanded span. - // We also stop at the function body level because no line stepping can occur - // at the level above that. - // Use span of the outermost expansion site, while keeping the original lexical scope. - span = rustc_span::hygiene::walk_chain(span, self.mir.span.ctxt()); - } - - span + // Walk up the macro expansion chain until we reach a non-expanded span. + // We also stop at the function body level because no line stepping can occur + // at the level above that. + // Use span of the outermost expansion site, while keeping the original lexical scope. + self.cx.tcx().collapsed_debuginfo(span, self.mir.span) } fn spill_operand_to_stack( diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 7e1cbfe66674c..1efa52df5817b 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -157,9 +157,7 @@ impl Qualif for NeedsNonConstDrop { // FIXME(effects): If `destruct` is not a `const_trait`, // or effects are disabled in this crate, then give up. let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span)); - if cx.tcx.generics_of(destruct_def_id).host_effect_index.is_none() - || !cx.tcx.features().effects - { + if !cx.tcx.has_host_param(destruct_def_id) || !cx.tcx.features().effects { return NeedsDrop::in_any_value_of_ty(cx, ty); } diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 9189a501aa5f4..63247f9d0519c 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -9,7 +9,6 @@ doctest = false [dependencies] # tidy-alphabetical-start -crossbeam-channel = "0.5.0" rustc_ast = { path = "../rustc_ast" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 001b3fe44968e..a1d213619571b 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -13,16 +13,16 @@ use rustc_session::config::ProcMacroExecutionStrategy; use rustc_span::profiling::SpannedEventArgRecorder; use rustc_span::{Span, DUMMY_SP}; -struct CrossbeamMessagePipe { - tx: crossbeam_channel::Sender, - rx: crossbeam_channel::Receiver, +struct MessagePipe { + tx: std::sync::mpsc::SyncSender, + rx: std::sync::mpsc::Receiver, } -impl pm::bridge::server::MessagePipe for CrossbeamMessagePipe { +impl pm::bridge::server::MessagePipe for MessagePipe { fn new() -> (Self, Self) { - let (tx1, rx1) = crossbeam_channel::bounded(1); - let (tx2, rx2) = crossbeam_channel::bounded(1); - (CrossbeamMessagePipe { tx: tx1, rx: rx2 }, CrossbeamMessagePipe { tx: tx2, rx: rx1 }) + let (tx1, rx1) = std::sync::mpsc::sync_channel(1); + let (tx2, rx2) = std::sync::mpsc::sync_channel(1); + (MessagePipe { tx: tx1, rx: rx2 }, MessagePipe { tx: tx2, rx: rx1 }) } fn send(&mut self, value: T) { @@ -35,7 +35,7 @@ impl pm::bridge::server::MessagePipe for CrossbeamMessagePipe { } fn exec_strategy(ecx: &ExtCtxt<'_>) -> impl pm::bridge::server::ExecutionStrategy { - pm::bridge::server::MaybeCrossThread::>::new( + pm::bridge::server::MaybeCrossThread::>::new( ecx.sess.opts.unstable_opts.proc_macro_execution_strategy == ProcMacroExecutionStrategy::CrossThread, ) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e88b876534e50..7585f7bc902d5 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1997,7 +1997,7 @@ pub enum LocalSource { AsyncFn, /// A desugared `.await`. AwaitDesugar, - /// A desugared `expr = expr`, where the LHS is a tuple, struct or array. + /// A desugared `expr = expr`, where the LHS is a tuple, struct, array or underscore expression. /// The span is that of the `=` sign. AssignDesugar(Span), } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 8939c2ddea54e..fc61467df0142 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -490,11 +490,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.require_lang_item(hir::LangItem::FnOnce, Some(span)); let fn_once_output_def_id = self.tcx.require_lang_item(hir::LangItem::FnOnceOutput, Some(span)); - if self.tcx.generics_of(fn_once_def_id).host_effect_index.is_none() { - if idx == 0 && !self.tcx.is_const_fn_raw(def_id) { - self.dcx().emit_err(errors::ConstSelectMustBeConst { span }); - } - } else { + if self.tcx.has_host_param(fn_once_def_id) { let const_param: ty::GenericArg<'tcx> = ([self.tcx.consts.false_, self.tcx.consts.true_])[idx].into(); self.register_predicate(traits::Obligation::new( @@ -523,6 +519,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); self.select_obligations_where_possible(|_| {}); + } else if idx == 0 && !self.tcx.is_const_fn_raw(def_id) { + self.dcx().emit_err(errors::ConstSelectMustBeConst { span }); } } else { self.dcx().emit_err(errors::ConstSelectMustBeFn { span, ty: arg_ty }); diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index 3eefd1b0e0833..bdace8e01f6c5 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -108,6 +108,10 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { if !matches!(local.pat.kind, hir::PatKind::Wild) { return; } + + if matches!(local.source, rustc_hir::LocalSource::AsyncFn) { + return; + } if let Some(init) = local.init { let init_ty = cx.typeck_results().expr_ty(init); // If the type has a trivial Drop implementation, then it doesn't @@ -126,6 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { suggestion: local.pat.span, multi_suggestion_start: local.span.until(init.span), multi_suggestion_end: init.span.shrink_to_hi(), + is_assign_desugar: matches!(local.source, rustc_hir::LocalSource::AssignDesugar(_)), }; if is_sync_lock { let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 614c3ecbcae2b..bc9a9d7b7452d 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -932,6 +932,7 @@ pub struct NonBindingLetSub { pub suggestion: Span, pub multi_suggestion_start: Span, pub multi_suggestion_end: Span, + pub is_assign_desugar: bool, } impl AddToDiagnostic for NonBindingLetSub { @@ -939,10 +940,11 @@ impl AddToDiagnostic for NonBindingLetSub { where F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, { + let prefix = if self.is_assign_desugar { "let " } else { "" }; diag.span_suggestion_verbose( self.suggestion, fluent::lint_non_binding_let_suggestion, - "_unused", + format!("{prefix}_unused"), Applicability::MachineApplicable, ); diag.multipart_suggestion( diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 4af82f124719a..9e8d7c2ef3ecb 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -51,7 +51,7 @@ use rustc_session::lint::LintBuffer; pub use rustc_session::lint::RegisteredTools; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{ExpnId, ExpnKind, Span}; +use rustc_span::{hygiene, ExpnId, ExpnKind, Span}; use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx}; pub use rustc_target::abi::{ReprFlags, ReprOptions}; pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, WithInfcx}; @@ -2518,21 +2518,21 @@ impl<'tcx> TyCtxt<'tcx> { (ident, scope) } - /// Returns `true` if the debuginfo for `span` should be collapsed to the outermost expansion - /// site. Only applies when `Span` is the result of macro expansion. + /// Returns corrected span if the debuginfo for `span` should be collapsed to the outermost + /// expansion site (with collapse_debuginfo attribute if the corresponding feature enabled). + /// Only applies when `Span` is the result of macro expansion. /// /// - If the `collapse_debuginfo` feature is enabled then debuginfo is not collapsed by default - /// and only when a macro definition is annotated with `#[collapse_debuginfo]`. + /// and only when a (some enclosing) macro definition is annotated with `#[collapse_debuginfo]`. /// - If `collapse_debuginfo` is not enabled, then debuginfo is collapsed by default. /// /// When `-Zdebug-macros` is provided then debuginfo will never be collapsed. - pub fn should_collapse_debuginfo(self, span: Span) -> bool { - !self.sess.opts.unstable_opts.debug_macros - && if self.features().collapse_debuginfo { - span.in_macro_expansion_with_collapse_debuginfo() - } else { - span.from_expansion() - } + pub fn collapsed_debuginfo(self, span: Span, upto: Span) -> Span { + if self.sess.opts.unstable_opts.debug_macros || !span.from_expansion() { + return span; + } + let collapse_debuginfo_enabled = self.features().collapse_debuginfo; + hygiene::walk_chain_collapsed(span, upto, collapse_debuginfo_enabled) } #[inline] diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index b9c75bd205b24..f2a33429bd760 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1,7 +1,7 @@ //! Miscellaneous type-system utilities that are too small to deserve their own modules. use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use crate::query::Providers; +use crate::query::{IntoQueryParam, Providers}; use crate::ty::layout::IntegerExt; use crate::ty::{ self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, @@ -786,6 +786,13 @@ impl<'tcx> TyCtxt<'tcx> { || self.extern_crate(key.as_def_id()).is_some_and(|e| e.is_direct()) } + /// Whether the item has a host effect param. This is different from `TyCtxt::is_const`, + /// because the item must also be "maybe const", and the crate where the item is + /// defined must also have the effects feature enabled. + pub fn has_host_param(self, def_id: impl IntoQueryParam) -> bool { + self.generics_of(def_id).host_effect_index.is_some() + } + pub fn expected_host_effect_param_for_body(self, def_id: impl Into) -> ty::Const<'tcx> { let def_id = def_id.into(); // FIXME(effects): This is suspicious and should probably not be done, diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index aa7b6b02f74e1..dcd7014f4fc90 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -23,7 +23,7 @@ use rustc_middle::mir::{ use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::SourceMap; -use rustc_span::{Span, Symbol}; +use rustc_span::{BytePos, Pos, RelativeBytePos, Span, Symbol}; /// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected /// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen @@ -107,6 +107,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { ); let mappings = self.create_mappings(&coverage_spans, &coverage_counters); + if mappings.is_empty() { + // No spans could be converted into valid mappings, so skip this function. + debug!("no spans could be converted into valid mappings; skipping"); + return; + } + self.inject_coverage_statements(bcb_has_coverage_spans, &coverage_counters); self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { @@ -148,9 +154,9 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { // Flatten the spans into individual term/span pairs. .flat_map(|(term, spans)| spans.iter().map(move |&span| (term, span))) // Convert each span to a code region, and create the final mapping. - .map(|(term, span)| { - let code_region = make_code_region(source_map, file_name, span, body_span); - Mapping { term, code_region } + .filter_map(|(term, span)| { + let code_region = make_code_region(source_map, file_name, span, body_span)?; + Some(Mapping { term, code_region }) }) .collect::>() } @@ -252,13 +258,22 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb data.statements.insert(0, statement); } -/// Convert the Span into its file name, start line and column, and end line and column +/// Convert the Span into its file name, start line and column, and end line and column. +/// +/// Line numbers and column numbers are 1-based. Unlike most column numbers emitted by +/// the compiler, these column numbers are denoted in **bytes**, because that's what +/// LLVM's `llvm-cov` tool expects to see in coverage maps. +/// +/// Returns `None` if the conversion failed for some reason. This shouldn't happen, +/// but it's hard to rule out entirely (especially in the presence of complex macros +/// or other expansions), and if it does happen then skipping a span or function is +/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid. fn make_code_region( source_map: &SourceMap, file_name: Symbol, span: Span, body_span: Span, -) -> CodeRegion { +) -> Option { debug!( "Called make_code_region(file_name={}, span={}, body_span={})", file_name, @@ -266,27 +281,62 @@ fn make_code_region( source_map.span_to_diagnostic_string(body_span) ); - let (file, mut start_line, mut start_col, mut end_line, mut end_col) = - source_map.span_to_location_info(span); - if span.hi() == span.lo() { - // Extend an empty span by one character so the region will be counted. - if span.hi() == body_span.hi() { - start_col = start_col.saturating_sub(1); - } else { - end_col = start_col + 1; - } + let lo = span.lo(); + let hi = span.hi(); + + let file = source_map.lookup_source_file(lo); + if !file.contains(hi) { + debug!(?span, ?file, ?lo, ?hi, "span crosses multiple files; skipping"); + return None; + } + + // Column numbers need to be in bytes, so we can't use the more convenient + // `SourceMap` methods for looking up file coordinates. + let rpos_and_line_and_byte_column = |pos: BytePos| -> Option<(RelativeBytePos, usize, usize)> { + let rpos = file.relative_position(pos); + let line_index = file.lookup_line(rpos)?; + let line_start = file.lines()[line_index]; + // Line numbers and column numbers are 1-based, so add 1 to each. + Some((rpos, line_index + 1, (rpos - line_start).to_usize() + 1)) }; - if let Some(file) = file { - start_line = source_map.doctest_offset_line(&file.name, start_line); - end_line = source_map.doctest_offset_line(&file.name, end_line); + + let (lo_rpos, mut start_line, mut start_col) = rpos_and_line_and_byte_column(lo)?; + let (hi_rpos, mut end_line, mut end_col) = rpos_and_line_and_byte_column(hi)?; + + // If the span is empty, try to expand it horizontally by one character's + // worth of bytes, so that it is more visible in `llvm-cov` reports. + // We do this after resolving line/column numbers, so that empty spans at the + // end of a line get an extra column instead of wrapping to the next line. + if span.is_empty() + && body_span.contains(span) + && let Some(src) = &file.src + { + // Prefer to expand the end position, if it won't go outside the body span. + if hi < body_span.hi() { + let hi_rpos = hi_rpos.to_usize(); + let nudge_bytes = src.ceil_char_boundary(hi_rpos + 1) - hi_rpos; + end_col += nudge_bytes; + } else if lo > body_span.lo() { + let lo_rpos = lo_rpos.to_usize(); + let nudge_bytes = lo_rpos - src.floor_char_boundary(lo_rpos - 1); + // Subtract the nudge, but don't go below column 1. + start_col = start_col.saturating_sub(nudge_bytes).max(1); + } + // If neither nudge could be applied, stick with the empty span coordinates. } - CodeRegion { + + // Apply an offset so that code in doctests has correct line numbers. + // FIXME(#79417): Currently we have no way to offset doctest _columns_. + start_line = source_map.doctest_offset_line(&file.name, start_line); + end_line = source_map.doctest_offset_line(&file.name, end_line); + + Some(CodeRegion { file_name, start_line: start_line as u32, start_col: start_col as u32, end_line: end_line as u32, end_col: end_col as u32, - } + }) } fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index f5f51c0ec8ad1..2c1602dadc17c 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -9,6 +9,7 @@ #![feature(min_specialization)] #![feature(never_type)] #![feature(option_get_or_insert_default)] +#![feature(round_char_boundary)] #![feature(trusted_step)] #![feature(try_blocks)] #![feature(yeet_expr)] diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 6a15961ee20fb..d03965b539c84 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -445,18 +445,46 @@ impl HygieneData { } fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span { + let orig_span = span; debug!("walk_chain({:?}, {:?})", span, to); debug!("walk_chain: span ctxt = {:?}", span.ctxt()); - while span.from_expansion() && span.ctxt() != to { + while span.ctxt() != to && span.from_expansion() { let outer_expn = self.outer_expn(span.ctxt()); debug!("walk_chain({:?}): outer_expn={:?}", span, outer_expn); let expn_data = self.expn_data(outer_expn); debug!("walk_chain({:?}): expn_data={:?}", span, expn_data); span = expn_data.call_site; } + debug!("walk_chain: for span {:?} >>> return span = {:?}", orig_span, span); span } + // We need to walk up and update return span if we meet macro instantiation to be collapsed + fn walk_chain_collapsed( + &self, + mut span: Span, + to: Span, + collapse_debuginfo_enabled: bool, + ) -> Span { + let orig_span = span; + let mut ret_span = span; + + debug!("walk_chain_collapsed({:?}, {:?})", span, to); + debug!("walk_chain_collapsed: span ctxt = {:?}", span.ctxt()); + while !span.eq_ctxt(to) && span.from_expansion() { + let outer_expn = self.outer_expn(span.ctxt()); + debug!("walk_chain_collapsed({:?}): outer_expn={:?}", span, outer_expn); + let expn_data = self.expn_data(outer_expn); + debug!("walk_chain_collapsed({:?}): expn_data={:?}", span, expn_data); + span = expn_data.call_site; + if !collapse_debuginfo_enabled || expn_data.collapse_debuginfo { + ret_span = span; + } + } + debug!("walk_chain_collapsed: for span {:?} >>> return span = {:?}", orig_span, ret_span); + ret_span + } + fn adjust(&self, ctxt: &mut SyntaxContext, expn_id: ExpnId) -> Option { let mut scope = None; while !self.is_descendant_of(expn_id, self.outer_expn(*ctxt)) { @@ -573,6 +601,10 @@ pub fn walk_chain(span: Span, to: SyntaxContext) -> Span { HygieneData::with(|data| data.walk_chain(span, to)) } +pub fn walk_chain_collapsed(span: Span, to: Span, collapse_debuginfo_enabled: bool) -> Span { + HygieneData::with(|hdata| hdata.walk_chain_collapsed(span, to, collapse_debuginfo_enabled)) +} + pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { // The new contexts that need updating are at the end of the list and have `$crate` as a name. let (len, to_update) = HygieneData::with(|data| { diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index bac0802241d68..4235293823c29 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -567,13 +567,6 @@ impl Span { !self.ctxt().is_root() } - /// Returns `true` if `span` originates in a macro's expansion where debuginfo should be - /// collapsed. - pub fn in_macro_expansion_with_collapse_debuginfo(self) -> bool { - let outer_expn = self.ctxt().outer_expn_data(); - matches!(outer_expn.kind, ExpnKind::Macro(..)) && outer_expn.collapse_debuginfo - } - /// Returns `true` if `span` originates in a derive-macro's expansion. pub fn in_derive_expansion(self) -> bool { matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _)) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 9d25388b90fd0..8e26327196a1a 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1601,6 +1601,8 @@ supported_targets! { ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), ("riscv32imc-esp-espidf", riscv32imc_esp_espidf), ("riscv32imac-esp-espidf", riscv32imac_esp_espidf), + ("riscv32imafc-esp-espidf", riscv32imafc_esp_espidf), + ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf), ("riscv32imafc-unknown-none-elf", riscv32imafc_unknown_none_elf), ("riscv32imac-unknown-xous-elf", riscv32imac_unknown_xous_elf), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs b/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs new file mode 100644 index 0000000000000..6c7c920bd1803 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs @@ -0,0 +1,30 @@ +use crate::spec::{cvs, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + llvm_target: "riscv32".into(), + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "espidf".into(), + env: "newlib".into(), + vendor: "espressif".into(), + linker: Some("riscv32-esp-elf-gcc".into()), + cpu: "generic-rv32".into(), + + max_atomic_width: Some(32), + atomic_cas: true, + + llvm_abiname: "ilp32f".into(), + features: "+m,+a,+c,+f".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 19eae93df9c87..c40ed10e52ff3 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -271,7 +271,7 @@ pub fn closure_trait_ref_and_return_type<'tcx>( TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()), }; - let trait_ref = if tcx.generics_of(fn_trait_def_id).host_effect_index.is_some() { + let trait_ref = if tcx.has_host_param(fn_trait_def_id) { ty::TraitRef::new( tcx, fn_trait_def_id, diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index 99adbb91599ac..2c7845d4304fd 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -70,7 +70,7 @@ /// implementation is still specific in this sense; for example, [`Vec`][vec] /// dereferences to `[T]`, so methods of `T` are not applicable. /// -/// Consider also that deref coericion means that deref traits are a much larger +/// Consider also that deref coercion means that deref traits are a much larger /// part of a type's public API than any other trait as it is implicitly called /// by the compiler. Therefore, it is advisable to consider whether this is /// something you are comfortable supporting as a public API. diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 33a678a31d6a3..2ddf5737fbd91 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -323,6 +323,7 @@ target | std | host | notes [`riscv32imac-unknown-xous-elf`](platform-support/riscv32imac-unknown-xous-elf.md) | ? | | RISC-V Xous (RV32IMAC ISA) [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF +[`riscv32imafc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ | | RISC-V Hermit `riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD `riscv64gc-unknown-fuchsia` | | | RISC-V Fuchsia diff --git a/src/doc/rustc/src/platform-support/esp-idf.md b/src/doc/rustc/src/platform-support/esp-idf.md index 8f630fa152c49..1f8d985980996 100644 --- a/src/doc/rustc/src/platform-support/esp-idf.md +++ b/src/doc/rustc/src/platform-support/esp-idf.md @@ -19,6 +19,7 @@ The target names follow this format: `$ARCH-esp-espidf`, where `$ARCH` specifies | `riscv32imc-esp-espidf` | [ESP32-C3](https://www.espressif.com/en/products/socs/esp32-c3) | `v4.3` | | `riscv32imac-esp-espidf` | [ESP32-C6](https://www.espressif.com/en/products/socs/esp32-c6) | `v5.1` | | `riscv32imac-esp-espidf` | [ESP32-H2](https://www.espressif.com/en/products/socs/esp32-h2) | `v5.1` | +| `riscv32imafc-esp-espidf`| [ESP32-P4](https://www.espressif.com/en/news/ESP32-P4) | `v5.2` | It is recommended to use the latest ESP-IDF stable release if possible. diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index b258b748ca876..8be4def15dedc 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -19,7 +19,6 @@ use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile}; use regex::{Captures, Regex}; use rustfix::{apply_suggestions, get_suggestions_from_json, Filter}; -use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::env; use std::ffi::{OsStr, OsString}; @@ -725,7 +724,7 @@ impl<'test> TestCx<'test> { /// Replace line numbers in coverage reports with the placeholder `LL`, /// so that the tests are less sensitive to lines being added/removed. - fn anonymize_coverage_line_numbers(coverage: &str) -> Cow<'_, str> { + fn anonymize_coverage_line_numbers(coverage: &str) -> String { // The coverage reporter prints line numbers at the start of a line. // They are truncated or left-padded to occupy exactly 5 columns. // (`LineNumberColumnWidth` in `SourceCoverageViewText.cpp`.) @@ -733,9 +732,28 @@ impl<'test> TestCx<'test> { // // Line numbers that appear inside expansion/instantiation subviews // have an additional prefix of ` |` for each nesting level. + // + // Branch views also include the relevant line number, so we want to + // redact those too. (These line numbers don't have padding.) + // + // Note: The pattern `(?m:^)` matches the start of a line. + + // ` 1|` => ` LL|` + // ` 10|` => ` LL|` + // ` 100|` => ` LL|` + // ` | 1000|` => ` | LL|` + // ` | | 1000|` => ` | | LL|` static LINE_NUMBER_RE: Lazy = Lazy::new(|| Regex::new(r"(?m:^)(?(?: \|)*) *[0-9]+\|").unwrap()); - LINE_NUMBER_RE.replace_all(coverage, "$prefix LL|") + let coverage = LINE_NUMBER_RE.replace_all(&coverage, "${prefix} LL|"); + + // ` | Branch (1:` => ` | Branch (LL:` + // ` | | Branch (10:` => ` | | Branch (LL:` + static BRANCH_LINE_NUMBER_RE: Lazy = + Lazy::new(|| Regex::new(r"(?m:^)(?(?: \|)+ Branch \()[0-9]+:").unwrap()); + let coverage = BRANCH_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:"); + + coverage.into_owned() } /// Coverage reports can describe multiple source files, separated by diff --git a/src/tools/compiletest/src/runtest/tests.rs b/src/tools/compiletest/src/runtest/tests.rs index fb3dd326a4c80..ee42243e83d8a 100644 --- a/src/tools/compiletest/src/runtest/tests.rs +++ b/src/tools/compiletest/src/runtest/tests.rs @@ -48,3 +48,75 @@ fn normalize_platform_differences() { r#"println!("test\ntest")"#, ); } + +/// Test for anonymizing line numbers in coverage reports, especially for +/// branch regions. +/// +/// FIXME(#119681): This test can be removed when we have examples of branch +/// coverage in the actual coverage test suite. +#[test] +fn anonymize_coverage_line_numbers() { + let anon = |coverage| TestCx::anonymize_coverage_line_numbers(coverage); + + let input = r#" + 6| 3|fn print_size() { + 7| 3| if std::mem::size_of::() > 4 { + ------------------ + | Branch (7:8): [True: 0, False: 1] + | Branch (7:8): [True: 0, False: 1] + | Branch (7:8): [True: 1, False: 0] + ------------------ + 8| 1| println!("size > 4"); +"#; + + let expected = r#" + LL| 3|fn print_size() { + LL| 3| if std::mem::size_of::() > 4 { + ------------------ + | Branch (LL:8): [True: 0, False: 1] + | Branch (LL:8): [True: 0, False: 1] + | Branch (LL:8): [True: 1, False: 0] + ------------------ + LL| 1| println!("size > 4"); +"#; + + assert_eq!(anon(input), expected); + + ////////// + + let input = r#" + 12| 3|} + ------------------ + | branch_generics::print_size::<()>: + | 6| 1|fn print_size() { + | 7| 1| if std::mem::size_of::() > 4 { + | ------------------ + | | Branch (7:8): [True: 0, False: 1] + | ------------------ + | 8| 0| println!("size > 4"); + | 9| 1| } else { + | 10| 1| println!("size <= 4"); + | 11| 1| } + | 12| 1|} + ------------------ +"#; + + let expected = r#" + LL| 3|} + ------------------ + | branch_generics::print_size::<()>: + | LL| 1|fn print_size() { + | LL| 1| if std::mem::size_of::() > 4 { + | ------------------ + | | Branch (LL:8): [True: 0, False: 1] + | ------------------ + | LL| 0| println!("size > 4"); + | LL| 1| } else { + | LL| 1| println!("size <= 4"); + | LL| 1| } + | LL| 1|} + ------------------ +"#; + + assert_eq!(anon(input), expected); +} diff --git a/tests/coverage/unicode.cov-map b/tests/coverage/unicode.cov-map new file mode 100644 index 0000000000000..cd40194a0831b --- /dev/null +++ b/tests/coverage/unicode.cov-map @@ -0,0 +1,53 @@ +Function name: unicode::main +Raw bytes (67): 0x[01, 01, 09, 01, 05, 03, 05, 1e, 0d, 22, 09, 03, 05, 11, 1b, 1e, 0d, 22, 09, 03, 05, 09, 01, 0e, 01, 00, 0b, 05, 01, 09, 00, 0c, 03, 00, 10, 00, 1b, 05, 00, 1c, 00, 28, 22, 02, 08, 00, 25, 09, 00, 29, 00, 46, 11, 00, 47, 02, 06, 1b, 02, 06, 00, 07, 17, 02, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 9 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(1) +- expression 2 operands: lhs = Expression(7, Sub), rhs = Counter(3) +- expression 3 operands: lhs = Expression(8, Sub), rhs = Counter(2) +- expression 4 operands: lhs = Expression(0, Add), rhs = Counter(1) +- expression 5 operands: lhs = Counter(4), rhs = Expression(6, Add) +- expression 6 operands: lhs = Expression(7, Sub), rhs = Counter(3) +- expression 7 operands: lhs = Expression(8, Sub), rhs = Counter(2) +- expression 8 operands: lhs = Expression(0, Add), rhs = Counter(1) +Number of file 0 mappings: 9 +- Code(Counter(0)) at (prev + 14, 1) to (start + 0, 11) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12) +- Code(Expression(0, Add)) at (prev + 0, 16) to (start + 0, 27) + = (c0 + c1) +- Code(Counter(1)) at (prev + 0, 28) to (start + 0, 40) +- Code(Expression(8, Sub)) at (prev + 2, 8) to (start + 0, 37) + = ((c0 + c1) - c1) +- Code(Counter(2)) at (prev + 0, 41) to (start + 0, 70) +- Code(Counter(4)) at (prev + 0, 71) to (start + 2, 6) +- Code(Expression(6, Add)) at (prev + 2, 6) to (start + 0, 7) + = ((((c0 + c1) - c1) - c2) + c3) +- Code(Expression(5, Add)) at (prev + 2, 5) to (start + 1, 2) + = (c4 + ((((c0 + c1) - c1) - c2) + c3)) + +Function name: unicode::サビ +Raw bytes (9): 0x[01, 01, 00, 01, 01, 1e, 14, 00, 18] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 30, 20) to (start + 0, 24) + +Function name: unicode::他 (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 1e, 19, 00, 25] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 30, 25) to (start + 0, 37) + +Function name: unicode::申し訳ございません +Raw bytes (9): 0x[01, 01, 00, 01, 01, 18, 01, 02, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 24, 1) to (start + 2, 2) + diff --git a/tests/coverage/unicode.coverage b/tests/coverage/unicode.coverage new file mode 100644 index 0000000000000..b284a557d5754 --- /dev/null +++ b/tests/coverage/unicode.coverage @@ -0,0 +1,40 @@ + LL| |// edition: 2021 + LL| |// ignore-windows - we can't force `llvm-cov` to use ANSI escapes on Windows + LL| |// llvm-cov-flags: --use-color + LL| | + LL| |// Check that column numbers are denoted in bytes, so that they don't cause + LL| |// `llvm-cov` to fail or emit malformed output. + LL| |// + LL| |// Note that when `llvm-cov` prints ^ arrows on a subsequent line, it simply + LL| |// inserts one space character for each "column", with no understanding of + LL| |// Unicode or character widths. So those arrows will tend to be misaligned + LL| |// for non-ASCII source code, regardless of whether column numbers are code + LL| |// points or bytes. + LL| | + LL| 1|fn main() { + LL| 33| for _İ in 'А'..='Я' { /* Я */ } + ^32 ^32 + LL| | + LL| 1| if 申し訳ございません() && 申し訳ございません() { + ^0 + LL| 0| println!("true"); + LL| 1| } + LL| | + LL| 1| サビ(); + LL| 1|} + LL| | + LL| 1|fn 申し訳ございません() -> bool { + LL| 1| std::hint::black_box(false) + LL| 1|} + LL| | + LL| |macro_rules! macro_that_defines_a_function { + LL| | (fn $名:ident () $体:tt) => { + LL| 1| fn $名 () $体 fn 他 () {} + ^0 + LL| | } + LL| |} + LL| | + LL| |macro_that_defines_a_function! { + LL| | fn サビ() {} + LL| |} + diff --git a/tests/coverage/unicode.rs b/tests/coverage/unicode.rs new file mode 100644 index 0000000000000..dfc5ea69dd231 --- /dev/null +++ b/tests/coverage/unicode.rs @@ -0,0 +1,36 @@ +// edition: 2021 +// ignore-windows - we can't force `llvm-cov` to use ANSI escapes on Windows +// llvm-cov-flags: --use-color + +// Check that column numbers are denoted in bytes, so that they don't cause +// `llvm-cov` to fail or emit malformed output. +// +// Note that when `llvm-cov` prints ^ arrows on a subsequent line, it simply +// inserts one space character for each "column", with no understanding of +// Unicode or character widths. So those arrows will tend to be misaligned +// for non-ASCII source code, regardless of whether column numbers are code +// points or bytes. + +fn main() { + for _İ in 'А'..='Я' { /* Я */ } + + if 申し訳ございません() && 申し訳ございません() { + println!("true"); + } + + サビ(); +} + +fn 申し訳ございません() -> bool { + std::hint::black_box(false) +} + +macro_rules! macro_that_defines_a_function { + (fn $名:ident () $体:tt) => { + fn $名 () $体 fn 他 () {} + } +} + +macro_that_defines_a_function! { + fn サビ() {} +} diff --git a/tests/debuginfo/collapse-debuginfo-in-non-collapse-macro.rs b/tests/debuginfo/collapse-debuginfo-in-non-collapse-macro.rs new file mode 100644 index 0000000000000..d9500c3641ef6 --- /dev/null +++ b/tests/debuginfo/collapse-debuginfo-in-non-collapse-macro.rs @@ -0,0 +1,124 @@ +// ignore-lldb +#![feature(collapse_debuginfo)] + +// Test that statement, skipped/added/reordered by macros, is correctly processed in debuginfo. +// When nested macros instantiations are tagged with collapse_debuginfo attribute, +// debug info should be corrected to the first outer macro instantiation +// without collapse_debuginfo attribute. +// collapse_debuginfo feature enabled. + +// compile-flags:-g + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1_pre[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_in_proxy[...] +// gdb-command:next 2 +// gdb-check:[...]#loc_rem_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1_pre[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_in_proxy[...] +// gdb-command:next 2 +// gdb-check:[...]#loc_add_macro[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder_call2[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1_pre[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_in_proxy[...] +// gdb-command:next 2 +// gdb-command:frame +// gdb-command:continue + +#[inline(never)] +fn myprintln_impl(text: &str) { + println!("{}", text) +} + +#[collapse_debuginfo] +macro_rules! myprintln { + ($($arg:tt)*) => {{ + myprintln_impl($($arg)*); + }}; +} + +macro_rules! proxy_println { + ($($arg:tt)*) => {{ + myprintln!($($arg)*); // #loc_in_proxy + }}; +} + +// Macro accepts 3 statements and removes the 2nd statement +macro_rules! remove_second_statement { + ($s1:stmt; $s2:stmt; $s3:stmt;) => { $s1 $s3 } +} + +macro_rules! add_second_statement { + ($s1:stmt; $s3:stmt;) => { + $s1 + call2(); // #loc_add_macro + $s3 + } +} + +macro_rules! reorder_statements { + ($s1:stmt; $s2:stmt; $s3:stmt;) => { $s2 $s3 $s1 } +} + +fn call1() { + let rv = 0; // #loc_call1_pre + proxy_println!("one"); // #loc_call1 +} + +fn call2() { + proxy_println!("two"); // #loc_call2 +} + +fn call3() { + proxy_println!("three"); // #loc_call3 +} + +fn main() { + let ret = 0; // #break, step should go to call1 + remove_second_statement! { // #loc_rem_hdr + call1(); // #loc_rem_call1 + call2(); // #loc_rem_call2 + call3(); // #loc_rem_call3 + } + add_second_statement! { // #loc_add_hdr + call1(); // #loc_add_call1 + call3(); // #loc_add_call3 + } + reorder_statements! { // #loc_reorder_hdr + call1(); // #loc_reorder_call1 + call2(); // #loc_reorder_call2 + call3(); // #loc_reorder_call3 + } + std::process::exit(ret); // #loc_exit +} diff --git a/tests/debuginfo/skip_second_statement.rs b/tests/debuginfo/skip_second_statement.rs new file mode 100644 index 0000000000000..535b54747631a --- /dev/null +++ b/tests/debuginfo/skip_second_statement.rs @@ -0,0 +1,168 @@ +// ignore-lldb + +// Test that statement, skipped/added/reordered by macros, is correctly processed in debuginfo. +// Performed step-over and step-into debug stepping through call statements. +// collapse_debuginfo feature disabled. + +// compile-flags:-g + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem1_call1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem1_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem2_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1[...] +// gdb-command:next 2 +// gdb-check:[...]#loc_rem2_call3[...] +// gdb-command:step 2 +// gdb-command:frame +// gdb-check:[...]#loc_call3_println[...] +// gdb-command:next 3 +// gdb-command:frame +// gdb-check:[...]#loc_after_rem[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add1_call1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add1_hdr[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add1_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add2_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1[...] +// gdb-command:next 2 +// gdb-check:[...]#loc_add2_hdr[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call2[...] +// gdb-command:next 2 +// gdb-command:frame +// gdb-check:[...]#loc_add2_call3[...] +// gdb-command:step 2 +// gdb-command:frame +// gdb-check:[...]#loc_call3_println[...] +// gdb-command:next 3 +// gdb-command:frame +// gdb-check:[...]#loc_reorder1_call2[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder1_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder1_call1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder2_call2[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call2[...] +// gdb-command:next 2 +// gdb-command:frame +// gdb-check:[...]#loc_reorder2_call3[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call3[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call3_println[...] +// gdb-command:next 3 +// gdb-command:frame +// gdb-check:[...]#loc_reorder2_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1[...] +// gdb-command:next 2 +// gdb-command:continue + +#[inline(never)] +fn myprintln_impl(text: &str) { + println!("{}", text) +} + +macro_rules! myprintln { + ($($arg:tt)*) => {{ + myprintln_impl($($arg)*); + }}; +} + +// Macro accepts 3 statements and removes the 2nd statement +macro_rules! remove_second_statement { + ($s1:stmt; $s2:stmt; $s3:stmt;) => { $s1 $s3 } +} + +macro_rules! add_second_statement { + ($s1:stmt; $s3:stmt;) => { + $s1 + call2(); // #loc_add_macro + $s3 + } +} + +macro_rules! reorder_statements { + ($s1:stmt; $s2:stmt; $s3:stmt;) => { $s2 $s3 $s1 } +} + +fn call1() { + myprintln!("one"); // #loc_call1 +} + +fn call2() { + myprintln!("two"); // #loc_call2 +} + +fn call3() { + (||{ + myprintln!("three") // #loc_call3_println + })(); // #loc_call3 +} + +fn main() { + let ret = 0; // #break, step should go to call1 + remove_second_statement! { // #loc_rem1_hdr + call1(); // #loc_rem1_call1, breakpoint should set to call1, step should go call3 + call2(); // #loc_rem1_call2, breakpoint should set to call3 + call3(); // #loc_rem1_call3 + } + remove_second_statement! { // #loc_rem2_hdr + call1(); // #loc_rem2_call1, breakpoint should set to call1, step should go call3 + call2(); // #loc_rem2_call2, breakpoint should set to call3 + call3(); // #loc_rem2_call3, breakpoint should set to call3 + } + myprintln!("After remove_second_statement test"); // #loc_after_rem + + add_second_statement! { // #loc_add1_hdr + call1(); // #loc_add1_call1 + call3(); // #loc_add1_call3 + } + add_second_statement! { // #loc_add2_hdr + call1(); // #loc_add2_call1 + call3(); // #loc_add2_call3 + } + + reorder_statements! { // #loc_reorder1_hdr + call1(); // #loc_reorder1_call1 + call2(); // #loc_reorder1_call2 + call3(); // #loc_reorder1_call3 + } + reorder_statements! { // #loc_reorder2_hdr + call1(); // #loc_reorder2_call1 + call2(); // #loc_reorder2_call2 + call3(); // #loc_reorder2_call3 + } + + std::process::exit(ret); // #loc_exit +} diff --git a/tests/debuginfo/skip_second_statement_collapse.rs b/tests/debuginfo/skip_second_statement_collapse.rs new file mode 100644 index 0000000000000..a0557ca9feec2 --- /dev/null +++ b/tests/debuginfo/skip_second_statement_collapse.rs @@ -0,0 +1,170 @@ +// ignore-lldb +#![feature(collapse_debuginfo)] + +// Test that statement, skipped/added/reordered by macros, is correctly processed in debuginfo +// Performed step-over and step-into debug stepping through call statements. +// collapse_debuginfo feature enabled. + +// compile-flags:-g + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem1_call1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem1_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_rem2_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1[...] +// gdb-command:next 2 +// gdb-check:[...]#loc_rem2_call3[...] +// gdb-command:step 2 +// gdb-command:frame +// gdb-check:[...]#loc_call3_println[...] +// gdb-command:next 3 +// gdb-command:frame +// gdb-check:[...]#loc_after_rem[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add1_call1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add_macro[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add1_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_add2_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1[...] +// gdb-command:next 2 +// gdb-check:[...]#loc_add_macro[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call2[...] +// gdb-command:next 2 +// gdb-command:frame +// gdb-check:[...]#loc_add2_call3[...] +// gdb-command:step 2 +// gdb-command:frame +// gdb-check:[...]#loc_call3_println[...] +// gdb-command:next 3 +// gdb-command:frame +// gdb-check:[...]#loc_reorder1_call2[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder1_call3[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder1_call1[...] +// gdb-command:next +// gdb-command:frame +// gdb-check:[...]#loc_reorder2_call2[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call2[...] +// gdb-command:next 2 +// gdb-command:frame +// gdb-check:[...]#loc_reorder2_call3[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call3[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call3_println[...] +// gdb-command:next 3 +// gdb-command:frame +// gdb-check:[...]#loc_reorder2_call1[...] +// gdb-command:step +// gdb-command:frame +// gdb-check:[...]#loc_call1[...] +// gdb-command:next 2 +// gdb-command:continue + +#[inline(never)] +fn myprintln_impl(text: &str) { + println!("{}", text) +} + +#[collapse_debuginfo] +macro_rules! myprintln { + ($($arg:tt)*) => {{ + myprintln_impl($($arg)*); + }}; +} + +// Macro accepts 3 statements and removes the 2nd statement +macro_rules! remove_second_statement { + ($s1:stmt; $s2:stmt; $s3:stmt;) => { $s1 $s3 } +} + +macro_rules! add_second_statement { + ($s1:stmt; $s3:stmt;) => { + $s1 + call2(); // #loc_add_macro + $s3 + } +} + +macro_rules! reorder_statements { + ($s1:stmt; $s2:stmt; $s3:stmt;) => { $s2 $s3 $s1 } +} + +fn call1() { + myprintln!("one"); // #loc_call1 +} + +fn call2() { + myprintln!("two"); // #loc_call2 +} + +fn call3() { + (||{ + myprintln!("three") // #loc_call3_println + })(); // #loc_call3 +} + +fn main() { + let ret = 0; // #break, step should go to call1 + remove_second_statement! { // #loc_rem1_hdr + call1(); // #loc_rem1_call1, breakpoint should set to call1, step should go call3 + call2(); // #loc_rem1_call2, breakpoint should set to call3 + call3(); // #loc_rem1_call3 + } + remove_second_statement! { // #loc_rem2_hdr + call1(); // #loc_rem2_call1, breakpoint should set to call1, step should go call3 + call2(); // #loc_rem2_call2, breakpoint should set to call3 + call3(); // #loc_rem2_call3, breakpoint should set to call3 + } + myprintln!("After remove_second_statement test"); // #loc_after_rem + + add_second_statement! { // #loc_add1_hdr + call1(); // #loc_add1_call1 + call3(); // #loc_add1_call3 + } + add_second_statement! { // #loc_add2_hdr + call1(); // #loc_add2_call1 + call3(); // #loc_add2_call3 + } + + reorder_statements! { // #loc_reorder1_hdr + call1(); // #loc_reorder1_call1 + call2(); // #loc_reorder1_call2 + call3(); // #loc_reorder1_call3 + } + reorder_statements! { // #loc_reorder2_hdr + call1(); // #loc_reorder2_call1 + call2(); // #loc_reorder2_call2 + call3(); // #loc_reorder2_call3 + } + + std::process::exit(ret); // #loc_exit +} diff --git a/tests/run-make/raw-dylib-c/Makefile b/tests/run-make/raw-dylib-c/Makefile index 06e7935c02646..af5c4a6edd7ba 100644 --- a/tests/run-make/raw-dylib-c/Makefile +++ b/tests/run-make/raw-dylib-c/Makefile @@ -17,7 +17,7 @@ else $(CC) "$(TMPDIR)"/extern_1.obj -shared -o "$(TMPDIR)"/extern_1.dll $(CC) "$(TMPDIR)"/extern_2.obj -shared -o "$(TMPDIR)"/extern_2.dll endif - "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt + "$(TMPDIR)"/driver | tr -d '\r' > "$(TMPDIR)"/output.txt "$(TMPDIR)"/raw_dylib_test_bin > "$(TMPDIR)"/output_bin.txt ifdef RUSTC_BLESS_TEST diff --git a/tests/run-make/raw-dylib-inline-cross-dylib/Makefile b/tests/run-make/raw-dylib-inline-cross-dylib/Makefile index 195b5fda56fd3..6b44b40e253e2 100644 --- a/tests/run-make/raw-dylib-inline-cross-dylib/Makefile +++ b/tests/run-make/raw-dylib-inline-cross-dylib/Makefile @@ -26,5 +26,5 @@ else $(CC) "$(TMPDIR)"/extern_1.obj -shared -o "$(TMPDIR)"/extern_1.dll $(CC) "$(TMPDIR)"/extern_2.obj -shared -o "$(TMPDIR)"/extern_2.dll endif - $(call RUN,driver) > "$(TMPDIR)"/output.txt + $(call RUN,driver) | tr -d '\r' > "$(TMPDIR)"/output.txt $(RUSTC_TEST_OP) "$(TMPDIR)"/output.txt output.txt diff --git a/tests/run-make/raw-dylib-link-ordinal/Makefile b/tests/run-make/raw-dylib-link-ordinal/Makefile index 49e959cdb5b8e..3cf1300c243a4 100644 --- a/tests/run-make/raw-dylib-link-ordinal/Makefile +++ b/tests/run-make/raw-dylib-link-ordinal/Makefile @@ -13,5 +13,5 @@ ifdef IS_MSVC else $(CC) "$(TMPDIR)"/exporter.obj exporter.def -shared -o "$(TMPDIR)"/exporter.dll endif - "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt + "$(TMPDIR)"/driver | tr -d '\r' > "$(TMPDIR)"/output.txt $(RUSTC_TEST_OP) "$(TMPDIR)"/output.txt output.txt diff --git a/tests/ui/consts/const-eval/ub-enum.64bit.stderr b/tests/ui/consts/const-eval/ub-enum.64bit.stderr deleted file mode 100644 index 6db43d379d153..0000000000000 --- a/tests/ui/consts/const-eval/ub-enum.64bit.stderr +++ /dev/null @@ -1,134 +0,0 @@ -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:27:1 - | -LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; - | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x0000000000000001, but expected a valid enum tag - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:30:1 - | -LL | const BAD_ENUM_PTR: Enum = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer - | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:33:1 - | -LL | const BAD_ENUM_WRAPPED: Wrap = unsafe { mem::transmute(&1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer - | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:45:1 - | -LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x0000000000000000, but expected a valid enum tag - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:47:1 - | -LL | const BAD_ENUM2_PTR: Enum2 = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer - | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:50:1 - | -LL | const BAD_ENUM2_WRAPPED: Wrap = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer - | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:59:42 - | -LL | const BAD_ENUM2_UNDEF : Enum2 = unsafe { MaybeUninit { uninit: () }.init }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:64:1 - | -LL | const BAD_ENUM2_OPTION_PTR: Option = unsafe { mem::transmute(&0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer - | - = help: this code performed an operation that depends on the underlying bytes representing a pointer - = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:81:1 - | -LL | const BAD_UNINHABITED_VARIANT1: UninhDiscriminant = unsafe { mem::transmute(1u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:83:1 - | -LL | const BAD_UNINHABITED_VARIANT2: UninhDiscriminant = unsafe { mem::transmute(3u8) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } - -error[E0080]: it is undefined behavior to use this value - --> $DIR/ub-enum.rs:91:1 - | -LL | const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute(!0u32) })); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at ..0.1: encountered 0xffffffff, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:96:77 - | -LL | const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant - -error[E0080]: evaluation of constant value failed - --> $DIR/ub-enum.rs:98:77 - | -LL | const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) }; - | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant - -error[E0080]: evaluation of constant value failed - --> $SRC_DIR/core/src/mem/mod.rs:LL:COL - | - = note: read discriminant of an uninhabited enum variant - | -note: inside `discriminant::` - --> $SRC_DIR/core/src/mem/mod.rs:LL:COL -note: inside `TEST_ICE_89765` - --> $DIR/ub-enum.rs:103:14 - | -LL | unsafe { std::mem::discriminant(&*(&() as *const () as *const Never)); }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 14 previous errors - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/ub-enum.rs b/tests/ui/consts/const-eval/ub-enum.rs index 72a0c9efed2cc..c11ace612f13e 100644 --- a/tests/ui/consts/const-eval/ub-enum.rs +++ b/tests/ui/consts/const-eval/ub-enum.rs @@ -1,7 +1,7 @@ -// stderr-per-bitwidth // Strip out raw byte dumps to make comparison platform-independent: // normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" -// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*a(lloc)?[0-9]+(\+[a-z0-9]+)?─*╼ )+ *│.*" -> "HEX_DUMP" +// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" +// normalize-stderr-test "0x0+" -> "0x0" #![feature(never_type)] #![allow(invalid_value)] diff --git a/tests/ui/consts/const-eval/ub-enum.32bit.stderr b/tests/ui/consts/const-eval/ub-enum.stderr similarity index 98% rename from tests/ui/consts/const-eval/ub-enum.32bit.stderr rename to tests/ui/consts/const-eval/ub-enum.stderr index c0ad6caecf2ba..a0712f64c7bcc 100644 --- a/tests/ui/consts/const-eval/ub-enum.32bit.stderr +++ b/tests/ui/consts/const-eval/ub-enum.stderr @@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:27:1 | LL | const BAD_ENUM: Enum = unsafe { mem::transmute(1usize) }; - | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x00000001, but expected a valid enum tag + | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x01, but expected a valid enum tag | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { @@ -31,7 +31,7 @@ error[E0080]: it is undefined behavior to use this value --> $DIR/ub-enum.rs:45:1 | LL | const BAD_ENUM2: Enum2 = unsafe { mem::transmute(0usize) }; - | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x00000000, but expected a valid enum tag + | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered 0x0, but expected a valid enum tag | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { diff --git a/tests/ui/consts/const-eval/ub-uninhabit.rs b/tests/ui/consts/const-eval/ub-uninhabit.rs index 01600f545ae03..0eb9ab415d793 100644 --- a/tests/ui/consts/const-eval/ub-uninhabit.rs +++ b/tests/ui/consts/const-eval/ub-uninhabit.rs @@ -1,6 +1,6 @@ // Strip out raw byte dumps to make comparison platform-independent: // normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" -// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*a(lloc)?[0-9]+(\+[a-z0-9]+)?─*╼ )+ *│.*" -> "HEX_DUMP" +// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" #![feature(core_intrinsics)] #![feature(never_type)] diff --git a/tests/ui/consts/validate_never_arrays.rs b/tests/ui/consts/validate_never_arrays.rs index f96ca6839265e..71c1340e5f819 100644 --- a/tests/ui/consts/validate_never_arrays.rs +++ b/tests/ui/consts/validate_never_arrays.rs @@ -1,6 +1,6 @@ // Strip out raw byte dumps to make comparison platform-independent: // normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" -// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*a(lloc)?[0-9]+(\+[a-z0-9]+)?─*╼ )+ *│.*" -> "HEX_DUMP" +// normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼ )+ *│.*" -> "HEX_DUMP" #![feature(never_type)] const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; //~ ERROR undefined behavior diff --git a/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs new file mode 100644 index 0000000000000..8e15b4da35a49 --- /dev/null +++ b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.rs @@ -0,0 +1,21 @@ +// edition: 2021 + +#![deny(let_underscore_drop)] +fn main() { + let _ = foo(); //~ ERROR non-binding let on a type that implements `Drop` +} + +async fn from_config(_: Config) {} + +async fn foo() { + from_config(Config { + nickname: None, + ..Default::default() + }) + .await; +} + +#[derive(Default)] +struct Config { + nickname: Option>, +} diff --git a/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr new file mode 100644 index 0000000000000..86e521580b87a --- /dev/null +++ b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr @@ -0,0 +1,22 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/issue-119696-err-on-fn.rs:5:5 + | +LL | let _ = foo(); + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/issue-119696-err-on-fn.rs:3:9 + | +LL | #![deny(let_underscore_drop)] + | ^^^^^^^^^^^^^^^^^^^ +help: consider binding to an unused variable to avoid immediately dropping the value + | +LL | let _unused = foo(); + | ~~~~~~~ +help: consider immediately dropping the value + | +LL | drop(foo()); + | ~~~~~ + + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/let_underscore/issue-119697-extra-let.rs b/tests/ui/lint/let_underscore/issue-119697-extra-let.rs new file mode 100644 index 0000000000000..1dc80a123f613 --- /dev/null +++ b/tests/ui/lint/let_underscore/issue-119697-extra-let.rs @@ -0,0 +1,21 @@ +#![deny(let_underscore_drop)] +#![feature(type_alias_impl_trait)] + +pub struct Foo { + /// This type must have nontrivial drop glue + field: String, +} + +pub type Tait = impl Sized; + +pub fn ice_cold(beverage: Tait) { + // Must destructure at least one field of `Foo` + let Foo { field } = beverage; + // boom + _ = field; //~ ERROR non-binding let on a type that implements `Drop` + + let _ = field; //~ ERROR non-binding let on a type that implements `Drop` +} + + +pub fn main() {} diff --git a/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr b/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr new file mode 100644 index 0000000000000..16df2c720eaf2 --- /dev/null +++ b/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr @@ -0,0 +1,37 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/issue-119697-extra-let.rs:15:5 + | +LL | _ = field; + | ^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/issue-119697-extra-let.rs:1:9 + | +LL | #![deny(let_underscore_drop)] + | ^^^^^^^^^^^^^^^^^^^ +help: consider binding to an unused variable to avoid immediately dropping the value + | +LL | let _unused = field; + | ~~~~~~~~~~~ +help: consider immediately dropping the value + | +LL | drop(field); + | ~~~~~ + + +error: non-binding let on a type that implements `Drop` + --> $DIR/issue-119697-extra-let.rs:17:5 + | +LL | let _ = field; + | ^^^^^^^^^^^^^^ + | +help: consider binding to an unused variable to avoid immediately dropping the value + | +LL | let _unused = field; + | ~~~~~~~ +help: consider immediately dropping the value + | +LL | drop(field); + | ~~~~~ + + +error: aborting due to 2 previous errors +