diff options
| author | bors <bors@rust-lang.org> | 2022-03-08 10:24:46 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-03-08 10:24:46 +0000 |
| commit | b97dc20784698730bd7bc1a20eabdcd32066d174 (patch) | |
| tree | 411126fa7c5a4a0cd31761dcfadbfefa6fc6174a /compiler | |
| parent | d2710dbb305a4447b889f11918bc5df96084357c (diff) | |
| parent | b879216f079eb5a9f47fab8d59b567859b51fd2c (diff) | |
Auto merge of #94734 - matthiaskrgr:rollup-28shqhy, r=matthiaskrgr
Rollup of 8 pull requests
Successful merges:
- #91993 (Tweak output for non-exhaustive `match` expression)
- #92385 (Add Result::{ok, err, and, or, unwrap_or} as const)
- #94559 (Remove argument from closure in thread::Scope::spawn.)
- #94580 (Emit `unused_attributes` if a level attr only has a reason)
- #94586 (Generalize `get_nullable_type` to allow types where null is all-ones.)
- #94708 (diagnostics: only talk about `Cargo.toml` if running under Cargo)
- #94712 (promot debug_assert to assert)
- #94726 (:arrow_up: rust-analyzer)
Failed merges:
r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 13 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic_builder.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_feature/src/builtin_attrs.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/levels.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/types.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/thir/pattern/check_match.rs | 143 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/check_attr.rs | 80 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/expr.rs | 4 |
10 files changed, 199 insertions, 65 deletions
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index a59d91ea789..39ebd57b4b2 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -7,6 +7,7 @@ use crate::SuggestionStyle; use crate::ToolMetadata; use rustc_lint_defs::Applicability; use rustc_serialize::json::Json; +use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::{MultiSpan, Span, DUMMY_SP}; use std::fmt; use std::hash::{Hash, Hasher}; @@ -342,6 +343,18 @@ impl Diagnostic { self } + /// Help the user upgrade to the latest edition. + /// This is factored out to make sure it does the right thing with `Cargo.toml`. + pub fn help_use_latest_edition(&mut self) -> &mut Self { + if std::env::var_os("CARGO").is_some() { + self.help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION)); + } else { + self.help(&format!("pass `--edition {}` to `rustc`", LATEST_STABLE_EDITION)); + } + self.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); + self + } + /// Disallow attaching suggestions this diagnostic. /// Any suggestions attached e.g. with the `span_suggestion_*` methods /// (before and after the call to `disable_suggestions`) will be ignored. diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 5dc71f16200..98b8b2a569e 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -409,6 +409,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { sp: impl Into<MultiSpan>, msg: &str, ) -> &mut Self); + forward!(pub fn help_use_latest_edition(&mut self,) -> &mut Self); forward!(pub fn set_is_lint(&mut self,) -> &mut Self); forward!(pub fn disable_suggestions(&mut self,) -> &mut Self); diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 4b9cf784495..bb51f880099 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -606,17 +606,17 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!( rustc_layout_scalar_valid_range_start, Normal, template!(List: "value"), ErrorFollowing, "the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \ - niche optimizations in libcore and will never be stable", + niche optimizations in libcore and libstd and will never be stable", ), rustc_attr!( rustc_layout_scalar_valid_range_end, Normal, template!(List: "value"), ErrorFollowing, "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \ - niche optimizations in libcore and will never be stable", + niche optimizations in libcore and libstd and will never be stable", ), rustc_attr!( rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing, "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to enable \ - niche optimizations in libcore and will never be stable", + niche optimizations in libcore and libstd and will never be stable", ), // ========================================================================== diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index f46f74fa45f..bbfbf61f486 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -258,7 +258,7 @@ impl<'s> LintLevelsBuilder<'s> { }; if metas.is_empty() { - // FIXME (#55112): issue unused-attributes lint for `#[level()]` + // This emits the unused_attributes lint for `#[level()]` continue; } @@ -271,8 +271,6 @@ impl<'s> LintLevelsBuilder<'s> { ast::MetaItemKind::Word => {} // actual lint names handled later ast::MetaItemKind::NameValue(ref name_value) => { if item.path == sym::reason { - // FIXME (#55112): issue unused-attributes lint if we thereby - // don't have any lint names (`#[level(reason = "foo")]`) if let ast::LitKind::Str(rationale, _) = name_value.kind { if !self.sess.features_untracked().lint_reasons { feature_err( diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 0d5e1e7f551..d4dac640cc7 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -795,7 +795,9 @@ crate fn repr_nullable_ptr<'tcx>( let field_ty_abi = &cx.layout_of(field_ty).unwrap().abi; if let Abi::Scalar(field_ty_scalar) = field_ty_abi { match (field_ty_scalar.valid_range.start, field_ty_scalar.valid_range.end) { - (0, _) => unreachable!("Non-null optimisation extended to a non-zero value."), + (0, x) if x == field_ty_scalar.value.size(&cx.tcx).unsigned_int_max() - 1 => { + return Some(get_nullable_type(cx, field_ty).unwrap()); + } (1, _) => { return Some(get_nullable_type(cx, field_ty).unwrap()); } diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 27a6b5beb62..b80d2e52ee7 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -20,7 +20,7 @@ use rustc_session::lint::builtin::{ }; use rustc_session::Session; use rustc_span::source_map::Spanned; -use rustc_span::{DesugaringKind, ExpnKind, Span}; +use rustc_span::{DesugaringKind, ExpnKind, MultiSpan, Span}; crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { let body_id = match def_id.as_local() { @@ -64,7 +64,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> { fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { intravisit::walk_expr(self, ex); match &ex.kind { - hir::ExprKind::Match(scrut, arms, source) => self.check_match(scrut, arms, *source), + hir::ExprKind::Match(scrut, arms, source) => { + self.check_match(scrut, arms, *source, ex.span) + } hir::ExprKind::Let(hir::Let { pat, init, span, .. }) => { self.check_let(pat, init, *span) } @@ -163,6 +165,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { scrut: &hir::Expr<'_>, hir_arms: &'tcx [hir::Arm<'tcx>], source: hir::MatchSource, + expr_span: Span, ) { let mut cx = self.new_cx(scrut.hir_id); @@ -208,7 +211,6 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } // Check if the match is exhaustive. - let is_empty_match = arms.is_empty(); let witnesses = report.non_exhaustiveness_witnesses; if !witnesses.is_empty() { if source == hir::MatchSource::ForLoopDesugar && hir_arms.len() == 2 { @@ -216,7 +218,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let pat = hir_arms[1].pat.for_loop_some().unwrap(); self.check_irrefutable(pat, "`for` loop binding", None); } else { - non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, is_empty_match); + non_exhaustive_match(&cx, scrut_ty, scrut.span, witnesses, hir_arms, expr_span); } } } @@ -334,7 +336,7 @@ fn check_for_bindings_named_same_as_variants( let ty_path = cx.tcx.def_path_str(edef.did); let mut err = lint.build(&format!( "pattern binding `{}` is named the same as one \ - of the variants of the type `{}`", + of the variants of the type `{}`", ident, ty_path )); err.code(error_code!(E0170)); @@ -494,8 +496,10 @@ fn non_exhaustive_match<'p, 'tcx>( scrut_ty: Ty<'tcx>, sp: Span, witnesses: Vec<DeconstructedPat<'p, 'tcx>>, - is_empty_match: bool, + arms: &[hir::Arm<'tcx>], + expr_span: Span, ) { + let is_empty_match = arms.is_empty(); let non_empty_enum = match scrut_ty.kind() { ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(), _ => false, @@ -503,12 +507,15 @@ fn non_exhaustive_match<'p, 'tcx>( // In the case of an empty match, replace the '`_` not covered' diagnostic with something more // informative. let mut err; + let pattern; + let mut patterns_len = 0; if is_empty_match && !non_empty_enum { err = create_e0004( cx.tcx.sess, sp, format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty), ); + pattern = "_".to_string(); } else { let joined_patterns = joined_uncovered_patterns(cx, &witnesses); err = create_e0004( @@ -517,6 +524,16 @@ fn non_exhaustive_match<'p, 'tcx>( format!("non-exhaustive patterns: {} not covered", joined_patterns), ); err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); + patterns_len = witnesses.len(); + pattern = if witnesses.len() < 4 { + witnesses + .iter() + .map(|witness| witness.to_pat(cx).to_string()) + .collect::<Vec<String>>() + .join(" | ") + } else { + "_".to_string() + }; }; let is_variant_list_non_exhaustive = match scrut_ty.kind() { @@ -525,10 +542,6 @@ fn non_exhaustive_match<'p, 'tcx>( }; adt_defined_here(cx, &mut err, scrut_ty, &witnesses); - err.help( - "ensure that all possible cases are being handled, \ - possibly by adding wildcards or more match arms", - ); err.note(&format!( "the matched value is of type `{}`{}", scrut_ty, @@ -540,14 +553,14 @@ fn non_exhaustive_match<'p, 'tcx>( && matches!(witnesses[0].ctor(), Constructor::NonExhaustive) { err.note(&format!( - "`{}` does not have a fixed maximum value, \ - so a wildcard `_` is necessary to match exhaustively", + "`{}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \ + exhaustively", scrut_ty, )); if cx.tcx.sess.is_nightly_build() { err.help(&format!( - "add `#![feature(precise_pointer_size_matching)]` \ - to the crate attributes to enable precise `{}` matching", + "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \ + enable precise `{}` matching", scrut_ty, )); } @@ -557,6 +570,84 @@ fn non_exhaustive_match<'p, 'tcx>( err.note("references are always considered inhabited"); } } + + let mut suggestion = None; + let sm = cx.tcx.sess.source_map(); + match arms { + [] if sp.ctxt() == expr_span.ctxt() => { + // Get the span for the empty match body `{}`. + let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) { + (format!("\n{}", snippet), " ") + } else { + (" ".to_string(), "") + }; + suggestion = Some(( + sp.shrink_to_hi().with_hi(expr_span.hi()), + format!( + " {{{indentation}{more}{pattern} => todo!(),{indentation}}}", + indentation = indentation, + more = more, + pattern = pattern, + ), + )); + } + [only] => { + let pre_indentation = if let (Some(snippet), true) = ( + sm.indentation_before(only.span), + sm.is_multiline(sp.shrink_to_hi().with_hi(only.span.lo())), + ) { + format!("\n{}", snippet) + } else { + " ".to_string() + }; + let comma = if matches!(only.body.kind, hir::ExprKind::Block(..)) { "" } else { "," }; + suggestion = Some(( + only.span.shrink_to_hi(), + format!("{}{}{} => todo!()", comma, pre_indentation, pattern), + )); + } + [.., prev, last] if prev.span.ctxt() == last.span.ctxt() => { + if let Ok(snippet) = sm.span_to_snippet(prev.span.between(last.span)) { + let comma = + if matches!(last.body.kind, hir::ExprKind::Block(..)) { "" } else { "," }; + suggestion = Some(( + last.span.shrink_to_hi(), + format!( + "{}{}{} => todo!()", + comma, + snippet.strip_prefix(",").unwrap_or(&snippet), + pattern + ), + )); + } + } + _ => {} + } + + let msg = format!( + "ensure that all possible cases are being handled by adding a match arm with a wildcard \ + pattern{}{}", + if patterns_len > 1 && patterns_len < 4 && suggestion.is_some() { + ", a match arm with multiple or-patterns" + } else { + // we are either not suggesting anything, or suggesting `_` + "" + }, + match patterns_len { + // non-exhaustive enum case + 0 if suggestion.is_some() => " as shown", + 0 => "", + 1 if suggestion.is_some() => " or an explicit pattern as shown", + 1 => " or an explicit pattern", + _ if suggestion.is_some() => " as shown, or multiple match arms", + _ => " or multiple match arms", + }, + ); + if let Some((span, sugg)) = suggestion { + err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders); + } else { + err.help(&msg); + } err.emit(); } @@ -597,15 +688,27 @@ fn adt_defined_here<'p, 'tcx>( ) { let ty = ty.peel_refs(); if let ty::Adt(def, _) = ty.kind() { - if let Some(sp) = cx.tcx.hir().span_if_local(def.did) { - err.span_label(sp, format!("`{}` defined here", ty)); - } - - if witnesses.len() < 4 { + let mut spans = vec![]; + if witnesses.len() < 5 { for sp in maybe_point_at_variant(cx, def, witnesses.iter()) { - err.span_label(sp, "not covered"); + spans.push(sp); } } + let def_span = cx + .tcx + .hir() + .get_if_local(def.did) + .and_then(|node| node.ident()) + .map(|ident| ident.span) + .unwrap_or_else(|| cx.tcx.def_span(def.did)); + let mut span: MultiSpan = + if spans.is_empty() { def_span.into() } else { spans.clone().into() }; + + span.push_span_label(def_span, String::new()); + for pat in spans { + span.push_span_label(pat, "not covered".to_string()); + } + err.span_note(span, &format!("`{}` defined here", ty)); } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index df865d77b9b..b993d48c995 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -20,7 +20,6 @@ use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult}; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_session::lint::BuiltinLintDiagnostics; -use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::source_map::{self, Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos}; @@ -2712,8 +2711,7 @@ impl<'a> Parser<'a> { let mut async_block_err = |e: &mut Diagnostic, span: Span| { recover_async = true; e.span_label(span, "`async` blocks are only allowed in Rust 2018 or later"); - e.help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION)); - e.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); + e.help_use_latest_edition(); }; while self.token != token::CloseDelim(close_delim) { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 5db1e4e0523..06460c7b1b3 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -14,7 +14,7 @@ use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, Visibility use rustc_ast::{MacArgs, MacCall, MacDelimiter}; use rustc_ast_pretty::pprust; use rustc_errors::{struct_span_err, Applicability, PResult, StashKey}; -use rustc_span::edition::{Edition, LATEST_STABLE_EDITION}; +use rustc_span::edition::Edition; use rustc_span::lev_distance::lev_distance; use rustc_span::source_map::{self, Span}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -2102,8 +2102,7 @@ impl<'a> Parser<'a> { let diag = self.diagnostic(); struct_span_err!(diag, span, E0670, "`async fn` is not permitted in Rust 2015") .span_label(span, "to use `async fn`, switch to Rust 2018 or later") - .help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION)) - .note("for more on editions, read https://doc.rust-lang.org/edition-guide") + .help_use_latest_edition() .emit(); } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d94ad7ba71a..22020015550 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -4,7 +4,7 @@ //! conflicts between multiple such attributes attached to the same //! item. -use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, NestedMetaItem}; +use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, struct_span_err, Applicability}; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; @@ -178,34 +178,7 @@ impl CheckAttrVisitor<'_> { check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen); } - // Warn on useless empty attributes. - if matches!( - attr.name_or_empty(), - sym::macro_use - | sym::allow - | sym::warn - | sym::deny - | sym::forbid - | sym::feature - | sym::repr - | sym::target_feature - ) && attr.meta_item_list().map_or(false, |list| list.is_empty()) - { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("unused attribute") - .span_suggestion( - attr.span, - "remove this attribute", - String::new(), - Applicability::MachineApplicable, - ) - .note(&format!( - "attribute `{}` with an empty list has no effect", - attr.name_or_empty() - )) - .emit(); - }); - } + self.check_unused_attribute(hir_id, attr) } if !is_valid { @@ -1969,6 +1942,55 @@ impl CheckAttrVisitor<'_> { }); } } + + fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) { + // Warn on useless empty attributes. + let note = if matches!( + attr.name_or_empty(), + sym::macro_use + | sym::allow + | sym::expect + | sym::warn + | sym::deny + | sym::forbid + | sym::feature + | sym::repr + | sym::target_feature + ) && attr.meta_item_list().map_or(false, |list| list.is_empty()) + { + format!( + "attribute `{}` with an empty list has no effect", + attr.name_or_empty() + ) + } else if matches!( + attr.name_or_empty(), + sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect + ) && let Some(meta) = attr.meta_item_list() + && meta.len() == 1 + && let Some(item) = meta[0].meta_item() + && let MetaItemKind::NameValue(_) = &item.kind + && item.path == sym::reason + { + format!( + "attribute `{}` without any lints has no effect", + attr.name_or_empty() + ) + } else { + return; + }; + + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + lint.build("unused attribute") + .span_suggestion( + attr.span, + "remove this attribute", + String::new(), + Applicability::MachineApplicable, + ) + .note(¬e) + .emit(); + }); + } } impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 8e245beaa3d..7e7104f62fd 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -43,7 +43,6 @@ use rustc_middle::ty::error::TypeError::{FieldMisMatch, Sorts}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, AdtKind, Ty, TypeFoldable}; use rustc_session::parse::feature_err; -use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::Span; @@ -2010,8 +2009,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We know by construction that `<expr>.await` is either on Rust 2015 // or results in `ExprKind::Await`. Suggest switching the edition to 2018. err.note("to `.await` a `Future`, switch to Rust 2018 or later"); - err.help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION)); - err.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); + err.help_use_latest_edition(); } err.emit(); |
