diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index b118c0eaed4f3..6d44feec2c496 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -347,8 +347,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::ExprKind<'hir> { macro_rules! make_if { ($opt:expr) => {{ + let cond = self.lower_expr(cond); let then_expr = self.lower_block_expr(then); - hir::ExprKind::If(self.lower_expr(cond), self.arena.alloc(then_expr), $opt) + hir::ExprKind::If(cond, self.arena.alloc(then_expr), $opt) }}; } if let Some(rslt) = else_opt { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 6514de2b81315..474ec2b589b71 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -665,6 +665,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { // involved, so we only emit errors where there are no other parsing errors. gate_all!(destructuring_assignment, "destructuring assignments are unstable"); } + gate_all!(pub_macro_rules, "`pub` on `macro_rules` items is unstable"); // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 2d0009c225c59..3f484ab568652 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -638,6 +638,9 @@ declare_features! ( /// Allows macro attributes to observe output of `#[derive]`. (active, macro_attributes_in_derive_output, "1.51.0", Some(81119), None), + /// Allows `pub` on `macro_rules` items. + (active, pub_macro_rules, "1.52.0", Some(78855), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index e98297b692c92..bfe37ce6959e7 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -69,23 +69,65 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc let (span, panic) = panic_call(cx, f); - cx.struct_span_lint(NON_FMT_PANIC, arg.span, |lint| { + // Find the span of the argument to `panic!()`, before expansion in the + // case of `panic!(some_macro!())`. + // We don't use source_callsite(), because this `panic!(..)` might itself + // be expanded from another macro, in which case we want to stop at that + // expansion. + let mut arg_span = arg.span; + let mut arg_macro = None; + while !span.contains(arg_span) { + let expn = arg_span.ctxt().outer_expn_data(); + if expn.is_root() { + break; + } + arg_macro = expn.macro_def_id; + arg_span = expn.call_site; + } + + cx.struct_span_lint(NON_FMT_PANIC, arg_span, |lint| { let mut l = lint.build("panic message is not a string literal"); l.note("this is no longer accepted in Rust 2021"); - if span.contains(arg.span) { + if !span.contains(arg_span) { + // No clue where this argument is coming from. + l.emit(); + return; + } + if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) { + // A case of `panic!(format!(..))`. + l.note("the panic!() macro supports formatting, so there's no need for the format!() macro here"); + if let Some((open, close, _)) = find_delimiters(cx, arg_span) { + l.multipart_suggestion( + "remove the `format!(..)` macro call", + vec![ + (arg_span.until(open.shrink_to_hi()), "".into()), + (close.until(arg_span.shrink_to_hi()), "".into()), + ], + Applicability::MachineApplicable, + ); + } + } else { l.span_suggestion_verbose( - arg.span.shrink_to_lo(), + arg_span.shrink_to_lo(), "add a \"{}\" format string to Display the message", "\"{}\", ".into(), Applicability::MaybeIncorrect, ); if panic == sym::std_panic_macro { - l.span_suggestion_verbose( - span.until(arg.span), - "or use std::panic::panic_any instead", - "std::panic::panic_any(".into(), - Applicability::MachineApplicable, - ); + if let Some((open, close, del)) = find_delimiters(cx, span) { + l.multipart_suggestion( + "or use std::panic::panic_any instead", + if del == '(' { + vec![(span.until(open), "std::panic::panic_any".into())] + } else { + vec![ + (span.until(open.shrink_to_hi()), "std::panic::panic_any(".into()), + (close, ")".into()), + ] + }, + Applicability::MachineApplicable, + ); + } } } l.emit(); @@ -175,6 +217,19 @@ fn check_panic_str<'tcx>( } } +/// Given the span of `some_macro!(args);`, gives the span of `(` and `)`, +/// and the type of (opening) delimiter used. +fn find_delimiters<'tcx>(cx: &LateContext<'tcx>, span: Span) -> Option<(Span, Span, char)> { + let snippet = cx.sess().parse_sess.source_map().span_to_snippet(span).ok()?; + let (open, open_ch) = snippet.char_indices().find(|&(_, c)| "([{".contains(c))?; + let close = snippet.rfind(|c| ")]}".contains(c))?; + Some(( + span.from_inner(InnerSpan { start: open, end: open + 1 }), + span.from_inner(InnerSpan { start: close, end: close + 1 }), + open_ch, + )) +} + fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol) { let mut expn = f.span.ctxt().outer_expn_data(); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index cd16a88e5fc3c..b0b58a8d00367 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -8,11 +8,10 @@ use rustc_index::vec::Idx; use rustc_middle::mir::{ self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, - ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm, + ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; -use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; -use rustc_span::source_map::DesugaringKind; -use rustc_span::Span; +use rustc_middle::ty::{self, suggest_constraining_type_param, Instance, Ty}; +use rustc_span::{source_map::DesugaringKind, symbol::sym, Span}; use crate::dataflow::drop_flag_effects; use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex}; @@ -1543,9 +1542,43 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None, ); + self.explain_deref_coercion(loan, &mut err); + err.buffer(&mut self.errors_buffer); } + fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) { + let tcx = self.infcx.tcx; + if let ( + Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }), + Some((method_did, method_substs)), + ) = ( + &self.body[loan.reserve_location.block].terminator, + crate::util::find_self_call( + tcx, + self.body, + loan.assigned_place.local, + loan.reserve_location.block, + ), + ) { + if tcx.is_diagnostic_item(sym::deref_method, method_did) { + let deref_target = + tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { + Instance::resolve(tcx, self.param_env, deref_target, method_substs) + .transpose() + }); + if let Some(Ok(instance)) = deref_target { + let deref_target_ty = instance.ty(tcx, self.param_env); + err.note(&format!( + "borrow occurs due to deref coercion to `{}`", + deref_target_ty + )); + err.span_note(tcx.def_span(instance.def_id()), "deref defined here"); + } + } + } + } + /// Reports an illegal reassignment; for example, an assignment to /// (part of) a non-`mut` local that occurs potentially after that /// local has already been initialized. `place` is the path being diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs index 37498e50c0eb1..92c7a358c0a41 100644 --- a/compiler/rustc_mir/src/util/graphviz.rs +++ b/compiler/rustc_mir/src/util/graphviz.rs @@ -2,7 +2,7 @@ use gsgdt::GraphvizSettings; use rustc_graphviz as dot; use rustc_hir::def_id::DefId; use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt}; use std::fmt::Debug; use std::io::{self, Write}; @@ -16,14 +16,27 @@ where { let def_ids = dump_mir_def_ids(tcx, single); - let use_subgraphs = def_ids.len() > 1; + let mirs = + def_ids + .iter() + .flat_map(|def_id| { + if tcx.is_const_fn_raw(*def_id) { + vec![tcx.optimized_mir(*def_id), tcx.mir_for_ctfe(*def_id)] + } else { + vec![tcx.instance_mir(ty::InstanceDef::Item(ty::WithOptConstParam::unknown( + *def_id, + )))] + } + }) + .collect::>(); + + let use_subgraphs = mirs.len() > 1; if use_subgraphs { writeln!(w, "digraph __crate__ {{")?; } - for def_id in def_ids { - let body = &tcx.optimized_mir(def_id); - write_mir_fn_graphviz(tcx, body, use_subgraphs, w)?; + for mir in mirs { + write_mir_fn_graphviz(tcx, mir, use_subgraphs, w)?; } if use_subgraphs { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index f3f5fc9af64fe..073a2d8bd5120 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1475,15 +1475,7 @@ impl<'a> Parser<'a> { let vstr = pprust::vis_to_string(vis); let vstr = vstr.trim_end(); if macro_rules { - let msg = format!("can't qualify macro_rules invocation with `{}`", vstr); - self.struct_span_err(vis.span, &msg) - .span_suggestion( - vis.span, - "try exporting the macro", - "#[macro_export]".to_owned(), - Applicability::MaybeIncorrect, // speculative - ) - .emit(); + self.sess.gated_spans.gate(sym::pub_macro_rules, vis.span); } else { self.struct_span_err(vis.span, "can't qualify macro invocation with `pub`") .span_suggestion( diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index d51b501f7ae3d..62a95aa57c29f 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -37,15 +37,6 @@ fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { ) } -fn base_expr<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { - loop { - match expr.kind { - hir::ExprKind::Field(base, ..) => expr = base, - _ => return expr, - } - } -} - struct MarkSymbolVisitor<'tcx> { worklist: Vec, tcx: TyCtxt<'tcx>, @@ -143,6 +134,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } } + fn handle_assign(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if self + .typeck_results() + .expr_adjustments(expr) + .iter() + .any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_))) + { + self.visit_expr(expr); + } else if let hir::ExprKind::Field(base, ..) = expr.kind { + // Ignore write to field + self.handle_assign(base); + } else { + self.visit_expr(expr); + } + } + fn handle_field_pattern_match( &mut self, lhs: &hir::Pat<'_>, @@ -272,8 +279,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.lookup_and_handle_method(expr.hir_id); } hir::ExprKind::Assign(ref left, ref right, ..) => { - // Ignore write to field - self.visit_expr(base_expr(left)); + self.handle_assign(left); self.visit_expr(right); return; } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index fac28281593c8..a2b6dd17ad9ba 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -114,6 +114,11 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { self.visit_impl_item(nested_impl_item) } + fn visit_nested_foreign_item(&mut self, id: hir::ForeignItemId) { + let nested_foreign_item = self.krate.unwrap().foreign_item(id); + self.visit_foreign_item(nested_foreign_item); + } + fn visit_nested_body(&mut self, body_id: hir::BodyId) { let nested_body = self.krate.unwrap().body(body_id); self.visit_body(nested_body) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 79ed0b5308dab..65e5b0dddea30 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1230,13 +1230,13 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { }; let res = Res::Def(DefKind::Macro(ext.macro_kind()), def_id.to_def_id()); + let is_macro_export = self.r.session.contains_name(&item.attrs, sym::macro_export); self.r.macro_map.insert(def_id.to_def_id(), ext); self.r.local_macro_def_scopes.insert(def_id, parent_scope.module); - if macro_rules { + if macro_rules && matches!(item.vis.kind, ast::VisibilityKind::Inherited) { let ident = ident.normalize_to_macros_2_0(); self.r.macro_names.insert(ident); - let is_macro_export = self.r.session.contains_name(&item.attrs, sym::macro_export); let vis = if is_macro_export { ty::Visibility::Public } else { @@ -1261,6 +1261,11 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { }), )) } else { + if is_macro_export { + let what = if macro_rules { "`macro_rules` with `pub`" } else { "`macro` items" }; + let msg = format!("`#[macro_export]` cannot be used on {what}"); + self.r.session.span_err(item.span, &msg); + } let module = parent_scope.module; let vis = match item.kind { // Visibilities must not be resolved non-speculatively twice diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4ed0262bf2cfe..b112402ffe371 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -560,6 +560,7 @@ symbols! { format_args, format_args_capture, format_args_nl, + format_macro, freeze, freg, frem_fast, @@ -880,6 +881,7 @@ symbols! { ptr_guaranteed_eq, ptr_guaranteed_ne, ptr_offset_from, + pub_macro_rules, pub_restricted, pure, pushpop_unsafe, diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index a64a8b32ad77f..6a64587a2237f 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -107,6 +107,7 @@ macro_rules! vec { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "format_macro")] macro_rules! format { ($($arg:tt)*) => {{ let res = $crate::fmt::format($crate::__export::format_args!($($arg)*)); diff --git a/library/core/tests/atomic.rs b/library/core/tests/atomic.rs index b735957666fc5..539982eb0e49f 100644 --- a/library/core/tests/atomic.rs +++ b/library/core/tests/atomic.rs @@ -61,6 +61,7 @@ fn uint_xor() { #[test] #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins +#[cfg_attr(miri, ignore)] // FIXME: Miri does not support atomic_min fn uint_min() { let x = AtomicUsize::new(0xf731); assert_eq!(x.fetch_min(0x137f, SeqCst), 0xf731); @@ -71,6 +72,7 @@ fn uint_min() { #[test] #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins +#[cfg_attr(miri, ignore)] // FIXME: Miri does not support atomic_max fn uint_max() { let x = AtomicUsize::new(0x137f); assert_eq!(x.fetch_max(0xf731, SeqCst), 0x137f); @@ -109,6 +111,7 @@ fn int_xor() { #[test] #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins +#[cfg_attr(miri, ignore)] // FIXME: Miri does not support atomic_min fn int_min() { let x = AtomicIsize::new(0xf731); assert_eq!(x.fetch_min(0x137f, SeqCst), 0xf731); @@ -119,6 +122,7 @@ fn int_min() { #[test] #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins +#[cfg_attr(miri, ignore)] // FIXME: Miri does not support atomic_max fn int_max() { let x = AtomicIsize::new(0x137f); assert_eq!(x.fetch_max(0xf731, SeqCst), 0x137f); diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 711b0298565d7..a24cb0a0f93a1 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -41,7 +41,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { ) -> Option { let tcx = self.cx.tcx; let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, &[]) }; - if !self.cx.generated_synthetics.borrow_mut().insert((ty, trait_def_id)) { + if !self.cx.generated_synthetics.insert((ty, trait_def_id)) { debug!("get_auto_trait_impl_for({:?}): already generated, aborting", trait_ref); return None; } diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index a9d19a725c44f..94b82037e75e9 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -22,8 +22,8 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { debug!("get_blanket_impls({:?})", ty); let mut impls = Vec::new(); for &trait_def_id in self.cx.tcx.all_traits(LOCAL_CRATE).iter() { - if !self.cx.renderinfo.borrow().access_levels.is_public(trait_def_id) - || self.cx.generated_synthetics.borrow_mut().get(&(ty, trait_def_id)).is_some() + if !self.cx.renderinfo.access_levels.is_public(trait_def_id) + || self.cx.generated_synthetics.get(&(ty, trait_def_id)).is_some() { continue; } @@ -94,7 +94,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { return; } - self.cx.generated_synthetics.borrow_mut().insert((ty, trait_def_id)); + self.cx.generated_synthetics.insert((ty, trait_def_id)); let provided_trait_methods = self .cx .tcx diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index fded0499ba6a8..ea75d1614bd80 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -122,7 +122,7 @@ crate fn try_inline( let target_attrs = load_attrs(cx, did); let attrs = box merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone); - cx.renderinfo.borrow_mut().inlined.insert(did); + cx.renderinfo.inlined.insert(did); let what_rustc_thinks = clean::Item::from_def_id_and_parts(did, Some(name), kind, cx); ret.push(clean::Item { attrs, ..what_rustc_thinks }); Some(ret) @@ -156,7 +156,7 @@ crate fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> { /// /// These names are used later on by HTML rendering to generate things like /// source links back to the original item. -crate fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKind) { +crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: clean::TypeKind) { let crate_name = cx.tcx.crate_name(did.krate).to_string(); let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| { @@ -181,9 +181,9 @@ crate fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKin }; if did.is_local() { - cx.renderinfo.borrow_mut().exact_paths.insert(did, fqn); + cx.renderinfo.exact_paths.insert(did, fqn); } else { - cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind)); + cx.renderinfo.external_paths.insert(did, (fqn, kind)); } } @@ -317,7 +317,7 @@ crate fn build_impl( attrs: Option>, ret: &mut Vec, ) { - if !cx.renderinfo.borrow_mut().inlined.insert(did) { + if !cx.renderinfo.inlined.insert(did) { return; } @@ -329,7 +329,7 @@ crate fn build_impl( if !did.is_local() { if let Some(traitref) = associated_trait { let did = traitref.def_id; - if !cx.renderinfo.borrow().access_levels.is_public(did) { + if !cx.renderinfo.access_levels.is_public(did) { return; } @@ -361,7 +361,7 @@ crate fn build_impl( // reachable in rustdoc generated documentation if !did.is_local() { if let Some(did) = for_.def_id() { - if !cx.renderinfo.borrow().access_levels.is_public(did) { + if !cx.renderinfo.access_levels.is_public(did) { return; } @@ -613,20 +613,19 @@ crate fn record_extern_trait(cx: &mut DocContext<'_>, did: DefId) { } { - if cx.external_traits.borrow().contains_key(&did) - || cx.active_extern_traits.borrow().contains(&did) + if cx.external_traits.borrow().contains_key(&did) || cx.active_extern_traits.contains(&did) { return; } } { - cx.active_extern_traits.borrow_mut().insert(did); + cx.active_extern_traits.insert(did); } debug!("record_extern_trait: {:?}", did); let trait_ = build_external_trait(cx, did); cx.external_traits.borrow_mut().insert(did, trait_); - cx.active_extern_traits.borrow_mut().remove(&did); + cx.active_extern_traits.remove(&did); } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 98e1299df2f26..b6e7046210596 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -357,7 +357,7 @@ impl Clean for hir::Lifetime { | rl::Region::LateBound(_, node_id, _) | rl::Region::Free(_, node_id), ) => { - if let Some(lt) = cx.lt_substs.borrow().get(&node_id).cloned() { + if let Some(lt) = cx.lt_substs.get(&node_id).cloned() { return lt; } } @@ -644,7 +644,7 @@ impl Clean for hir::Generics<'_> { match param.kind { GenericParamDefKind::Lifetime => unreachable!(), GenericParamDefKind::Type { did, ref bounds, .. } => { - cx.impl_trait_bounds.borrow_mut().insert(did.into(), bounds.clone()); + cx.impl_trait_bounds.insert(did.into(), bounds.clone()); } GenericParamDefKind::Const { .. } => unreachable!(), } @@ -803,7 +803,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics, ty::GenericPredicates<'tcx unreachable!(); } - cx.impl_trait_bounds.borrow_mut().insert(param, bounds); + cx.impl_trait_bounds.insert(param, bounds); } // Now that `cx.impl_trait_bounds` is populated, we can process @@ -1291,10 +1291,10 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type { match qpath { hir::QPath::Resolved(None, ref path) => { if let Res::Def(DefKind::TyParam, did) = path.res { - if let Some(new_ty) = cx.ty_substs.borrow().get(&did).cloned() { + if let Some(new_ty) = cx.ty_substs.get(&did).cloned() { return new_ty; } - if let Some(bounds) = cx.impl_trait_bounds.borrow_mut().remove(&did.into()) { + if let Some(bounds) = cx.impl_trait_bounds.remove(&did.into()) { return ImplTrait(bounds); } } @@ -1304,7 +1304,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type { // Substitute private type aliases if let Some(def_id) = def_id.as_local() { let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id); - if !cx.renderinfo.borrow().access_levels.is_exported(def_id.to_def_id()) { + if !cx.renderinfo.access_levels.is_exported(def_id.to_def_id()) { alias = Some(&cx.tcx.hir().expect_item(hir_id).kind); } } @@ -1651,7 +1651,7 @@ impl<'tcx> Clean for Ty<'tcx> { ty::Projection(ref data) => data.clean(cx), ty::Param(ref p) => { - if let Some(bounds) = cx.impl_trait_bounds.borrow_mut().remove(&p.index.into()) { + if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) { ImplTrait(bounds) } else { Generic(p.name) diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index c7bfd363a129b..d2eee49f0c968 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -23,10 +23,9 @@ crate fn krate(mut cx: &mut DocContext<'_>) -> Crate { let krate = cx.tcx.hir().krate(); let module = crate::visit_ast::RustdocVisitor::new(&mut cx).visit(krate); - let mut r = cx.renderinfo.get_mut(); - r.deref_trait_did = cx.tcx.lang_items().deref_trait(); - r.deref_mut_trait_did = cx.tcx.lang_items().deref_mut_trait(); - r.owned_box_did = cx.tcx.lang_items().owned_box(); + cx.renderinfo.deref_trait_did = cx.tcx.lang_items().deref_trait(); + cx.renderinfo.deref_mut_trait_did = cx.tcx.lang_items().deref_mut_trait(); + cx.renderinfo.owned_box_did = cx.tcx.lang_items().owned_box(); let mut externs = Vec::new(); for &cnum in cx.tcx.crates().iter() { @@ -494,10 +493,10 @@ crate fn enter_impl_trait(cx: &mut DocContext<'_>, f: F) -> R where F: FnOnce(&mut DocContext<'_>) -> R, { - let old_bounds = mem::take(&mut *cx.impl_trait_bounds.get_mut()); + let old_bounds = mem::take(&mut cx.impl_trait_bounds); let r = f(cx); - assert!(cx.impl_trait_bounds.borrow().is_empty()); - *cx.impl_trait_bounds.get_mut() = old_bounds; + assert!(cx.impl_trait_bounds.is_empty()); + cx.impl_trait_bounds = old_bounds; r } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index dbf202a732108..8fceb00eeae51 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -42,32 +42,37 @@ crate type ExternalPaths = FxHashMap, clean::TypeKind)>; crate struct DocContext<'tcx> { crate tcx: TyCtxt<'tcx>, + /// Name resolver. Used for intra-doc links. + /// + /// The `Rc>` wrapping is needed because that is what's returned by + /// [`Queries::expansion()`]. + // FIXME: see if we can get rid of this RefCell somehow crate resolver: Rc>, /// Used for normalization. /// /// Most of this logic is copied from rustc_lint::late. crate param_env: ParamEnv<'tcx>, /// Later on moved into `cache` - crate renderinfo: RefCell, + crate renderinfo: RenderInfo, /// Later on moved through `clean::Crate` into `cache` crate external_traits: Rc>>, /// Used while populating `external_traits` to ensure we don't process the same trait twice at /// the same time. - crate active_extern_traits: RefCell>, + crate active_extern_traits: FxHashSet, // The current set of type and lifetime substitutions, // for expanding type aliases at the HIR level: /// Table `DefId` of type parameter -> substituted type - crate ty_substs: RefCell>, + crate ty_substs: FxHashMap, /// Table `DefId` of lifetime parameter -> substituted lifetime - crate lt_substs: RefCell>, + crate lt_substs: FxHashMap, /// Table `DefId` of const parameter -> substituted const - crate ct_substs: RefCell>, + crate ct_substs: FxHashMap, /// Table synthetic type parameter for `impl Trait` in argument position -> bounds - crate impl_trait_bounds: RefCell>>, + crate impl_trait_bounds: FxHashMap>, crate fake_def_ids: FxHashMap, /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`. // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set. - crate generated_synthetics: RefCell, DefId)>>, + crate generated_synthetics: FxHashSet<(Ty<'tcx>, DefId)>, crate auto_traits: Vec, /// The options given to rustdoc that could be relevant to a pass. crate render_options: RenderOptions, @@ -112,14 +117,14 @@ impl<'tcx> DocContext<'tcx> { F: FnOnce(&mut Self) -> R, { let (old_tys, old_lts, old_cts) = ( - mem::replace(&mut *self.ty_substs.get_mut(), ty_substs), - mem::replace(&mut *self.lt_substs.get_mut(), lt_substs), - mem::replace(&mut *self.ct_substs.get_mut(), ct_substs), + mem::replace(&mut self.ty_substs, ty_substs), + mem::replace(&mut self.lt_substs, lt_substs), + mem::replace(&mut self.ct_substs, ct_substs), ); let r = f(self); - *self.ty_substs.get_mut() = old_tys; - *self.lt_substs.get_mut() = old_lts; - *self.ct_substs.get_mut() = old_cts; + self.ty_substs = old_tys; + self.lt_substs = old_lts; + self.ct_substs = old_cts; r } @@ -509,7 +514,7 @@ crate fn run_global_ctxt( param_env: ParamEnv::empty(), external_traits: Default::default(), active_extern_traits: Default::default(), - renderinfo: RefCell::new(renderinfo), + renderinfo, ty_substs: Default::default(), lt_substs: Default::default(), ct_substs: Default::default(), @@ -642,7 +647,7 @@ crate fn run_global_ctxt( // The main crate doc comments are always collapsed. krate.collapsed = true; - (krate, ctxt.renderinfo.into_inner(), ctxt.render_options) + (krate, ctxt.renderinfo, ctxt.render_options) } /// Due to , diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 542cf6d2c275a..c3365b844ecb8 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -127,7 +127,7 @@ impl<'a, 'b> CoverageCalculator<'a, 'b> { } fn print_results(&self) { - let output_format = self.ctx.renderinfo.borrow().output_format; + let output_format = self.ctx.renderinfo.output_format; if output_format.is_json() { println!("{}", self.to_json()); return; diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 0b6d81d1b447e..0271a5b78a7ef 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -48,11 +48,10 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate { if !cx.tcx.get_attrs(def_id).lists(sym::doc).has_word(sym::hidden) { let self_ty = cx.tcx.type_of(def_id); let impls = get_auto_trait_and_blanket_impls(cx, self_ty, def_id); - let mut renderinfo = cx.renderinfo.borrow_mut(); - new_items.extend(impls.filter(|i| renderinfo.inlined.insert(i.def_id))); + new_items.extend(impls.filter(|i| cx.renderinfo.inlined.insert(i.def_id))); } - }) + }); } } diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs index 042a895d2fa2f..e8e1bead84fb6 100644 --- a/src/librustdoc/passes/doc_test_lints.rs +++ b/src/librustdoc/passes/doc_test_lints.rs @@ -97,8 +97,7 @@ crate fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) { |lint| lint.build("missing code example in this documentation").emit(), ); } - } else if tests.found_tests > 0 && !cx.renderinfo.borrow().access_levels.is_public(item.def_id) - { + } else if tests.found_tests > 0 && !cx.renderinfo.access_levels.is_public(item.def_id) { cx.tcx.struct_span_lint_hir( lint::builtin::PRIVATE_DOC_TESTS, hir_id, diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index c0bb05af3edb5..f83eab6799ee6 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -17,7 +17,7 @@ crate const STRIP_PRIVATE: Pass = Pass { crate fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { // This stripper collects all *retained* nodes. let mut retained = DefIdSet::default(); - let access_levels = cx.renderinfo.borrow().access_levels.clone(); + let access_levels = cx.renderinfo.access_levels.clone(); // strip all private items { diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index e92ea55caa737..4d42c181d8cf2 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -113,7 +113,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { assert_eq!(cur_mod_def_id, macro_parent_def_id); cur_mod.macros.push((def, None)); } - self.cx.renderinfo.get_mut().exact_paths = self.exact_paths; + self.cx.renderinfo.exact_paths = self.exact_paths; top_level_module } @@ -199,12 +199,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } else { // All items need to be handled here in case someone wishes to link // to them with intra-doc links - self.cx - .renderinfo - .get_mut() - .access_levels - .map - .insert(did, AccessLevel::Public); + self.cx.renderinfo.access_levels.map.insert(did, AccessLevel::Public); } } } @@ -216,7 +211,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { None => return false, }; - let is_private = !self.cx.renderinfo.borrow().access_levels.is_public(res_did); + let is_private = !self.cx.renderinfo.access_levels.is_public(res_did); let is_hidden = inherits_doc_hidden(self.cx, res_hir_id); // Only inline if requested or if the item would otherwise be stripped. diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs index 0bf22562eaede..daed5bd107db1 100644 --- a/src/librustdoc/visit_lib.rs +++ b/src/librustdoc/visit_lib.rs @@ -25,7 +25,7 @@ impl<'a, 'tcx> LibEmbargoVisitor<'a, 'tcx> { crate fn new(cx: &'a mut crate::core::DocContext<'tcx>) -> LibEmbargoVisitor<'a, 'tcx> { LibEmbargoVisitor { tcx: cx.tcx, - access_levels: &mut cx.renderinfo.get_mut().access_levels, + access_levels: &mut cx.renderinfo.access_levels, prev_level: Some(AccessLevel::Public), visited_mods: FxHashSet::default(), } diff --git a/src/test/rustdoc-json/nested.rs b/src/test/rustdoc-json/nested.rs index 7e705255d983d..a3d4935f49614 100644 --- a/src/test/rustdoc-json/nested.rs +++ b/src/test/rustdoc-json/nested.rs @@ -1,24 +1,28 @@ // edition:2018 -// @has nested.json "$.index[*][?(@.name=='nested')].kind" \"module\" -// @has - "$.index[*][?(@.name=='nested')].inner.is_crate" true +// @is nested.json "$.index[*][?(@.name=='nested')].kind" \"module\" +// @is - "$.index[*][?(@.name=='nested')].inner.is_crate" true // @count - "$.index[*][?(@.name=='nested')].inner.items[*]" 1 -// @has nested.json "$.index[*][?(@.name=='l1')].kind" \"module\" -// @has - "$.index[*][?(@.name=='l1')].inner.is_crate" false +// @is nested.json "$.index[*][?(@.name=='l1')].kind" \"module\" +// @is - "$.index[*][?(@.name=='l1')].inner.is_crate" false // @count - "$.index[*][?(@.name=='l1')].inner.items[*]" 2 pub mod l1 { - // @has nested.json "$.index[*][?(@.name=='l3')].kind" \"module\" - // @has - "$.index[*][?(@.name=='l3')].inner.is_crate" false + // @is nested.json "$.index[*][?(@.name=='l3')].kind" \"module\" + // @is - "$.index[*][?(@.name=='l3')].inner.is_crate" false // @count - "$.index[*][?(@.name=='l3')].inner.items[*]" 1 + // @set l3_id = - "$.index[*][?(@.name=='l3')].id" + // @has - "$.index[*][?(@.name=='l1')].inner.items[*]" $l3_id pub mod l3 { - // @has nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\" - // @has - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\" + // @is nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\" + // @is - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\" + // @set l4_id = - "$.index[*][?(@.name=='L4')].id" + // @has - "$.index[*][?(@.name=='l3')].inner.items[*]" $l4_id pub struct L4; } - // @has nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\" - // @has - "$.index[*][?(@.inner.span=='l3::L4')].inner.glob" false + // @is nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\" + // @is - "$.index[*][?(@.inner.span=='l3::L4')].inner.glob" false pub use l3::L4; } diff --git a/src/test/ui/borrowck/issue-81365-1.rs b/src/test/ui/borrowck/issue-81365-1.rs new file mode 100644 index 0000000000000..8e212a7701647 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-1.rs @@ -0,0 +1,26 @@ +use std::ops::Deref; + +struct DerefTarget { + target_field: bool, +} +struct Container { + target: DerefTarget, + container_field: bool, +} + +impl Deref for Container { + type Target = DerefTarget; + fn deref(&self) -> &Self::Target { + &self.target + } +} + +impl Container { + fn bad_borrow(&mut self) { + let first = &self.target_field; + self.container_field = true; //~ ERROR E0506 + first; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-1.stderr b/src/test/ui/borrowck/issue-81365-1.stderr new file mode 100644 index 0000000000000..ef88d7f14a39b --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-1.stderr @@ -0,0 +1,20 @@ +error[E0506]: cannot assign to `self.container_field` because it is borrowed + --> $DIR/issue-81365-1.rs:21:9 + | +LL | let first = &self.target_field; + | ---- borrow of `self.container_field` occurs here +LL | self.container_field = true; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here +LL | first; + | ----- borrow later used here + | + = note: borrow occurs due to deref coercion to `DerefTarget` +note: deref defined here + --> $DIR/issue-81365-1.rs:12:5 + | +LL | type Target = DerefTarget; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-10.rs b/src/test/ui/borrowck/issue-81365-10.rs new file mode 100644 index 0000000000000..7602e184a9f9d --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-10.rs @@ -0,0 +1,26 @@ +use std::ops::Deref; + +struct DerefTarget { + target_field: bool, +} +struct Container { + target: DerefTarget, + container_field: bool, +} + +impl Deref for Container { + type Target = DerefTarget; + fn deref(&self) -> &Self::Target { + &self.target + } +} + +impl Container { + fn bad_borrow(&mut self) { + let first = &self.deref().target_field; + self.container_field = true; //~ ERROR E0506 + first; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-10.stderr b/src/test/ui/borrowck/issue-81365-10.stderr new file mode 100644 index 0000000000000..891f70ed7f6b4 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-10.stderr @@ -0,0 +1,13 @@ +error[E0506]: cannot assign to `self.container_field` because it is borrowed + --> $DIR/issue-81365-10.rs:21:9 + | +LL | let first = &self.deref().target_field; + | ---- borrow of `self.container_field` occurs here +LL | self.container_field = true; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here +LL | first; + | ----- borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-11.rs b/src/test/ui/borrowck/issue-81365-11.rs new file mode 100644 index 0000000000000..6b558c65d3d61 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-11.rs @@ -0,0 +1,32 @@ +use std::ops::{Deref, DerefMut}; + +struct DerefTarget { + target_field: bool, +} +struct Container { + target: DerefTarget, + container_field: bool, +} + +impl Deref for Container { + type Target = DerefTarget; + fn deref(&self) -> &Self::Target { + &self.target + } +} + +impl DerefMut for Container { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.target + } +} + +impl Container { + fn bad_borrow(&mut self) { + let first = &mut self.target_field; + self.container_field = true; //~ ERROR E0506 + first; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-11.stderr b/src/test/ui/borrowck/issue-81365-11.stderr new file mode 100644 index 0000000000000..0770c136632db --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-11.stderr @@ -0,0 +1,13 @@ +error[E0506]: cannot assign to `self.container_field` because it is borrowed + --> $DIR/issue-81365-11.rs:27:9 + | +LL | let first = &mut self.target_field; + | ---- borrow of `self.container_field` occurs here +LL | self.container_field = true; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here +LL | first; + | ----- borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-2.rs b/src/test/ui/borrowck/issue-81365-2.rs new file mode 100644 index 0000000000000..fbbdd93b97cc8 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-2.rs @@ -0,0 +1,30 @@ +use std::ops::Deref; + +struct DerefTarget { + target_field: bool, +} +struct Container { + target: DerefTarget, + container_field: bool, +} + +impl Deref for Container { + type Target = DerefTarget; + fn deref(&self) -> &Self::Target { + &self.target + } +} + +struct Outer { + container: Container, +} + +impl Outer { + fn bad_borrow(&mut self) { + let first = &self.container.target_field; + self.container.container_field = true; //~ ERROR E0506 + first; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-2.stderr b/src/test/ui/borrowck/issue-81365-2.stderr new file mode 100644 index 0000000000000..e71edb509649f --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-2.stderr @@ -0,0 +1,20 @@ +error[E0506]: cannot assign to `self.container.container_field` because it is borrowed + --> $DIR/issue-81365-2.rs:25:9 + | +LL | let first = &self.container.target_field; + | -------------- borrow of `self.container.container_field` occurs here +LL | self.container.container_field = true; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container.container_field` occurs here +LL | first; + | ----- borrow later used here + | + = note: borrow occurs due to deref coercion to `DerefTarget` +note: deref defined here + --> $DIR/issue-81365-2.rs:12:5 + | +LL | type Target = DerefTarget; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-3.rs b/src/test/ui/borrowck/issue-81365-3.rs new file mode 100644 index 0000000000000..9a9e3a3135822 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-3.rs @@ -0,0 +1,37 @@ +use std::ops::Deref; + +struct DerefTarget { + target_field: bool, +} +struct Container { + target: DerefTarget, + container_field: bool, +} + +impl Deref for Container { + type Target = DerefTarget; + fn deref(&self) -> &Self::Target { + &self.target + } +} + +struct Outer { + container: Container, +} + +impl Deref for Outer { + type Target = Container; + fn deref(&self) -> &Self::Target { + &self.container + } +} + +impl Outer { + fn bad_borrow(&mut self) { + let first = &self.target_field; + self.container.container_field = true; //~ ERROR E0506 + first; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-3.stderr b/src/test/ui/borrowck/issue-81365-3.stderr new file mode 100644 index 0000000000000..70bb6bb93a94b --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-3.stderr @@ -0,0 +1,20 @@ +error[E0506]: cannot assign to `self.container.container_field` because it is borrowed + --> $DIR/issue-81365-3.rs:32:9 + | +LL | let first = &self.target_field; + | ---- borrow of `self.container.container_field` occurs here +LL | self.container.container_field = true; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container.container_field` occurs here +LL | first; + | ----- borrow later used here + | + = note: borrow occurs due to deref coercion to `Container` +note: deref defined here + --> $DIR/issue-81365-3.rs:23:5 + | +LL | type Target = Container; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-4.rs b/src/test/ui/borrowck/issue-81365-4.rs new file mode 100644 index 0000000000000..b2643eb3358ac --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-4.rs @@ -0,0 +1,38 @@ +use std::ops::Deref; + +struct DerefTarget { + target_field: bool, +} +struct Container { + target: DerefTarget, + container_field: bool, +} + +impl Deref for Container { + type Target = DerefTarget; + fn deref(&self) -> &Self::Target { + &self.target + } +} + +struct Outer { + container: Container, + outer_field: bool, +} + +impl Deref for Outer { + type Target = Container; + fn deref(&self) -> &Self::Target { + &self.container + } +} + +impl Outer { + fn bad_borrow(&mut self) { + let first = &self.target_field; + self.outer_field = true; //~ ERROR E0506 + first; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-4.stderr b/src/test/ui/borrowck/issue-81365-4.stderr new file mode 100644 index 0000000000000..e714bb86d1cd2 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-4.stderr @@ -0,0 +1,20 @@ +error[E0506]: cannot assign to `self.outer_field` because it is borrowed + --> $DIR/issue-81365-4.rs:33:9 + | +LL | let first = &self.target_field; + | ---- borrow of `self.outer_field` occurs here +LL | self.outer_field = true; + | ^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.outer_field` occurs here +LL | first; + | ----- borrow later used here + | + = note: borrow occurs due to deref coercion to `Container` +note: deref defined here + --> $DIR/issue-81365-4.rs:24:5 + | +LL | type Target = Container; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-5.rs b/src/test/ui/borrowck/issue-81365-5.rs new file mode 100644 index 0000000000000..d36b79615e3ad --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-5.rs @@ -0,0 +1,33 @@ +use std::ops::Deref; + +struct DerefTarget { + target_field: bool, +} + +impl DerefTarget { + fn get(&self) -> &bool { + &self.target_field + } +} + +struct Container { + target: DerefTarget, + container_field: bool, +} + +impl Deref for Container { + type Target = DerefTarget; + fn deref(&self) -> &Self::Target { + &self.target + } +} + +impl Container { + fn bad_borrow(&mut self) { + let first = self.get(); + self.container_field = true; //~ ERROR E0506 + first; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-5.stderr b/src/test/ui/borrowck/issue-81365-5.stderr new file mode 100644 index 0000000000000..7c0e9f43bd0b0 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-5.stderr @@ -0,0 +1,20 @@ +error[E0506]: cannot assign to `self.container_field` because it is borrowed + --> $DIR/issue-81365-5.rs:28:9 + | +LL | let first = self.get(); + | ---- borrow of `self.container_field` occurs here +LL | self.container_field = true; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here +LL | first; + | ----- borrow later used here + | + = note: borrow occurs due to deref coercion to `DerefTarget` +note: deref defined here + --> $DIR/issue-81365-5.rs:19:5 + | +LL | type Target = DerefTarget; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-6.rs b/src/test/ui/borrowck/issue-81365-6.rs new file mode 100644 index 0000000000000..85ea77756b3ba --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-6.rs @@ -0,0 +1,23 @@ +use std::ops::Deref; + +struct Container { + target: Vec<()>, + container_field: bool, +} + +impl Deref for Container { + type Target = [()]; + fn deref(&self) -> &Self::Target { + &self.target + } +} + +impl Container { + fn bad_borrow(&mut self) { + let first = &self[0]; + self.container_field = true; //~ ERROR E0506 + first; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-6.stderr b/src/test/ui/borrowck/issue-81365-6.stderr new file mode 100644 index 0000000000000..85ed6acca3d41 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-6.stderr @@ -0,0 +1,20 @@ +error[E0506]: cannot assign to `self.container_field` because it is borrowed + --> $DIR/issue-81365-6.rs:18:9 + | +LL | let first = &self[0]; + | ---- borrow of `self.container_field` occurs here +LL | self.container_field = true; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here +LL | first; + | ----- borrow later used here + | + = note: borrow occurs due to deref coercion to `[()]` +note: deref defined here + --> $DIR/issue-81365-6.rs:9:5 + | +LL | type Target = [()]; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-7.rs b/src/test/ui/borrowck/issue-81365-7.rs new file mode 100644 index 0000000000000..cbf70f11a9acf --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-7.rs @@ -0,0 +1,24 @@ +use std::ops::Deref; + +struct DerefTarget { + target_field: bool, +} +struct Container { + target: DerefTarget, + container_field: bool, +} + +impl Deref for Container { + type Target = DerefTarget; + fn deref(&self) -> &Self::Target { + &self.target + } +} + +fn bad_borrow(c: &mut Container) { + let first = &c.target_field; + c.container_field = true; //~ ERROR E0506 + first; +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-7.stderr b/src/test/ui/borrowck/issue-81365-7.stderr new file mode 100644 index 0000000000000..506732ec0c5b4 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-7.stderr @@ -0,0 +1,20 @@ +error[E0506]: cannot assign to `c.container_field` because it is borrowed + --> $DIR/issue-81365-7.rs:20:5 + | +LL | let first = &c.target_field; + | - borrow of `c.container_field` occurs here +LL | c.container_field = true; + | ^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `c.container_field` occurs here +LL | first; + | ----- borrow later used here + | + = note: borrow occurs due to deref coercion to `DerefTarget` +note: deref defined here + --> $DIR/issue-81365-7.rs:12:5 + | +LL | type Target = DerefTarget; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-8.rs b/src/test/ui/borrowck/issue-81365-8.rs new file mode 100644 index 0000000000000..0bb1033fb42f7 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-8.rs @@ -0,0 +1,26 @@ +use std::ops::Deref; + +struct DerefTarget { + target_field: bool, +} +struct Container { + target: DerefTarget, + container_field: bool, +} + +impl Deref for Container { + type Target = DerefTarget; + fn deref(&self) -> &Self::Target { + &self.target + } +} + +impl Container { + fn bad_borrow(&mut self) { + let first = &(*self).target_field; + self.container_field = true; //~ ERROR E0506 + first; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-8.stderr b/src/test/ui/borrowck/issue-81365-8.stderr new file mode 100644 index 0000000000000..716b6e9b51fd0 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-8.stderr @@ -0,0 +1,20 @@ +error[E0506]: cannot assign to `self.container_field` because it is borrowed + --> $DIR/issue-81365-8.rs:21:9 + | +LL | let first = &(*self).target_field; + | ------- borrow of `self.container_field` occurs here +LL | self.container_field = true; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here +LL | first; + | ----- borrow later used here + | + = note: borrow occurs due to deref coercion to `DerefTarget` +note: deref defined here + --> $DIR/issue-81365-8.rs:12:5 + | +LL | type Target = DerefTarget; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/borrowck/issue-81365-9.rs b/src/test/ui/borrowck/issue-81365-9.rs new file mode 100644 index 0000000000000..cd57afa288df7 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-9.rs @@ -0,0 +1,26 @@ +use std::ops::Deref; + +struct DerefTarget { + target_field: bool, +} +struct Container { + target: DerefTarget, + container_field: bool, +} + +impl Deref for Container { + type Target = DerefTarget; + fn deref(&self) -> &Self::Target { + &self.target + } +} + +impl Container { + fn bad_borrow(&mut self) { + let first = &Deref::deref(self).target_field; + self.container_field = true; //~ ERROR E0506 + first; + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-81365-9.stderr b/src/test/ui/borrowck/issue-81365-9.stderr new file mode 100644 index 0000000000000..c7d48214fd4a8 --- /dev/null +++ b/src/test/ui/borrowck/issue-81365-9.stderr @@ -0,0 +1,13 @@ +error[E0506]: cannot assign to `self.container_field` because it is borrowed + --> $DIR/issue-81365-9.rs:21:9 + | +LL | let first = &Deref::deref(self).target_field; + | ---- borrow of `self.container_field` occurs here +LL | self.container_field = true; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here +LL | first; + | ----- borrow later used here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/did_you_mean/pub-macro-rules.rs b/src/test/ui/did_you_mean/pub-macro-rules.rs deleted file mode 100644 index c5393703f7091..0000000000000 --- a/src/test/ui/did_you_mean/pub-macro-rules.rs +++ /dev/null @@ -1,16 +0,0 @@ -#[macro_use] mod bleh { - pub macro_rules! foo { //~ ERROR can't qualify macro_rules invocation - ($n:ident) => ( - fn $n () -> i32 { - 1 - } - ) - } - -} - -foo!(meh); - -fn main() { - println!("{}", meh()); -} diff --git a/src/test/ui/did_you_mean/pub-macro-rules.stderr b/src/test/ui/did_you_mean/pub-macro-rules.stderr deleted file mode 100644 index 0bde5783b8cc6..0000000000000 --- a/src/test/ui/did_you_mean/pub-macro-rules.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: can't qualify macro_rules invocation with `pub` - --> $DIR/pub-macro-rules.rs:2:5 - | -LL | pub macro_rules! foo { - | ^^^ help: try exporting the macro: `#[macro_export]` - -error: aborting due to previous error - diff --git a/src/test/ui/feature-gates/feature-gate-pub_macro_rules.rs b/src/test/ui/feature-gates/feature-gate-pub_macro_rules.rs new file mode 100644 index 0000000000000..5504ec317ae59 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-pub_macro_rules.rs @@ -0,0 +1,10 @@ +pub macro_rules! m1 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable + +#[cfg(FALSE)] +pub macro_rules! m2 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable + +pub(crate) macro_rules! m3 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable + +pub(in self) macro_rules! m4 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable + +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-pub_macro_rules.stderr b/src/test/ui/feature-gates/feature-gate-pub_macro_rules.stderr new file mode 100644 index 0000000000000..bfaec398d9a97 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-pub_macro_rules.stderr @@ -0,0 +1,39 @@ +error[E0658]: `pub` on `macro_rules` items is unstable + --> $DIR/feature-gate-pub_macro_rules.rs:1:1 + | +LL | pub macro_rules! m1 { () => {} } + | ^^^ + | + = note: see issue #78855 for more information + = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable + +error[E0658]: `pub` on `macro_rules` items is unstable + --> $DIR/feature-gate-pub_macro_rules.rs:4:1 + | +LL | pub macro_rules! m2 { () => {} } + | ^^^ + | + = note: see issue #78855 for more information + = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable + +error[E0658]: `pub` on `macro_rules` items is unstable + --> $DIR/feature-gate-pub_macro_rules.rs:6:1 + | +LL | pub(crate) macro_rules! m3 { () => {} } + | ^^^^^^^^^^ + | + = note: see issue #78855 for more information + = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable + +error[E0658]: `pub` on `macro_rules` items is unstable + --> $DIR/feature-gate-pub_macro_rules.rs:8:1 + | +LL | pub(in self) macro_rules! m4 { () => {} } + | ^^^^^^^^^^^^ + | + = note: see issue #78855 for more information + = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/issues/issue-81918.rs b/src/test/ui/issues/issue-81918.rs new file mode 100644 index 0000000000000..8938b8a6f2c52 --- /dev/null +++ b/src/test/ui/issues/issue-81918.rs @@ -0,0 +1,11 @@ +// check-pass +// dont-check-compiler-stdout +// compile-flags: -Z unpretty=mir-cfg + +// This checks that unpretty=mir-cfg does not panic. See #81918. + +const TAG: &'static str = "ABCD"; + +fn main() { + if TAG == "" {} +} diff --git a/src/test/ui/lint/dead-code/write-only-field.rs b/src/test/ui/lint/dead-code/write-only-field.rs index 78cfcfda8f971..7b3f1e9f5b6cb 100644 --- a/src/test/ui/lint/dead-code/write-only-field.rs +++ b/src/test/ui/lint/dead-code/write-only-field.rs @@ -17,4 +17,53 @@ fn field_write(s: &mut S) { fn main() { let mut s = S { f: 0, sub: Sub { f: 0 } }; field_write(&mut s); + + auto_deref(); + nested_boxes(); +} + +fn auto_deref() { + struct E { + x: bool, + y: bool, //~ ERROR: field is never read + } + + struct P<'a> { + e: &'a mut E + } + + impl P<'_> { + fn f(&mut self) { + self.e.x = true; + self.e.y = true; + } + } + + let mut e = E { x: false, y: false }; + let mut p = P { e: &mut e }; + p.f(); + assert!(e.x); +} + +fn nested_boxes() { + struct A { + b: Box, + } + + struct B { + c: Box, + } + + struct C { + u: u32, //~ ERROR: field is never read + v: u32, //~ ERROR: field is never read + } + + let mut a = A { + b: Box::new(B { + c: Box::new(C { u: 0, v: 0 }), + }), + }; + a.b.c.v = 10; + a.b.c = Box::new(C { u: 1, v: 2 }); } diff --git a/src/test/ui/lint/dead-code/write-only-field.stderr b/src/test/ui/lint/dead-code/write-only-field.stderr index 70d2149665b20..a191d22c8b94c 100644 --- a/src/test/ui/lint/dead-code/write-only-field.stderr +++ b/src/test/ui/lint/dead-code/write-only-field.stderr @@ -22,5 +22,23 @@ error: field is never read: `f` LL | f: i32, | ^^^^^^ -error: aborting due to 3 previous errors +error: field is never read: `y` + --> $DIR/write-only-field.rs:28:9 + | +LL | y: bool, + | ^^^^^^^ + +error: field is never read: `u` + --> $DIR/write-only-field.rs:58:9 + | +LL | u: u32, + | ^^^^^^ + +error: field is never read: `v` + --> $DIR/write-only-field.rs:59:9 + | +LL | v: u32, + | ^^^^^^ + +error: aborting due to 6 previous errors diff --git a/src/test/ui/macros/macro-export-on-modularized-macros.rs b/src/test/ui/macros/macro-export-on-modularized-macros.rs new file mode 100644 index 0000000000000..467c6ba7b78e4 --- /dev/null +++ b/src/test/ui/macros/macro-export-on-modularized-macros.rs @@ -0,0 +1,11 @@ +#![feature(decl_macro)] +#![feature(pub_macro_rules)] + +#[macro_export] +macro m1() {} //~ ERROR `#[macro_export]` cannot be used on `macro` items + +#[macro_export] +pub macro_rules! m2 { () => {} } +//~^ ERROR `#[macro_export]` cannot be used on `macro_rules` with `pub` + +fn main() {} diff --git a/src/test/ui/macros/macro-export-on-modularized-macros.stderr b/src/test/ui/macros/macro-export-on-modularized-macros.stderr new file mode 100644 index 0000000000000..8bb031e12cba2 --- /dev/null +++ b/src/test/ui/macros/macro-export-on-modularized-macros.stderr @@ -0,0 +1,14 @@ +error: `#[macro_export]` cannot be used on `macro` items + --> $DIR/macro-export-on-modularized-macros.rs:5:1 + | +LL | macro m1() {} + | ^^^^^^^^^^^^^ + +error: `#[macro_export]` cannot be used on `macro_rules` with `pub` + --> $DIR/macro-export-on-modularized-macros.rs:8:1 + | +LL | pub macro_rules! m2 { () => {} } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/macros/pub-macro-rules-fail.rs b/src/test/ui/macros/pub-macro-rules-fail.rs new file mode 100644 index 0000000000000..bdb4c73f18b18 --- /dev/null +++ b/src/test/ui/macros/pub-macro-rules-fail.rs @@ -0,0 +1,28 @@ +#![feature(pub_macro_rules)] + +#[macro_use] +mod m { + pub macro_rules! mac { () => {} } + + // `pub` `macro_rules` cannot be redefined in the same module. + pub macro_rules! mac { () => {} } //~ ERROR the name `mac` is defined multiple times + + pub(self) macro_rules! private_mac { () => {} } +} + +const _: () = { + pub macro_rules! block_mac { () => {} } +}; + +mod n { + // Scope of `pub` `macro_rules` is not extended by `#[macro_use]`. + mac!(); //~ ERROR cannot find macro `mac` in this scope + + // `pub` `macro_rules` doesn't put the macro into the root module, unlike `#[macro_export]`. + crate::mac!(); //~ ERROR failed to resolve: maybe a missing crate `mac` + crate::block_mac!(); //~ ERROR failed to resolve: maybe a missing crate `block_mac` + + crate::m::private_mac!(); //~ ERROR macro `private_mac` is private +} + +fn main() {} diff --git a/src/test/ui/macros/pub-macro-rules-fail.stderr b/src/test/ui/macros/pub-macro-rules-fail.stderr new file mode 100644 index 0000000000000..588d79dd76a4b --- /dev/null +++ b/src/test/ui/macros/pub-macro-rules-fail.stderr @@ -0,0 +1,48 @@ +error[E0428]: the name `mac` is defined multiple times + --> $DIR/pub-macro-rules-fail.rs:8:5 + | +LL | pub macro_rules! mac { () => {} } + | -------------------- previous definition of the macro `mac` here +... +LL | pub macro_rules! mac { () => {} } + | ^^^^^^^^^^^^^^^^^^^^ `mac` redefined here + | + = note: `mac` must be defined only once in the macro namespace of this module + +error[E0433]: failed to resolve: maybe a missing crate `mac`? + --> $DIR/pub-macro-rules-fail.rs:22:12 + | +LL | crate::mac!(); + | ^^^ maybe a missing crate `mac`? + +error[E0433]: failed to resolve: maybe a missing crate `block_mac`? + --> $DIR/pub-macro-rules-fail.rs:23:12 + | +LL | crate::block_mac!(); + | ^^^^^^^^^ maybe a missing crate `block_mac`? + +error: cannot find macro `mac` in this scope + --> $DIR/pub-macro-rules-fail.rs:19:5 + | +LL | mac!(); + | ^^^ + | + = note: consider importing this macro: + m::mac + +error[E0603]: macro `private_mac` is private + --> $DIR/pub-macro-rules-fail.rs:25:15 + | +LL | crate::m::private_mac!(); + | ^^^^^^^^^^^ private macro + | +note: the macro `private_mac` is defined here + --> $DIR/pub-macro-rules-fail.rs:10:5 + | +LL | pub(self) macro_rules! private_mac { () => {} } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0428, E0433, E0603. +For more information about an error, try `rustc --explain E0428`. diff --git a/src/test/ui/macros/pub-macro-rules.rs b/src/test/ui/macros/pub-macro-rules.rs new file mode 100644 index 0000000000000..cd4a845f7c07d --- /dev/null +++ b/src/test/ui/macros/pub-macro-rules.rs @@ -0,0 +1,20 @@ +// check-pass + +#![feature(pub_macro_rules)] + +mod m { + // `pub` `macro_rules` can be used earlier in item order than they are defined. + foo!(); + + pub macro_rules! foo { () => {} } + + // `pub(...)` works too. + pub(super) macro_rules! bar { () => {} } +} + +// `pub` `macro_rules` are available by module path. +m::foo!(); + +m::bar!(); + +fn main() {} diff --git a/src/test/ui/non-fmt-panic.rs b/src/test/ui/non-fmt-panic.rs index 25c53316e1290..c80a90b3eaaac 100644 --- a/src/test/ui/non-fmt-panic.rs +++ b/src/test/ui/non-fmt-panic.rs @@ -29,6 +29,17 @@ fn main() { fancy_panic::fancy_panic!(S); //~^ WARN panic message is not a string literal + macro_rules! a { + () => { 123 }; + } + + panic!(a!()); //~ WARN panic message is not a string literal + + panic!(format!("{}", 1)); //~ WARN panic message is not a string literal + + panic![123]; //~ WARN panic message is not a string literal + panic!{123}; //~ WARN panic message is not a string literal + // Check that the lint only triggers for std::panic and core::panic, // not any panic macro: macro_rules! panic { diff --git a/src/test/ui/non-fmt-panic.stderr b/src/test/ui/non-fmt-panic.stderr index 45187c518c423..7a333b3e76abe 100644 --- a/src/test/ui/non-fmt-panic.stderr +++ b/src/test/ui/non-fmt-panic.stderr @@ -93,7 +93,7 @@ LL | panic!("{}", C); help: or use std::panic::panic_any instead | LL | std::panic::panic_any(C); - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ warning: panic message is not a string literal --> $DIR/non-fmt-panic.rs:20:12 @@ -109,7 +109,7 @@ LL | panic!("{}", S); help: or use std::panic::panic_any instead | LL | std::panic::panic_any(S); - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ warning: panic message is not a string literal --> $DIR/non-fmt-panic.rs:21:17 @@ -125,7 +125,7 @@ LL | std::panic!("{}", 123); help: or use std::panic::panic_any instead | LL | std::panic::panic_any(123); - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ warning: panic message is not a string literal --> $DIR/non-fmt-panic.rs:22:18 @@ -183,5 +183,66 @@ LL | fancy_panic::fancy_panic!(S); | = note: this is no longer accepted in Rust 2021 -warning: 14 warnings emitted +warning: panic message is not a string literal + --> $DIR/non-fmt-panic.rs:36:12 + | +LL | panic!(a!()); + | ^^^^ + | + = note: this is no longer accepted in Rust 2021 +help: add a "{}" format string to Display the message + | +LL | panic!("{}", a!()); + | ^^^^^ +help: or use std::panic::panic_any instead + | +LL | std::panic::panic_any(a!()); + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: panic message is not a string literal + --> $DIR/non-fmt-panic.rs:38:12 + | +LL | panic!(format!("{}", 1)); + | ^^^^^^^^^^^^^^^^ + | + = note: this is no longer accepted in Rust 2021 + = note: the panic!() macro supports formatting, so there's no need for the format!() macro here +help: remove the `format!(..)` macro call + | +LL | panic!("{}", 1); + | -- -- + +warning: panic message is not a string literal + --> $DIR/non-fmt-panic.rs:40:12 + | +LL | panic![123]; + | ^^^ + | + = note: this is no longer accepted in Rust 2021 +help: add a "{}" format string to Display the message + | +LL | panic!["{}", 123]; + | ^^^^^ +help: or use std::panic::panic_any instead + | +LL | std::panic::panic_any(123); + | ^^^^^^^^^^^^^^^^^^^^^^ ^ + +warning: panic message is not a string literal + --> $DIR/non-fmt-panic.rs:41:12 + | +LL | panic!{123}; + | ^^^ + | + = note: this is no longer accepted in Rust 2021 +help: add a "{}" format string to Display the message + | +LL | panic!{"{}", 123}; + | ^^^^^ +help: or use std::panic::panic_any instead + | +LL | std::panic::panic_any(123); + | ^^^^^^^^^^^^^^^^^^^^^^ ^ + +warning: 18 warnings emitted diff --git a/src/test/ui/pattern/issue-82290.rs b/src/test/ui/pattern/issue-82290.rs new file mode 100644 index 0000000000000..67f0274fe7436 --- /dev/null +++ b/src/test/ui/pattern/issue-82290.rs @@ -0,0 +1,7 @@ +#![feature(let_chains)] //~ WARN the feature `let_chains` is incomplete + +fn main() { + if true && let x = 1 { //~ ERROR `let` expressions are not supported here + let _ = x; + } +} diff --git a/src/test/ui/pattern/issue-82290.stderr b/src/test/ui/pattern/issue-82290.stderr new file mode 100644 index 0000000000000..65ef018dc9737 --- /dev/null +++ b/src/test/ui/pattern/issue-82290.stderr @@ -0,0 +1,20 @@ +error: `let` expressions are not supported here + --> $DIR/issue-82290.rs:4:16 + | +LL | if true && let x = 1 { + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + +warning: the feature `let_chains` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/issue-82290.rs:1:12 + | +LL | #![feature(let_chains)] + | ^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53667 for more information + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/tools/jsondocck/src/cache.rs b/src/tools/jsondocck/src/cache.rs index b742f0eb3ee55..8a6a911321c34 100644 --- a/src/tools/jsondocck/src/cache.rs +++ b/src/tools/jsondocck/src/cache.rs @@ -9,6 +9,7 @@ pub struct Cache { root: PathBuf, files: HashMap, values: HashMap, + pub variables: HashMap, last_path: Option, } @@ -19,6 +20,7 @@ impl Cache { root: Path::new(doc_dir).to_owned(), files: HashMap::new(), values: HashMap::new(), + variables: HashMap::new(), last_path: None, } } diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 6ec292aba6495..5020a4917a00a 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -2,6 +2,7 @@ use jsonpath_lib::select; use lazy_static::lazy_static; use regex::{Regex, RegexBuilder}; use serde_json::Value; +use std::borrow::Cow; use std::{env, fmt, fs}; mod cache; @@ -48,13 +49,16 @@ pub struct Command { pub enum CommandKind { Has, Count, + Is, + Set, } impl CommandKind { fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool { let count = match self { CommandKind::Has => (1..=3).contains(&args.len()), - CommandKind::Count => 3 == args.len(), + CommandKind::Count | CommandKind::Is => 3 == args.len(), + CommandKind::Set => 4 == args.len(), }; if !count { @@ -83,6 +87,8 @@ impl fmt::Display for CommandKind { let text = match self { CommandKind::Has => "has", CommandKind::Count => "count", + CommandKind::Is => "is", + CommandKind::Set => "set", }; write!(f, "{}", text) } @@ -127,6 +133,8 @@ fn get_commands(template: &str) -> Result, ()> { let cmd = match cmd { "has" => CommandKind::Has, "count" => CommandKind::Count, + "is" => CommandKind::Is, + "set" => CommandKind::Set, _ => { print_err(&format!("Unrecognized command name `@{}`", cmd), lineno); errors = true; @@ -180,6 +188,7 @@ fn get_commands(template: &str) -> Result, ()> { /// Performs the actual work of ensuring a command passes. Generally assumes the command /// is syntactically valid. fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { + // FIXME: Be more granular about why, (e.g. syntax error, count not equal) let result = match command.kind { CommandKind::Has => { match command.args.len() { @@ -188,23 +197,15 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { // @has = check path exists 2 => { let val = cache.get_value(&command.args[0])?; - - match select(&val, &command.args[1]) { - Ok(results) => !results.is_empty(), - Err(_) => false, - } + let results = select(&val, &command.args[1]).unwrap(); + !results.is_empty() } // @has = check *any* item matched by path equals value 3 => { let val = cache.get_value(&command.args[0])?; - match select(&val, &command.args[1]) { - Ok(results) => { - let pat: Value = serde_json::from_str(&command.args[2]).unwrap(); - - !results.is_empty() && results.into_iter().any(|val| *val == pat) - } - Err(_) => false, - } + let results = select(&val, &command.args[1]).unwrap(); + let pat = string_to_value(&command.args[2], cache); + results.contains(&pat.as_ref()) } _ => unreachable!(), } @@ -215,9 +216,37 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { let expected: usize = command.args[2].parse().unwrap(); let val = cache.get_value(&command.args[0])?; - match select(&val, &command.args[1]) { - Ok(results) => results.len() == expected, - Err(_) => false, + let results = select(&val, &command.args[1]).unwrap(); + results.len() == expected + } + CommandKind::Is => { + // @has = check *exactly one* item matched by path, and it equals value + assert_eq!(command.args.len(), 3); + let val = cache.get_value(&command.args[0])?; + let results = select(&val, &command.args[1]).unwrap(); + let pat = string_to_value(&command.args[2], cache); + results.len() == 1 && results[0] == pat.as_ref() + } + CommandKind::Set => { + // @set = + assert_eq!(command.args.len(), 4); + assert_eq!(command.args[1], "=", "Expected an `=`"); + let val = cache.get_value(&command.args[2])?; + let results = select(&val, &command.args[3]).unwrap(); + assert_eq!(results.len(), 1); + match results.len() { + 0 => false, + 1 => { + let r = cache.variables.insert(command.args[0].clone(), results[0].clone()); + assert!(r.is_none(), "Name collision: {} is duplicated", command.args[0]); + true + } + _ => { + panic!( + "Got multiple results in `@set` for `{}`: {:?}", + &command.args[3], results + ); + } } } }; @@ -247,3 +276,11 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { Ok(()) } } + +fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> { + if s.starts_with("$") { + Cow::Borrowed(&cache.variables[&s[1..]]) + } else { + Cow::Owned(serde_json::from_str(s).unwrap()) + } +}