diff options
Diffstat (limited to 'compiler')
20 files changed, 268 insertions, 59 deletions
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index fd0ff5b66e6..289629d9215 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -199,7 +199,7 @@ impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { } self.visit_local(&place_ref.local, context, location); - self.visit_projection(place_ref.local, place_ref.projection, context, location); + self.visit_projection(*place_ref, context, location); } } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 9800ed9bfa9..a0be7442d59 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -30,6 +30,7 @@ use rustc_span::{Loc, MultiSpan, Span}; use std::borrow::Cow; use std::hash::{Hash, Hasher}; +use std::num::NonZeroUsize; use std::panic; use std::path::Path; use std::{error, fmt}; @@ -359,7 +360,7 @@ pub struct HandlerFlags { pub can_emit_warnings: bool, /// If true, error-level diagnostics are upgraded to bug-level. /// (rustc: see `-Z treat-err-as-bug`) - pub treat_err_as_bug: Option<usize>, + pub treat_err_as_bug: Option<NonZeroUsize>, /// If true, immediately emit diagnostics that would otherwise be buffered. /// (rustc: see `-Z dont-buffer-diagnostics` and `-Z treat-err-as-bug`) pub dont_buffer_diagnostics: bool, @@ -396,7 +397,7 @@ impl Handler { pub fn with_tty_emitter( color_config: ColorConfig, can_emit_warnings: bool, - treat_err_as_bug: Option<usize>, + treat_err_as_bug: Option<NonZeroUsize>, sm: Option<Lrc<SourceMap>>, ) -> Self { Self::with_tty_emitter_and_flags( @@ -424,7 +425,7 @@ impl Handler { pub fn with_emitter( can_emit_warnings: bool, - treat_err_as_bug: Option<usize>, + treat_err_as_bug: Option<NonZeroUsize>, emitter: Box<dyn Emitter + sync::Send>, ) -> Self { Handler::with_emitter_and_flags( @@ -841,7 +842,7 @@ impl HandlerInner { } fn treat_err_as_bug(&self) -> bool { - self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() >= c) + self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() >= c.get()) } fn print_error_count(&mut self, registry: &Registry) { @@ -950,7 +951,7 @@ impl HandlerInner { // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before // incrementing `err_count` by one, so we need to +1 the comparing. // FIXME: Would be nice to increment err_count in a more coherent way. - if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c) { + if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c.get()) { // FIXME: don't abort here if report_delayed_bugs is off self.span_bug(sp, msg); } @@ -1023,7 +1024,7 @@ impl HandlerInner { fn panic_if_treat_err_as_bug(&self) { if self.treat_err_as_bug() { - match (self.err_count(), self.flags.treat_err_as_bug.unwrap_or(0)) { + match (self.err_count(), self.flags.treat_err_as_bug.map(|c| c.get()).unwrap_or(0)) { (1, 1) => panic!("aborting due to `-Z treat-err-as-bug=1`"), (0, _) | (1, _) => {} (count, as_bug) => panic!( diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 61586dd1a1c..2fef7c2cc08 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,5 +1,5 @@ // ignore-tidy-filelength -use crate::def::{DefKind, Namespace, Res}; +use crate::def::{CtorKind, DefKind, Namespace, Res}; use crate::def_id::DefId; crate use crate::hir_id::HirId; use crate::{itemlikevisit, LangItem}; @@ -1576,6 +1576,63 @@ impl Expr<'_> { } expr } + + pub fn can_have_side_effects(&self) -> bool { + match self.peel_drop_temps().kind { + ExprKind::Path(_) | ExprKind::Lit(_) => false, + ExprKind::Type(base, _) + | ExprKind::Unary(_, base) + | ExprKind::Field(base, _) + | ExprKind::Index(base, _) + | ExprKind::AddrOf(.., base) + | ExprKind::Cast(base, _) => { + // This isn't exactly true for `Index` and all `Unnary`, but we are using this + // method exclusively for diagnostics and there's a *cultural* pressure against + // them being used only for its side-effects. + base.can_have_side_effects() + } + ExprKind::Struct(_, fields, init) => fields + .iter() + .map(|field| field.expr) + .chain(init.into_iter()) + .all(|e| e.can_have_side_effects()), + + ExprKind::Array(args) + | ExprKind::Tup(args) + | ExprKind::Call( + Expr { + kind: + ExprKind::Path(QPath::Resolved( + None, + Path { res: Res::Def(DefKind::Ctor(_, CtorKind::Fn), _), .. }, + )), + .. + }, + args, + ) => args.iter().all(|arg| arg.can_have_side_effects()), + ExprKind::If(..) + | ExprKind::Match(..) + | ExprKind::MethodCall(..) + | ExprKind::Call(..) + | ExprKind::Closure(..) + | ExprKind::Block(..) + | ExprKind::Repeat(..) + | ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::Loop(..) + | ExprKind::Assign(..) + | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) + | ExprKind::AssignOp(..) + | ExprKind::ConstBlock(..) + | ExprKind::Box(..) + | ExprKind::Binary(..) + | ExprKind::Yield(..) + | ExprKind::DropTemps(..) + | ExprKind::Err => true, + } + } } /// Checks if the specified expression is a built-in range literal. diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index f9c3406d3b3..a2e96146568 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -20,6 +20,7 @@ use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy} use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TlsModel}; use std::collections::{BTreeMap, BTreeSet}; use std::iter::FromIterator; +use std::num::NonZeroUsize; use std::path::{Path, PathBuf}; type CfgSpecs = FxHashSet<(String, Option<String>)>; @@ -595,7 +596,7 @@ fn test_debugging_options_tracking_hash() { tracked!(tune_cpu, Some(String::from("abc"))); tracked!(tls_model, Some(TlsModel::GeneralDynamic)); tracked!(trap_unreachable, Some(false)); - tracked!(treat_err_as_bug, Some(1)); + tracked!(treat_err_as_bug, NonZeroUsize::new(1)); tracked!(unleash_the_miri_inside_of_you, true); tracked!(use_ctors_section, Some(true)); tracked!(verify_llvm_ir, true); diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 023555d91cc..9530efaedbc 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -998,12 +998,11 @@ macro_rules! visit_place_fns { () => { fn visit_projection( &mut self, - local: Local, - projection: &[PlaceElem<'tcx>], + place_ref: PlaceRef<'tcx>, context: PlaceContext, location: Location, ) { - self.super_projection(local, projection, context, location); + self.super_projection(place_ref, context, location); } fn visit_projection_elem( @@ -1033,20 +1032,20 @@ macro_rules! visit_place_fns { self.visit_local(&place.local, context, location); - self.visit_projection(place.local, &place.projection, context, location); + self.visit_projection(place.as_ref(), context, location); } fn super_projection( &mut self, - local: Local, - projection: &[PlaceElem<'tcx>], + place_ref: PlaceRef<'tcx>, context: PlaceContext, location: Location, ) { - let mut cursor = projection; + // FIXME: Use PlaceRef::iter_projections, once that exists. + let mut cursor = place_ref.projection; while let &[ref proj_base @ .., elem] = cursor { cursor = proj_base; - self.visit_projection_elem(local, cursor, elem, context, location); + self.visit_projection_elem(place_ref.local, cursor, elem, context, location); } } diff --git a/compiler/rustc_mir/src/dataflow/impls/liveness.rs b/compiler/rustc_mir/src/dataflow/impls/liveness.rs index 85aaff5ab72..2d20f0d9547 100644 --- a/compiler/rustc_mir/src/dataflow/impls/liveness.rs +++ b/compiler/rustc_mir/src/dataflow/impls/liveness.rs @@ -95,7 +95,7 @@ where // We purposefully do not call `super_place` here to avoid calling `visit_local` for this // place with one of the `Projection` variants of `PlaceContext`. - self.visit_projection(local, projection, context, location); + self.visit_projection(place.as_ref(), context, location); match DefUse::for_place(context) { // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a use. diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index 4973450ca83..a8263683712 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -515,7 +515,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { // Special-case reborrows to be more like a copy of a reference. match *rvalue { Rvalue::Ref(_, kind, place) => { - if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, self.body, place) { + if let Some(reborrowed_place_ref) = place_as_reborrow(self.tcx, self.body, place) { let ctx = match kind { BorrowKind::Shared => { PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) @@ -530,21 +530,21 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { PlaceContext::MutatingUse(MutatingUseContext::Borrow) } }; - self.visit_local(&place.local, ctx, location); - self.visit_projection(place.local, reborrowed_proj, ctx, location); + self.visit_local(&reborrowed_place_ref.local, ctx, location); + self.visit_projection(reborrowed_place_ref, ctx, location); return; } } Rvalue::AddressOf(mutbl, place) => { - if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, self.body, place) { + if let Some(reborrowed_place_ref) = place_as_reborrow(self.tcx, self.body, place) { let ctx = match mutbl { Mutability::Not => { PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf) } Mutability::Mut => PlaceContext::MutatingUse(MutatingUseContext::AddressOf), }; - self.visit_local(&place.local, ctx, location); - self.visit_projection(place.local, reborrowed_proj, ctx, location); + self.visit_local(&reborrowed_place_ref.local, ctx, location); + self.visit_projection(reborrowed_place_ref, ctx, location); return; } } @@ -1039,7 +1039,7 @@ fn place_as_reborrow( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, place: Place<'tcx>, -) -> Option<&'a [PlaceElem<'tcx>]> { +) -> Option<PlaceRef<'tcx>> { match place.as_ref().last_projection() { Some((place_base, ProjectionElem::Deref)) => { // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const` @@ -1048,13 +1048,14 @@ fn place_as_reborrow( None } else { // Ensure the type being derefed is a reference and not a raw pointer. - // // This is sufficient to prevent an access to a `static mut` from being marked as a // reborrow, even if the check above were to disappear. let inner_ty = place_base.ty(body, tcx).ty; - match inner_ty.kind() { - ty::Ref(..) => Some(place_base.projection), - _ => None, + + if let ty::Ref(..) = inner_ty.kind() { + return Some(place_base); + } else { + return None; } } } diff --git a/compiler/rustc_mir/src/transform/simplify.rs b/compiler/rustc_mir/src/transform/simplify.rs index 11539d3ef30..ae4f15b3bbc 100644 --- a/compiler/rustc_mir/src/transform/simplify.rs +++ b/compiler/rustc_mir/src/transform/simplify.rs @@ -413,8 +413,7 @@ impl UsedLocals { } else { // A definition. Although, it still might use other locals for indexing. self.super_projection( - place.local, - &place.projection, + place.as_ref(), PlaceContext::MutatingUse(MutatingUseContext::Projection), location, ); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 1292286bc18..8a097bf481d 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -950,7 +950,7 @@ impl<'a> Parser<'a> { self.bump(); Ok(Ident::new(symbol, self.prev_token.span)) } else { - self.parse_ident_common(false) + self.parse_ident_common(true) } } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 8874548da78..9e2e7359ca9 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1028,7 +1028,7 @@ impl<'a> Parser<'a> { let boxed_span = self.token.span; let is_ref = self.eat_keyword(kw::Ref); let is_mut = self.eat_keyword(kw::Mut); - let fieldname = self.parse_ident()?; + let fieldname = self.parse_field_name()?; hi = self.prev_token.span; let bind_type = match (is_ref, is_mut) { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 4533b37f10b..e4f7e140281 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2313,6 +2313,7 @@ crate mod dep_tracking { use std::collections::hash_map::DefaultHasher; use std::collections::BTreeMap; use std::hash::Hash; + use std::num::NonZeroUsize; use std::path::PathBuf; pub trait DepTrackingHash { @@ -2353,6 +2354,7 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(lint::Level); impl_dep_tracking_hash_via_hash!(Option<bool>); impl_dep_tracking_hash_via_hash!(Option<usize>); + impl_dep_tracking_hash_via_hash!(Option<NonZeroUsize>); impl_dep_tracking_hash_via_hash!(Option<String>); impl_dep_tracking_hash_via_hash!(Option<(String, u64)>); impl_dep_tracking_hash_via_hash!(Option<Vec<String>>); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index baa0502521d..d439753d042 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -16,6 +16,7 @@ use std::collections::BTreeMap; use std::collections::hash_map::DefaultHasher; use std::hash::Hasher; +use std::num::NonZeroUsize; use std::path::PathBuf; use std::str; @@ -591,10 +592,10 @@ macro_rules! options { true } - fn parse_treat_err_as_bug(slot: &mut Option<usize>, v: Option<&str>) -> bool { + fn parse_treat_err_as_bug(slot: &mut Option<NonZeroUsize>, v: Option<&str>) -> bool { match v { - Some(s) => { *slot = s.parse().ok().filter(|&x| x != 0); slot.unwrap_or(0) != 0 } - None => { *slot = Some(1); true } + Some(s) => { *slot = s.parse().ok(); slot.is_some() } + None => { *slot = NonZeroUsize::new(1); true } } } @@ -1141,7 +1142,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "for every macro invocation, print its name and arguments (default: no)"), trap_unreachable: Option<bool> = (None, parse_opt_bool, [TRACKED], "generate trap instructions for unreachable intrinsics (default: use target setting, usually yes)"), - treat_err_as_bug: Option<usize> = (None, parse_treat_err_as_bug, [TRACKED], + treat_err_as_bug: Option<NonZeroUsize> = (None, parse_treat_err_as_bug, [TRACKED], "treat error number `val` that occurs as bug"), trim_diagnostic_paths: bool = (true, parse_bool, [UNTRACKED], "in diagnostics, use heuristics to shorten paths referring to items"), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 653d70b6cf2..4ed0262bf2c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -169,10 +169,14 @@ symbols! { Option, Ord, Ordering, + OsStr, + OsString, Output, Param, PartialEq, PartialOrd, + Path, + PathBuf, Pending, Pin, Poll, @@ -198,6 +202,8 @@ symbols! { StructuralPartialEq, Sync, Target, + ToOwned, + ToString, Try, Ty, TyCtxt, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index ae803dbdb31..6cf1112820d 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -641,6 +641,7 @@ supported_targets! { ("powerpc64le-unknown-linux-gnu", powerpc64le_unknown_linux_gnu), ("powerpc64le-unknown-linux-musl", powerpc64le_unknown_linux_musl), ("s390x-unknown-linux-gnu", s390x_unknown_linux_gnu), + ("s390x-unknown-linux-musl", s390x_unknown_linux_musl), ("sparc-unknown-linux-gnu", sparc_unknown_linux_gnu), ("sparc64-unknown-linux-gnu", sparc64_unknown_linux_gnu), ("arm-unknown-linux-gnueabi", arm_unknown_linux_gnueabi), diff --git a/compiler/rustc_target/src/spec/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/s390x_unknown_linux_musl.rs new file mode 100644 index 00000000000..4f811ce98c1 --- /dev/null +++ b/compiler/rustc_target/src/spec/s390x_unknown_linux_musl.rs @@ -0,0 +1,24 @@ +use crate::abi::Endian; +use crate::spec::Target; + +pub fn target() -> Target { + let mut base = super::linux_musl_base::opts(); + base.endian = Endian::Big; + // z10 is the oldest CPU supported by LLVM + base.cpu = "z10".to_string(); + // FIXME: The data_layout string below and the ABI implementation in + // cabi_s390x.rs are for now hard-coded to assume the no-vector ABI. + // Pass the -vector feature string to LLVM to respect this assumption. + base.features = "-vector".to_string(); + base.max_atomic_width = Some(64); + base.min_global_align = Some(16); + base.static_position_independent_executables = true; + + Target { + llvm_target: "s390x-unknown-linux-musl".to_string(), + pointer_width: 64, + data_layout: "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-a:8:16-n32:64".to_string(), + arch: "s390x".to_string(), + options: base, + } +} diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 4836418b3c2..f2fbb95fc02 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -346,7 +346,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if call_is_multiline { err.span_suggestion( callee.span.shrink_to_hi(), - "try adding a semicolon", + "consider using a semicolon here", ";".to_owned(), Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index b2395b7bb25..159c97d8bfa 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -1450,7 +1450,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { ) { if cond_expr.span.desugaring_kind().is_none() { err.span_label(cond_expr.span, "expected this to be `()`"); - fcx.suggest_semicolon_at_end(cond_expr.span, &mut err); + if expr.can_have_side_effects() { + fcx.suggest_semicolon_at_end(cond_expr.span, &mut err); + } } } fcx.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) @@ -1458,7 +1460,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx.get_fn_decl(parent_id) }; - if let (Some((fn_decl, can_suggest)), _) = (fn_decl, pointing_at_return_type) { + if let Some((fn_decl, can_suggest)) = fn_decl { if expression.is_none() { pointing_at_return_type |= fcx.suggest_missing_return_type( &mut err, @@ -1472,6 +1474,16 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fn_output = Some(&fn_decl.output); // `impl Trait` return type } } + + let parent_id = fcx.tcx.hir().get_parent_item(id); + let parent_item = fcx.tcx.hir().get(parent_id); + + if let (Some((expr, _)), Some((fn_decl, _, _))) = + (expression, fcx.get_node_fn_decl(parent_item)) + { + fcx.suggest_missing_return_expr(&mut err, expr, fn_decl, expected, found); + } + if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) { self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output); } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 3326be796ce..155c10e8916 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -561,7 +561,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::StmtKind::Expr(ref expr) => { // Check with expected type of `()`. self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| { - self.suggest_semicolon_at_end(expr.span, err); + if expr.can_have_side_effects() { + self.suggest_semicolon_at_end(expr.span, err); + } }); } hir::StmtKind::Semi(ref expr) => { diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index a0465ca6aef..416b75d9e2e 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -44,11 +44,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { blk_id: hir::HirId, ) -> bool { let expr = expr.peel_drop_temps(); - self.suggest_missing_semicolon(err, expr, expected, cause_span); + if expr.can_have_side_effects() { + self.suggest_missing_semicolon(err, expr, expected, cause_span); + } let mut pointing_at_return_type = false; if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { pointing_at_return_type = self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest); + self.suggest_missing_return_expr(err, expr, &fn_decl, expected, found); } pointing_at_return_type } @@ -392,10 +395,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ExprKind::Loop(..) | ExprKind::If(..) | ExprKind::Match(..) - | ExprKind::Block(..) => { + | ExprKind::Block(..) + if expression.can_have_side_effects() => + { err.span_suggestion( cause_span.shrink_to_hi(), - "try adding a semicolon", + "consider using a semicolon here", ";".to_string(), Applicability::MachineApplicable, ); @@ -464,6 +469,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + pub(in super::super) fn suggest_missing_return_expr( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &'tcx hir::Expr<'tcx>, + fn_decl: &hir::FnDecl<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if !expected.is_unit() { + return; + } + let found = self.resolve_vars_with_obligations(found); + if let hir::FnRetTy::Return(ty) = fn_decl.output { + let ty = AstConv::ast_ty_to_ty(self, ty); + let ty = self.normalize_associated_types_in(expr.span, ty); + if self.can_coerce(found, ty) { + err.multipart_suggestion( + "you might have meant to return this value", + vec![ + (expr.span.shrink_to_lo(), "return ".to_string()), + (expr.span.shrink_to_hi(), ";".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + } + } + pub(in super::super) fn suggest_missing_parentheses( &self, err: &mut DiagnosticBuilder<'_>, diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 5df00ea1d75..897b1f01569 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -17,6 +17,7 @@ use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::Ident; use rustc_span::{BytePos, DUMMY_SP}; use rustc_trait_selection::traits::{ObligationCause, Pattern}; +use ty::VariantDef; use std::cmp; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -1264,14 +1265,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { u.emit(); } } - (None, Some(mut err)) | (Some(mut err), None) => { + (None, Some(mut u)) => { + if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { + u.delay_as_bug(); + e.emit(); + } else { + u.emit(); + } + } + (Some(mut err), None) => { err.emit(); } - (None, None) => {} + (None, None) => { + if let Some(mut err) = + self.error_tuple_variant_index_shorthand(variant, pat, fields) + { + err.emit(); + } + } } no_field_errors } + fn error_tuple_variant_index_shorthand( + &self, + variant: &VariantDef, + pat: &'_ Pat<'_>, + fields: &[hir::FieldPat<'_>], + ) -> Option<DiagnosticBuilder<'_>> { + // if this is a tuple struct, then all field names will be numbers + // so if any fields in a struct pattern use shorthand syntax, they will + // be invalid identifiers (for example, Foo { 0, 1 }). + if let (CtorKind::Fn, PatKind::Struct(qpath, field_patterns, ..)) = + (variant.ctor_kind, &pat.kind) + { + let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand); + if has_shorthand_field_name { + let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { + s.print_qpath(qpath, false) + }); + let mut err = struct_span_err!( + self.tcx.sess, + pat.span, + E0769, + "tuple variant `{}` written as struct variant", + path + ); + err.span_suggestion_verbose( + qpath.span().shrink_to_hi().to(pat.span.shrink_to_hi()), + "use the tuple variant pattern syntax instead", + format!("({})", self.get_suggested_tuple_struct_pattern(fields, variant)), + Applicability::MaybeIncorrect, + ); + return Some(err); + } + } + None + } + fn error_foreign_non_exhaustive_spat(&self, pat: &Pat<'_>, descr: &str, no_fields: bool) { let sess = self.tcx.sess; let sm = sess.source_map(); @@ -1411,16 +1462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let (sugg, appl) = if fields.len() == variant.fields.len() { ( - fields - .iter() - .map(|f| match self.tcx.sess.source_map().span_to_snippet(f.pat.span) { - Ok(f) => f, - Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { - s.print_pat(f.pat) - }), - }) - .collect::<Vec<String>>() - .join(", "), + self.get_suggested_tuple_struct_pattern(fields, variant), Applicability::MachineApplicable, ) } else { @@ -1429,10 +1471,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ) }; - err.span_suggestion( - pat.span, + err.span_suggestion_verbose( + qpath.span().shrink_to_hi().to(pat.span.shrink_to_hi()), "use the tuple variant pattern syntax instead", - format!("{}({})", path, sugg), + format!("({})", sugg), appl, ); return Some(err); @@ -1440,6 +1482,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } + fn get_suggested_tuple_struct_pattern( + &self, + fields: &[hir::FieldPat<'_>], + variant: &VariantDef, + ) -> String { + let variant_field_idents = variant.fields.iter().map(|f| f.ident).collect::<Vec<Ident>>(); + fields + .iter() + .map(|field| { + match self.tcx.sess.source_map().span_to_snippet(field.pat.span) { + Ok(f) => { + // Field names are numbers, but numbers + // are not valid identifiers + if variant_field_idents.contains(&field.ident) { + String::from("_") + } else { + f + } + } + Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { + s.print_pat(field.pat) + }), + } + }) + .collect::<Vec<String>>() + .join(", ") + } + /// Returns a diagnostic reporting a struct pattern which is missing an `..` due to /// inaccessible fields. /// |
