Skip to content

Commit

Permalink
Auto merge of rust-lang#94734 - matthiaskrgr:rollup-28shqhy, r=matthi…
Browse files Browse the repository at this point in the history
…askrgr

Rollup of 8 pull requests

Successful merges:

 - rust-lang#91993 (Tweak output for non-exhaustive `match` expression)
 - rust-lang#92385 (Add Result::{ok, err, and, or, unwrap_or} as const)
 - rust-lang#94559 (Remove argument from closure in thread::Scope::spawn.)
 - rust-lang#94580 (Emit `unused_attributes` if a level attr only has a reason)
 - rust-lang#94586 (Generalize `get_nullable_type` to allow types where null is all-ones.)
 - rust-lang#94708 (diagnostics: only talk about `Cargo.toml` if running under Cargo)
 - rust-lang#94712 (promot debug_assert to assert)
 - rust-lang#94726 (:arrow_up: rust-analyzer)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Mar 8, 2022
2 parents d2710db + b879216 commit b97dc20
Show file tree
Hide file tree
Showing 97 changed files with 2,785 additions and 1,093 deletions.
13 changes: 13 additions & 0 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_errors/src/diagnostic_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
),

// ==========================================================================
Expand Down
4 changes: 1 addition & 3 deletions compiler/rustc_lint/src/levels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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(
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_lint/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
143 changes: 123 additions & 20 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -208,15 +211,14 @@ 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 {
// the for loop pattern is not irrefutable
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);
}
}
}
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -494,21 +496,26 @@ 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,
};
// 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(
Expand All @@ -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() {
Expand All @@ -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,
Expand All @@ -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,
));
}
Expand All @@ -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();
}

Expand Down Expand Up @@ -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));
}
}

Expand Down
4 changes: 1 addition & 3 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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) {
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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();
}
}
Expand Down
Loading

0 comments on commit b97dc20

Please sign in to comment.