Auto merge of #128959 - matthiaskrgr:rollup-6jdqi3l, r=matthiaskrgr

Rollup of 6 pull requests

Successful merges:

 - #120314 (core: optimise Debug impl for ascii::Char)
 - #128536 (Preliminary cleanup of `WitnessPat` hoisting/printing)
 - #128592 (Promote aarch64-apple-darwin to Tier 1)
 - #128762 (Use more slice patterns inside the compiler)
 - #128875 (rm `import.used`)
 - #128882 (make LocalWaker::will_wake consistent with Waker::will_wake)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-08-11 05:54:39 +00:00
commit c9bd03cb72
57 changed files with 480 additions and 453 deletions

View file

@ -585,7 +585,9 @@ impl Pat {
}
// A slice/array pattern `[P]` can be reparsed as `[T]`, an unsized array,
// when `P` can be reparsed as a type `T`.
PatKind::Slice(pats) if pats.len() == 1 => pats[0].to_ty().map(TyKind::Slice)?,
PatKind::Slice(pats) if let [pat] = pats.as_slice() => {
pat.to_ty().map(TyKind::Slice)?
}
// A tuple pattern `(P0, .., Pn)` can be reparsed as `(T0, .., Tn)`
// assuming `T0` to `Tn` are all syntactically valid as types.
PatKind::Tuple(pats) => {
@ -1187,8 +1189,8 @@ impl Expr {
/// Does not ensure that the path resolves to a const param, the caller should check this.
pub fn is_potential_trivial_const_arg(&self) -> bool {
let this = if let ExprKind::Block(block, None) = &self.kind
&& block.stmts.len() == 1
&& let StmtKind::Expr(expr) = &block.stmts[0].kind
&& let [stmt] = block.stmts.as_slice()
&& let StmtKind::Expr(expr) = &stmt.kind
{
expr
} else {
@ -1248,7 +1250,9 @@ impl Expr {
expr.to_ty().map(|ty| TyKind::Array(ty, expr_len.clone()))?
}
ExprKind::Array(exprs) if exprs.len() == 1 => exprs[0].to_ty().map(TyKind::Slice)?,
ExprKind::Array(exprs) if let [expr] = exprs.as_slice() => {
expr.to_ty().map(TyKind::Slice)?
}
ExprKind::Tup(exprs) => {
let tys = exprs.iter().map(|expr| expr.to_ty()).collect::<Option<ThinVec<_>>>()?;

View file

@ -275,8 +275,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
// FIXME(fn_delegation): Alternatives for target expression lowering:
// https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2197170600.
fn lower_target_expr(&mut self, block: &Block) -> hir::Expr<'hir> {
if block.stmts.len() == 1
&& let StmtKind::Expr(expr) = &block.stmts[0].kind
if let [stmt] = block.stmts.as_slice()
&& let StmtKind::Expr(expr) = &stmt.kind
{
return self.lower_expr_mut(expr);
}

View file

@ -502,8 +502,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
if !self.is_beginning_of_line() {
self.word(" ");
}
if cmnt.lines.len() == 1 {
self.word(cmnt.lines[0].clone());
if let [line] = cmnt.lines.as_slice() {
self.word(line.clone());
self.hardbreak()
} else {
self.visual_align();

View file

@ -783,8 +783,8 @@ impl<'a> State<'a> {
}
if items.is_empty() {
self.word("{}");
} else if items.len() == 1 {
self.print_use_tree(&items[0].0);
} else if let [(item, _)] = items.as_slice() {
self.print_use_tree(item);
} else {
self.cbox(INDENT_UNIT);
self.word("{");

View file

@ -665,12 +665,12 @@ pub fn eval_condition(
res & eval_condition(mi.meta_item().unwrap(), sess, features, eval)
}),
sym::not => {
if mis.len() != 1 {
let [mi] = mis.as_slice() else {
dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span });
return false;
}
};
!eval_condition(mis[0].meta_item().unwrap(), sess, features, eval)
!eval_condition(mi.meta_item().unwrap(), sess, features, eval)
}
sym::target => {
if let Some(features) = features
@ -1051,10 +1051,10 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
MetaItemKind::List(nested_items) => {
if meta_item.has_name(sym::align) {
recognised = true;
if nested_items.len() == 1 {
if let [nested_item] = nested_items.as_slice() {
sess.dcx().emit_err(
session_diagnostics::IncorrectReprFormatExpectInteger {
span: nested_items[0].span(),
span: nested_item.span(),
},
);
} else {
@ -1066,10 +1066,10 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
}
} else if meta_item.has_name(sym::packed) {
recognised = true;
if nested_items.len() == 1 {
if let [nested_item] = nested_items.as_slice() {
sess.dcx().emit_err(
session_diagnostics::IncorrectReprFormatPackedExpectInteger {
span: nested_items[0].span(),
span: nested_item.span(),
},
);
} else {

View file

@ -206,8 +206,8 @@ impl OutlivesSuggestionBuilder {
// If there is exactly one suggestable constraints, then just suggest it. Otherwise, emit a
// list of diagnostics.
let mut diag = if suggested.len() == 1 {
mbcx.dcx().struct_help(match suggested.last().unwrap() {
let mut diag = if let [constraint] = suggested.as_slice() {
mbcx.dcx().struct_help(match constraint {
SuggestedConstraint::Outlives(a, bs) => {
let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect();
format!("add bound `{a}: {}`", bs.join(" + "))

View file

@ -745,10 +745,9 @@ fn expand_preparsed_asm(
unused_operands.push((args.operands[idx].1, msg));
}
}
match unused_operands.len() {
0 => {}
1 => {
let (sp, msg) = unused_operands.into_iter().next().unwrap();
match unused_operands[..] {
[] => {}
[(sp, msg)] => {
ecx.dcx()
.struct_span_err(sp, msg)
.with_span_label(sp, msg)

View file

@ -378,8 +378,8 @@ impl BlockOrExpr {
None => cx.expr_block(cx.block(span, ThinVec::new())),
Some(expr) => expr,
}
} else if self.0.len() == 1
&& let ast::StmtKind::Expr(expr) = &self.0[0].kind
} else if let [stmt] = self.0.as_slice()
&& let ast::StmtKind::Expr(expr) = &stmt.kind
&& self.1.is_none()
{
// There's only a single statement expression. Pull it out.
@ -1273,7 +1273,7 @@ impl<'a> MethodDef<'a> {
}
FieldlessVariantsStrategy::Default => (),
}
} else if variants.len() == 1 {
} else if let [variant] = variants.as_slice() {
// If there is a single variant, we don't need an operation on
// the discriminant(s). Just use the most degenerate result.
return self.call_substructure_method(
@ -1281,7 +1281,7 @@ impl<'a> MethodDef<'a> {
trait_,
type_ident,
nonselflike_args,
&EnumMatching(0, &variants[0], Vec::new()),
&EnumMatching(0, variant, Vec::new()),
);
}
}

View file

@ -180,8 +180,8 @@ fn make_format_args(
Ok((mut err, suggested)) => {
if !suggested {
if let ExprKind::Block(block, None) = &efmt.kind
&& block.stmts.len() == 1
&& let StmtKind::Expr(expr) = &block.stmts[0].kind
&& let [stmt] = block.stmts.as_slice()
&& let StmtKind::Expr(expr) = &stmt.kind
&& let ExprKind::Path(None, path) = &expr.kind
&& path.is_potential_trivial_const_arg()
{
@ -196,8 +196,8 @@ fn make_format_args(
} else {
let sugg_fmt = match args.explicit_args().len() {
0 => "{}".to_string(),
_ => {
format!("{}{{}}", "{} ".repeat(args.explicit_args().len()))
count => {
format!("{}{{}}", "{} ".repeat(count))
}
};
err.span_suggestion(

View file

@ -203,9 +203,9 @@ impl<T: Eq + Hash + Copy> TransitiveRelation<T> {
/// exists). See `postdom_upper_bound` for details.
pub fn mutual_immediate_postdominator(&self, mut mubs: Vec<T>) -> Option<T> {
loop {
match mubs.len() {
0 => return None,
1 => return Some(mubs[0]),
match mubs[..] {
[] => return None,
[mub] => return Some(mub),
_ => {
let m = mubs.pop().unwrap();
let n = mubs.pop().unwrap();

View file

@ -338,12 +338,11 @@ fn run_compiler(
config.input = input;
true // has input: normal compilation
}
Ok(None) => match matches.free.len() {
0 => false, // no input: we will exit early
1 => panic!("make_input should have provided valid inputs"),
_ => default_early_dcx.early_fatal(format!(
"multiple input filenames provided (first two filenames are `{}` and `{}`)",
matches.free[0], matches.free[1],
Ok(None) => match matches.free.as_slice() {
[] => false, // no input: we will exit early
[_] => panic!("make_input should have provided valid inputs"),
[fst, snd, ..] => default_early_dcx.early_fatal(format!(
"multiple input filenames provided (first two filenames are `{fst}` and `{snd}`)"
)),
},
};
@ -491,34 +490,30 @@ fn make_input(
early_dcx: &EarlyDiagCtxt,
free_matches: &[String],
) -> Result<Option<Input>, ErrorGuaranteed> {
if free_matches.len() == 1 {
let ifile = &free_matches[0];
if ifile == "-" {
let mut src = String::new();
if io::stdin().read_to_string(&mut src).is_err() {
// Immediately stop compilation if there was an issue reading
// the input (for example if the input stream is not UTF-8).
let reported = early_dcx
.early_err("couldn't read from stdin, as it did not contain valid UTF-8");
return Err(reported);
}
if let Ok(path) = env::var("UNSTABLE_RUSTDOC_TEST_PATH") {
let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect(
"when UNSTABLE_RUSTDOC_TEST_PATH is set \
let [ifile] = free_matches else { return Ok(None) };
if ifile == "-" {
let mut src = String::new();
if io::stdin().read_to_string(&mut src).is_err() {
// Immediately stop compilation if there was an issue reading
// the input (for example if the input stream is not UTF-8).
let reported =
early_dcx.early_err("couldn't read from stdin, as it did not contain valid UTF-8");
return Err(reported);
}
if let Ok(path) = env::var("UNSTABLE_RUSTDOC_TEST_PATH") {
let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect(
"when UNSTABLE_RUSTDOC_TEST_PATH is set \
UNSTABLE_RUSTDOC_TEST_LINE also needs to be set",
);
let line = isize::from_str_radix(&line, 10)
.expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number");
let file_name = FileName::doc_test_source_code(PathBuf::from(path), line);
Ok(Some(Input::Str { name: file_name, input: src }))
} else {
Ok(Some(Input::Str { name: FileName::anon_source_code(&src), input: src }))
}
);
let line = isize::from_str_radix(&line, 10)
.expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number");
let file_name = FileName::doc_test_source_code(PathBuf::from(path), line);
Ok(Some(Input::Str { name: file_name, input: src }))
} else {
Ok(Some(Input::File(PathBuf::from(ifile))))
Ok(Some(Input::Str { name: FileName::anon_source_code(&src), input: src }))
}
} else {
Ok(None)
Ok(Some(Input::File(PathBuf::from(ifile))))
}
}

View file

@ -226,17 +226,17 @@ pub trait Emitter: Translate {
) {
if let Some((sugg, rest)) = suggestions.split_first() {
let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap();
if rest.is_empty() &&
if rest.is_empty()
// ^ if there is only one suggestion
// don't display multi-suggestions as labels
sugg.substitutions.len() == 1 &&
&& let [substitution] = sugg.substitutions.as_slice()
// don't display multipart suggestions as labels
sugg.substitutions[0].parts.len() == 1 &&
&& let [part] = substitution.parts.as_slice()
// don't display long messages as labels
msg.split_whitespace().count() < 10 &&
&& msg.split_whitespace().count() < 10
// don't display multiline suggestions as labels
!sugg.substitutions[0].parts[0].snippet.contains('\n') &&
![
&& !part.snippet.contains('\n')
&& ![
// when this style is set we want the suggestion to be a message, not inline
SuggestionStyle::HideCodeAlways,
// trivial suggestion for tooling's sake, never shown
@ -245,8 +245,8 @@ pub trait Emitter: Translate {
SuggestionStyle::ShowAlways,
].contains(&sugg.style)
{
let substitution = &sugg.substitutions[0].parts[0].snippet.trim();
let msg = if substitution.is_empty() || sugg.style.hide_inline() {
let snippet = part.snippet.trim();
let msg = if snippet.is_empty() || sugg.style.hide_inline() {
// This substitution is only removal OR we explicitly don't want to show the
// code inline (`hide_inline`). Therefore, we don't show the substitution.
format!("help: {msg}")
@ -255,19 +255,18 @@ pub trait Emitter: Translate {
format!(
"help: {}{}: `{}`",
msg,
if self.source_map().is_some_and(|sm| is_case_difference(
sm,
substitution,
sugg.substitutions[0].parts[0].span,
)) {
if self
.source_map()
.is_some_and(|sm| is_case_difference(sm, snippet, part.span,))
{
" (notice the capitalization)"
} else {
""
},
substitution,
snippet,
)
};
primary_span.push_span_label(sugg.substitutions[0].parts[0].span, msg);
primary_span.push_span_label(part.span, msg);
// We return only the modified primary_span
suggestions.clear();

View file

@ -2024,11 +2024,11 @@ pub fn a_or_an(s: &str) -> &'static str {
///
/// Take a list ["a", "b", "c"] and output a display friendly version "a, b and c"
pub fn display_list_with_comma_and<T: std::fmt::Display>(v: &[T]) -> String {
match v.len() {
0 => "".to_string(),
1 => v[0].to_string(),
2 => format!("{} and {}", v[0], v[1]),
_ => format!("{}, {}", v[0], display_list_with_comma_and(&v[1..])),
match v {
[] => "".to_string(),
[a] => a.to_string(),
[a, b] => format!("{a} and {b}"),
[a, v @ ..] => format!("{a}, {}", display_list_with_comma_and(v)),
}
}

View file

@ -1306,12 +1306,12 @@ pub fn parse_macro_name_and_helper_attrs(
// that it's of the form `#[proc_macro_derive(Foo)]` or
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
let list = attr.meta_item_list()?;
if list.len() != 1 && list.len() != 2 {
let ([trait_attr] | [trait_attr, _]) = list.as_slice() else {
dcx.emit_err(errors::AttrNoArguments { span: attr.span });
return None;
}
let Some(trait_attr) = list[0].meta_item() else {
dcx.emit_err(errors::NotAMetaItem { span: list[0].span() });
};
let Some(trait_attr) = trait_attr.meta_item() else {
dcx.emit_err(errors::NotAMetaItem { span: trait_attr.span() });
return None;
};
let trait_ident = match trait_attr.ident() {

View file

@ -2743,15 +2743,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else if let ty::RawPtr(ptr_ty, _) = expr_t.kind()
&& let ty::Adt(adt_def, _) = ptr_ty.kind()
&& let ExprKind::Field(base_expr, _) = expr.kind
&& adt_def.variants().len() == 1
&& adt_def
.variants()
.iter()
.next()
.unwrap()
.fields
.iter()
.any(|f| f.ident(self.tcx) == field)
&& let [variant] = &adt_def.variants().raw
&& variant.fields.iter().any(|f| f.ident(self.tcx) == field)
{
err.multipart_suggestion(
"to access the field, dereference first",

View file

@ -654,17 +654,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
traits::upcast_choices(self.tcx, source_trait_ref, target_trait_def_id);
// must be exactly one trait ref or we'd get an ambig error etc
if upcast_trait_refs.len() != 1 {
let [upcast_trait_ref] = upcast_trait_refs.as_slice() else {
span_bug!(
self.span,
"cannot uniquely upcast `{:?}` to `{:?}`: `{:?}`",
source_trait_ref,
target_trait_def_id,
upcast_trait_refs
);
}
)
};
upcast_trait_refs.into_iter().next().unwrap()
*upcast_trait_ref
}
fn instantiate_binder_with_fresh_vars<T>(&self, value: ty::Binder<'tcx, T>) -> T

View file

@ -321,19 +321,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
};
let suggest_for_privacy =
|err: &mut Diag<'_>, mut msg: String, sugg: Vec<String>| {
if sugg.len() == 1 {
let msg = format!("\
|err: &mut Diag<'_>, mut msg: String, suggs: Vec<String>| {
if let [sugg] = suggs.as_slice() {
err.help(format!("\
trait `{}` provides `{item_name}` is implemented but not reachable",
sugg[0].trim()
);
err.help(msg);
sugg.trim(),
));
} else {
msg += &format!(" but {} not reachable", pluralize!("is", sugg.len()));
msg += &format!(" but {} not reachable", pluralize!("is", suggs.len()));
err.span_suggestions(
span,
msg,
sugg,
suggs,
Applicability::MaybeIncorrect,
);
}
@ -2988,11 +2987,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
if local_spans.primary_span().is_some() {
let msg = if local_preds.len() == 1 {
let msg = if let [local_pred] = local_preds.as_slice() {
format!(
"an implementation of `{}` might be missing for `{}`",
local_preds[0].trait_ref.print_trait_sugared(),
local_preds[0].self_ty()
local_pred.trait_ref.print_trait_sugared(),
local_pred.self_ty()
)
} else {
format!(
@ -3034,11 +3033,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
if foreign_spans.primary_span().is_some() {
let msg = if foreign_preds.len() == 1 {
let msg = if let [foreign_pred] = foreign_preds.as_slice() {
format!(
"the foreign item type `{}` doesn't implement `{}`",
foreign_preds[0].self_ty(),
foreign_preds[0].trait_ref.print_trait_sugared()
foreign_pred.self_ty(),
foreign_pred.trait_ref.print_trait_sugared()
)
} else {
format!(
@ -3388,26 +3387,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
self.suggest_use_candidates(candidates, |accessible_sugg, inaccessible_sugg, span| {
let suggest_for_access = |err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| {
let suggest_for_access = |err: &mut Diag<'_>, mut msg: String, suggs: Vec<_>| {
msg += &format!(
"; perhaps you want to import {one_of}",
one_of = if sugg.len() == 1 { "it" } else { "one of them" },
one_of = if suggs.len() == 1 { "it" } else { "one of them" },
);
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
err.span_suggestions(span, msg, suggs, Applicability::MaybeIncorrect);
};
let suggest_for_privacy = |err: &mut Diag<'_>, sugg: Vec<String>| {
let suggest_for_privacy = |err: &mut Diag<'_>, suggs: Vec<String>| {
let msg = format!(
"{this_trait_is} implemented but not reachable",
this_trait_is = if sugg.len() == 1 {
format!("trait `{}` which provides `{item_name}` is", sugg[0].trim())
this_trait_is = if let [sugg] = suggs.as_slice() {
format!("trait `{}` which provides `{item_name}` is", sugg.trim())
} else {
format!("the following traits which provide `{item_name}` are")
}
);
if sugg.len() == 1 {
if suggs.len() == 1 {
err.help(msg);
} else {
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
err.span_suggestions(span, msg, suggs, Applicability::MaybeIncorrect);
}
};
if accessible_sugg.is_empty() {

View file

@ -527,11 +527,11 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
// Lint for constants that look like binding identifiers (#7526)
if let PatKind::Path(hir::QPath::Resolved(None, path)) = p.kind {
if let Res::Def(DefKind::Const, _) = path.res {
if path.segments.len() == 1 {
if let [segment] = path.segments {
NonUpperCaseGlobals::check_upper_case(
cx,
"constant in pattern",
&path.segments[0].ident,
&segment.ident,
);
}
}

View file

@ -211,15 +211,12 @@ fn lint_overflowing_range_endpoint<'tcx>(
if !is_range_literal(struct_expr) {
return false;
};
let ExprKind::Struct(_, eps, _) = &struct_expr.kind else { return false };
if eps.len() != 2 {
return false;
}
let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { return false };
// We can suggest using an inclusive range
// (`..=`) instead only if it is the `end` that is
// overflowing and only by 1.
if !(eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max) {
if !(end.expr.hir_id == expr.hir_id && lit_val - 1 == max) {
return false;
};
@ -232,7 +229,7 @@ fn lint_overflowing_range_endpoint<'tcx>(
};
let sub_sugg = if expr.span.lo() == lit_span.lo() {
let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false };
let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false };
UseInclusiveRange::WithoutParen {
sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()),
start,

View file

@ -808,7 +808,7 @@ trait UnusedDelimLint {
return;
}
let spans = match value.kind {
ast::ExprKind::Block(ref block, None) if block.stmts.len() == 1 => block.stmts[0]
ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => stmt
.span
.find_ancestor_inside(value.span)
.map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))),
@ -1544,14 +1544,12 @@ impl UnusedImportBraces {
}
// Trigger the lint only if there is one nested item
if items.len() != 1 {
return;
}
let [(tree, _)] = items.as_slice() else { return };
// Trigger the lint if the nested item is a non-self single item
let node_name = match items[0].0.kind {
let node_name = match tree.kind {
ast::UseTreeKind::Simple(rename) => {
let orig_ident = items[0].0.prefix.segments.last().unwrap().ident;
let orig_ident = tree.prefix.segments.last().unwrap().ident;
if orig_ident.name == kw::SelfLower {
return;
}

View file

@ -483,9 +483,11 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
// Check if the match is exhaustive.
let witnesses = report.non_exhaustiveness_witnesses;
if !witnesses.is_empty() {
if source == hir::MatchSource::ForLoopDesugar && arms.len() == 2 {
if source == hir::MatchSource::ForLoopDesugar
&& let [_, snd_arm] = *arms
{
// the for loop pattern is not irrefutable
let pat = &self.thir[arms[1]].pattern;
let pat = &self.thir[snd_arm].pattern;
// `pat` should be `Some(<pat_field>)` from a desugared for loop.
debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop));
let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() };

View file

@ -350,8 +350,8 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
// An inline asm terminator can normally be chained, except when it diverges or uses asm
// goto.
InlineAsm { ref targets, .. } => {
if targets.len() == 1 {
CoverageSuccessors::Chainable(targets[0])
if let [target] = targets[..] {
CoverageSuccessors::Chainable(target)
} else {
CoverageSuccessors::NotChainable(targets)
}

View file

@ -309,11 +309,11 @@ fn verify_candidate_branch<'tcx>(
) -> bool {
// In order for the optimization to be correct, the branch must...
// ...have exactly one statement
if branch.statements.len() != 1 {
let [statement] = branch.statements.as_slice() else {
return false;
}
};
// ...assign the discriminant of `place` in that statement
let StatementKind::Assign(boxed) = &branch.statements[0].kind else { return false };
let StatementKind::Assign(boxed) = &statement.kind else { return false };
let (discr_place, Rvalue::Discriminant(from_place)) = &**boxed else { return false };
if *from_place != place {
return false;

View file

@ -264,9 +264,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
};
// It's definitely not a clone if there are multiple arguments
if args.len() != 1 {
return;
}
let [arg] = &args[..] else { return };
let Some(destination_block) = *target else { return };
@ -280,7 +278,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> {
// These types are easily available from locals, so check that before
// doing DefId lookups to figure out what we're actually calling.
let arg_ty = args[0].node.ty(self.local_decls, self.tcx);
let arg_ty = arg.node.ty(self.local_decls, self.tcx);
let ty::Ref(_region, inner_ty, Mutability::Not) = *arg_ty.kind() else { return };

View file

@ -898,8 +898,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.param_env,
adt_def.non_enum_variant().fields[field].ty(self.tcx, args),
);
if fields.len() == 1 {
let src_ty = fields.raw[0].ty(self.body, self.tcx);
if let [field] = fields.raw.as_slice() {
let src_ty = field.ty(self.body, self.tcx);
if !self.mir_assign_valid_types(src_ty, dest_ty) {
self.fail(location, "union field has the wrong type");
}
@ -967,11 +967,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.fail(location, "RawPtr should be in runtime MIR only");
}
if fields.len() != 2 {
self.fail(location, "raw pointer aggregate must have 2 fields");
} else {
let data_ptr_ty = fields.raw[0].ty(self.body, self.tcx);
let metadata_ty = fields.raw[1].ty(self.body, self.tcx);
if let [data_ptr, metadata] = fields.raw.as_slice() {
let data_ptr_ty = data_ptr.ty(self.body, self.tcx);
let metadata_ty = metadata.ty(self.body, self.tcx);
if let ty::RawPtr(in_pointee, in_mut) = data_ptr_ty.kind() {
if *in_mut != mutability {
self.fail(location, "input and output mutability must match");
@ -998,6 +996,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.fail(location, "metadata for pointer-to-thin must be unit");
}
}
} else {
self.fail(location, "raw pointer aggregate must have 2 fields");
}
}
},

View file

@ -694,12 +694,12 @@ impl<'a> Parser<'a> {
// `foo: `
ExprKind::Path(None, ast::Path { segments, .. }),
token::Ident(kw::For | kw::Loop | kw::While, IdentIsRaw::No),
) if segments.len() == 1 => {
) if let [segment] = segments.as_slice() => {
let snapshot = self.create_snapshot_for_diagnostic();
let label = Label {
ident: Ident::from_str_and_span(
&format!("'{}", segments[0].ident),
segments[0].ident.span,
&format!("'{}", segment.ident),
segment.ident.span,
),
};
match self.parse_expr_labeled(label, false) {

View file

@ -471,9 +471,8 @@ impl<'a> Parser<'a> {
Err(mut err) => {
// Maybe the user misspelled `macro_rules` (issue #91227)
if self.token.is_ident()
&& path.segments.len() == 1
&& edit_distance("macro_rules", &path.segments[0].ident.to_string(), 2)
.is_some()
&& let [segment] = path.segments.as_slice()
&& edit_distance("macro_rules", &segment.ident.to_string(), 2).is_some()
{
err.span_suggestion(
path.span,

View file

@ -826,7 +826,8 @@ impl<'a> Parser<'a> {
// We can only resolve single-segment paths at the moment, because multi-segment paths
// require type-checking: see `visit_generic_arg` in `src/librustc_resolve/late.rs`.
ast::ExprKind::Path(None, path)
if path.segments.len() == 1 && path.segments[0].args.is_none() =>
if let [segment] = path.segments.as_slice()
&& segment.args.is_none() =>
{
true
}

View file

@ -676,7 +676,7 @@ impl<'a> Parser<'a> {
match &expr.kind {
ExprKind::Path(None, ast::Path { segments, .. })
if segments.len() == 1 =>
if let [segment] = segments.as_slice() =>
{
if self.token == token::Colon
&& self.look_ahead(1, |token| {
@ -693,8 +693,8 @@ impl<'a> Parser<'a> {
let snapshot = self.create_snapshot_for_diagnostic();
let label = Label {
ident: Ident::from_str_and_span(
&format!("'{}", segments[0].ident),
segments[0].ident.span,
&format!("'{}", segment.ident),
segment.ident.span,
),
};
match self.parse_expr_labeled(label, false) {

View file

@ -2213,8 +2213,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
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 [meta] = meta.as_slice()
&& let Some(item) = meta.meta_item()
&& let MetaItemKind::NameValue(_) = &item.kind
&& item.path == sym::reason
{

View file

@ -19,9 +19,7 @@ impl DebuggerVisualizerCollector<'_> {
return;
};
let hint = if hints.len() == 1 {
&hints[0]
} else {
let [hint] = hints.as_slice() else {
self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span });
return;
};

View file

@ -5,6 +5,7 @@
// tidy-alphabetical-start
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![cfg_attr(feature = "rustc", feature(let_chains))]
// tidy-alphabetical-end
pub mod constructor;

View file

@ -23,6 +23,7 @@ use crate::constructor::{
};
use crate::lints::lint_nonexhaustive_missing_variants;
use crate::pat_column::PatternColumn;
use crate::rustc::print::EnumInfo;
use crate::usefulness::{compute_match_usefulness, PlaceValidity};
use crate::{errors, Captures, PatCx, PrivateUninhabitedField};
@ -824,77 +825,64 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
use print::{FieldPat, Pat, PatKind};
let cx = self;
let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild);
let mut subpatterns = pat.iter_fields().map(|p| Box::new(cx.hoist_witness_pat(p)));
let hoist = |p| Box::new(cx.hoist_witness_pat(p));
let kind = match pat.ctor() {
Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) },
IntRange(range) => return self.hoist_pat_range(range, *pat.ty()),
Struct | Variant(_) | UnionField => match pat.ty().kind() {
ty::Tuple(..) => PatKind::Leaf {
subpatterns: subpatterns
.enumerate()
.map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern })
.collect(),
},
ty::Adt(adt_def, _) if adt_def.is_box() => {
// Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
// of `std`). So this branch is only reachable when the feature is enabled and
// the pattern is a box pattern.
PatKind::Deref { subpattern: subpatterns.next().unwrap() }
}
ty::Adt(adt_def, _args) => {
let variant_index = RustcPatCtxt::variant_index_for_adt(&pat.ctor(), *adt_def);
let subpatterns = subpatterns
.enumerate()
.map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern })
.collect();
if adt_def.is_enum() {
PatKind::Variant { adt_def: *adt_def, variant_index, subpatterns }
} else {
PatKind::Leaf { subpatterns }
}
}
_ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()),
},
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
// be careful to reconstruct the correct constant pattern here. However a string
// literal pattern will never be reported as a non-exhaustiveness witness, so we
// ignore this issue.
Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
Slice(slice) => {
match slice.kind {
SliceKind::FixedLen(_) => PatKind::Slice {
prefix: subpatterns.collect(),
slice: None,
suffix: Box::new([]),
Struct if pat.ty().is_box() => {
// Outside of the `alloc` crate, the only way to create a struct pattern
// of type `Box` is to use a `box` pattern via #[feature(box_patterns)].
PatKind::Box { subpattern: hoist(&pat.fields[0]) }
}
Struct | Variant(_) | UnionField => {
let enum_info = match *pat.ty().kind() {
ty::Adt(adt_def, _) if adt_def.is_enum() => EnumInfo::Enum {
adt_def,
variant_index: RustcPatCtxt::variant_index_for_adt(pat.ctor(), adt_def),
},
SliceKind::VarLen(prefix, _) => {
let mut subpatterns = subpatterns.peekable();
let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect();
if slice.array_len.is_some() {
// Improves diagnostics a bit: if the type is a known-size array, instead
// of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`.
// This is incorrect if the size is not known, since `[_, ..]` captures
// arrays of lengths `>= 1` whereas `[..]` captures any length.
while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) {
prefix.pop();
}
while subpatterns.peek().is_some()
&& is_wildcard(subpatterns.peek().unwrap())
{
subpatterns.next();
}
}
let suffix: Box<[_]> = subpatterns.collect();
let wild = Pat { ty: pat.ty().inner(), kind: PatKind::Wild };
PatKind::Slice {
prefix: prefix.into_boxed_slice(),
slice: Some(Box::new(wild)),
suffix,
}
ty::Adt(..) | ty::Tuple(..) => EnumInfo::NotEnum,
_ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()),
};
let subpatterns = pat
.iter_fields()
.enumerate()
.map(|(i, pat)| FieldPat { field: FieldIdx::new(i), pattern: hoist(pat) })
.collect::<Vec<_>>();
PatKind::StructLike { enum_info, subpatterns }
}
Ref => PatKind::Deref { subpattern: hoist(&pat.fields[0]) },
Slice(slice) => {
let (prefix_len, has_dot_dot) = match slice.kind {
SliceKind::FixedLen(len) => (len, false),
SliceKind::VarLen(prefix_len, _) => (prefix_len, true),
};
let (mut prefix, mut suffix) = pat.fields.split_at(prefix_len);
// If the pattern contains a `..`, but is applied to values of statically-known
// length (arrays), then we can slightly simplify diagnostics by merging any
// adjacent wildcard patterns into the `..`: `[x, _, .., _, y]` => `[x, .., y]`.
// (This simplification isn't allowed for slice values, because in that case
// `[x, .., y]` would match some slices that `[x, _, .., _, y]` would not.)
if has_dot_dot && slice.array_len.is_some() {
while let [rest @ .., last] = prefix
&& would_print_as_wildcard(cx.tcx, last)
{
prefix = rest;
}
while let [first, rest @ ..] = suffix
&& would_print_as_wildcard(cx.tcx, first)
{
suffix = rest;
}
}
let prefix = prefix.iter().map(hoist).collect();
let suffix = suffix.iter().map(hoist).collect();
PatKind::Slice { prefix, has_dot_dot, suffix }
}
&Str(value) => PatKind::Constant { value },
Never if self.tcx.features().never_patterns => PatKind::Never,
@ -912,6 +900,22 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
}
}
/// Returns `true` if the given pattern would be printed as a wildcard (`_`).
fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool {
match p.ctor() {
Constructor::IntRange(IntRange {
lo: MaybeInfiniteInt::NegInfinity,
hi: MaybeInfiniteInt::PosInfinity,
})
| Constructor::Wildcard
| Constructor::NonExhaustive
| Constructor::Hidden
| Constructor::PrivateUninhabited => true,
Constructor::Never if !tcx.features().never_patterns => true,
_ => false,
}
}
impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
type Ty = RevealedTy<'tcx>;
type Error = ErrorGuaranteed;

View file

@ -12,7 +12,7 @@
use std::fmt;
use rustc_middle::thir::PatRange;
use rustc_middle::ty::{self, AdtDef, Ty};
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
use rustc_middle::{bug, mir};
use rustc_span::sym;
use rustc_target::abi::{FieldIdx, VariantIdx};
@ -33,14 +33,13 @@ pub(crate) struct Pat<'tcx> {
pub(crate) enum PatKind<'tcx> {
Wild,
Variant {
adt_def: AdtDef<'tcx>,
variant_index: VariantIdx,
StructLike {
enum_info: EnumInfo<'tcx>,
subpatterns: Vec<FieldPat<'tcx>>,
},
Leaf {
subpatterns: Vec<FieldPat<'tcx>>,
Box {
subpattern: Box<Pat<'tcx>>,
},
Deref {
@ -55,7 +54,9 @@ pub(crate) enum PatKind<'tcx> {
Slice {
prefix: Box<[Box<Pat<'tcx>>]>,
slice: Option<Box<Pat<'tcx>>>,
/// True if this slice-like pattern should include a `..` between the
/// prefix and suffix.
has_dot_dot: bool,
suffix: Box<[Box<Pat<'tcx>>]>,
},
@ -64,130 +65,155 @@ pub(crate) enum PatKind<'tcx> {
impl<'tcx> fmt::Display for Pat<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Printing lists is a chore.
let mut first = true;
let mut start_or_continue = |s| {
if first {
first = false;
""
} else {
s
}
};
let mut start_or_comma = || start_or_continue(", ");
match self.kind {
PatKind::Wild => write!(f, "_"),
PatKind::Never => write!(f, "!"),
PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => {
let variant_and_name = match self.kind {
PatKind::Variant { adt_def, variant_index, .. } => ty::tls::with(|tcx| {
let variant = adt_def.variant(variant_index);
let adt_did = adt_def.did();
let name = if tcx.get_diagnostic_item(sym::Option) == Some(adt_did)
|| tcx.get_diagnostic_item(sym::Result) == Some(adt_did)
{
variant.name.to_string()
} else {
format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name)
};
Some((variant, name))
}),
_ => self.ty.ty_adt_def().and_then(|adt_def| {
if !adt_def.is_enum() {
ty::tls::with(|tcx| {
Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did())))
})
} else {
None
}
}),
};
if let Some((variant, name)) = &variant_and_name {
write!(f, "{name}")?;
// Only for Adt we can have `S {...}`,
// which we handle separately here.
if variant.ctor.is_none() {
write!(f, " {{ ")?;
let mut printed = 0;
for p in subpatterns {
if let PatKind::Wild = p.pattern.kind {
continue;
}
let name = variant.fields[p.field].name;
write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?;
printed += 1;
}
let is_union = self.ty.ty_adt_def().is_some_and(|adt| adt.is_union());
if printed < variant.fields.len() && (!is_union || printed == 0) {
write!(f, "{}..", start_or_comma())?;
}
return write!(f, " }}");
}
}
let num_fields =
variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len());
if num_fields != 0 || variant_and_name.is_none() {
write!(f, "(")?;
for i in 0..num_fields {
write!(f, "{}", start_or_comma())?;
// Common case: the field is where we expect it.
if let Some(p) = subpatterns.get(i) {
if p.field.index() == i {
write!(f, "{}", p.pattern)?;
continue;
}
}
// Otherwise, we have to go looking for it.
if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) {
write!(f, "{}", p.pattern)?;
} else {
write!(f, "_")?;
}
}
write!(f, ")")?;
}
Ok(())
}
PatKind::Deref { ref subpattern } => {
match self.ty.kind() {
ty::Adt(def, _) if def.is_box() => write!(f, "box ")?,
ty::Ref(_, _, mutbl) => {
write!(f, "&{}", mutbl.prefix_str())?;
}
_ => bug!("{} is a bad Deref pattern type", self.ty),
}
write!(f, "{subpattern}")
PatKind::Box { ref subpattern } => write!(f, "box {subpattern}"),
PatKind::StructLike { ref enum_info, ref subpatterns } => {
ty::tls::with(|tcx| write_struct_like(f, tcx, self.ty, enum_info, subpatterns))
}
PatKind::Deref { ref subpattern } => write_ref_like(f, self.ty, subpattern),
PatKind::Constant { value } => write!(f, "{value}"),
PatKind::Range(ref range) => write!(f, "{range}"),
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
write!(f, "[")?;
for p in prefix.iter() {
write!(f, "{}{}", start_or_comma(), p)?;
}
if let Some(ref slice) = *slice {
write!(f, "{}", start_or_comma())?;
match slice.kind {
PatKind::Wild => {}
_ => write!(f, "{slice}")?,
}
write!(f, "..")?;
}
for p in suffix.iter() {
write!(f, "{}{}", start_or_comma(), p)?;
}
write!(f, "]")
PatKind::Slice { ref prefix, has_dot_dot, ref suffix } => {
write_slice_like(f, prefix, has_dot_dot, suffix)
}
}
}
}
/// Returns a closure that will return `""` when called the first time,
/// and then return `", "` when called any subsequent times.
/// Useful for printing comma-separated lists.
fn start_or_comma() -> impl FnMut() -> &'static str {
let mut first = true;
move || {
if first {
first = false;
""
} else {
", "
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum EnumInfo<'tcx> {
Enum { adt_def: AdtDef<'tcx>, variant_index: VariantIdx },
NotEnum,
}
fn write_struct_like<'tcx>(
f: &mut impl fmt::Write,
tcx: TyCtxt<'_>,
ty: Ty<'tcx>,
enum_info: &EnumInfo<'tcx>,
subpatterns: &[FieldPat<'tcx>],
) -> fmt::Result {
let variant_and_name = match *enum_info {
EnumInfo::Enum { adt_def, variant_index } => {
let variant = adt_def.variant(variant_index);
let adt_did = adt_def.did();
let name = if tcx.is_diagnostic_item(sym::Option, adt_did)
|| tcx.is_diagnostic_item(sym::Result, adt_did)
{
variant.name.to_string()
} else {
format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name)
};
Some((variant, name))
}
EnumInfo::NotEnum => ty.ty_adt_def().and_then(|adt_def| {
Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did())))
}),
};
let mut start_or_comma = start_or_comma();
if let Some((variant, name)) = &variant_and_name {
write!(f, "{name}")?;
// Only for Adt we can have `S {...}`,
// which we handle separately here.
if variant.ctor.is_none() {
write!(f, " {{ ")?;
let mut printed = 0;
for p in subpatterns {
if let PatKind::Wild = p.pattern.kind {
continue;
}
let name = variant.fields[p.field].name;
write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?;
printed += 1;
}
let is_union = ty.ty_adt_def().is_some_and(|adt| adt.is_union());
if printed < variant.fields.len() && (!is_union || printed == 0) {
write!(f, "{}..", start_or_comma())?;
}
return write!(f, " }}");
}
}
let num_fields = variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len());
if num_fields != 0 || variant_and_name.is_none() {
write!(f, "(")?;
for i in 0..num_fields {
write!(f, "{}", start_or_comma())?;
// Common case: the field is where we expect it.
if let Some(p) = subpatterns.get(i) {
if p.field.index() == i {
write!(f, "{}", p.pattern)?;
continue;
}
}
// Otherwise, we have to go looking for it.
if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) {
write!(f, "{}", p.pattern)?;
} else {
write!(f, "_")?;
}
}
write!(f, ")")?;
}
Ok(())
}
fn write_ref_like<'tcx>(
f: &mut impl fmt::Write,
ty: Ty<'tcx>,
subpattern: &Pat<'tcx>,
) -> fmt::Result {
match ty.kind() {
ty::Ref(_, _, mutbl) => {
write!(f, "&{}", mutbl.prefix_str())?;
}
_ => bug!("{ty} is a bad ref pattern type"),
}
write!(f, "{subpattern}")
}
fn write_slice_like<'tcx>(
f: &mut impl fmt::Write,
prefix: &[Box<Pat<'tcx>>],
has_dot_dot: bool,
suffix: &[Box<Pat<'tcx>>],
) -> fmt::Result {
let mut start_or_comma = start_or_comma();
write!(f, "[")?;
for p in prefix.iter() {
write!(f, "{}{}", start_or_comma(), p)?;
}
if has_dot_dot {
write!(f, "{}..", start_or_comma())?;
}
for p in suffix.iter() {
write!(f, "{}{}", start_or_comma(), p)?;
}
write!(f, "]")
}

View file

@ -46,15 +46,14 @@ pub struct EdgeFilter {
impl EdgeFilter {
pub fn new(test: &str) -> Result<EdgeFilter, Box<dyn Error>> {
let parts: Vec<_> = test.split("->").collect();
if parts.len() != 2 {
Err(format!("expected a filter like `a&b -> c&d`, not `{test}`").into())
} else {
if let [source, target] = *test.split("->").collect::<Vec<_>>() {
Ok(EdgeFilter {
source: DepNodeFilter::new(parts[0]),
target: DepNodeFilter::new(parts[1]),
source: DepNodeFilter::new(source),
target: DepNodeFilter::new(target),
index_to_node: Lock::new(FxHashMap::default()),
})
} else {
Err(format!("expected a filter like `a&b -> c&d`, not `{test}`").into())
}
}

View file

@ -374,7 +374,6 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
root_span,
root_id,
vis,
used: Default::default(),
});
self.r.indeterminate_imports.push(import);
@ -890,8 +889,10 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
span: item.span,
module_path: Vec::new(),
vis,
used: Cell::new(used.then_some(Used::Other)),
});
if used {
self.r.import_use_map.insert(import, Used::Other);
}
self.r.potentially_unused_imports.push(import);
let imported_binding = self.r.import(binding, import);
if parent == self.r.graph_root {
@ -1091,7 +1092,6 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
span,
module_path: Vec::new(),
vis: ty::Visibility::Restricted(CRATE_DEF_ID),
used: Default::default(),
})
};
@ -1256,8 +1256,8 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
span,
module_path: Vec::new(),
vis,
used: Cell::new(Some(Used::Other)),
});
self.r.import_use_map.insert(import, Used::Other);
let import_binding = self.r.import(binding, import);
self.r.define(self.r.graph_root, ident, MacroNS, import_binding);
} else {

View file

@ -381,9 +381,9 @@ impl Resolver<'_, '_> {
for import in self.potentially_unused_imports.iter() {
match import.kind {
_ if import.used.get().is_some()
|| import.vis.is_public()
|| import.span.is_dummy() =>
_ if import.vis.is_public()
|| import.span.is_dummy()
|| self.import_use_map.contains_key(import) =>
{
if let ImportKind::MacroUse { .. } = import.kind {
if !import.span.is_dummy() {

View file

@ -176,7 +176,6 @@ pub(crate) struct ImportData<'a> {
/// The resolution of `module_path`.
pub imported_module: Cell<Option<ModuleOrUniformRoot<'a>>>,
pub vis: ty::Visibility,
pub used: Cell<Option<Used>>,
}
/// All imports are unique and allocated on a same arena,
@ -484,7 +483,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
});
self.record_use(target, dummy_binding, Used::Other);
} else if import.imported_module.get().is_none() {
import.used.set(Some(Used::Other));
self.import_use_map.insert(import, Used::Other);
if let Some(id) = import.id() {
self.used_imports.insert(id);
}
@ -1347,7 +1346,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// module defined by a block).
// Skip if the import is public or was used through non scope-based resolution,
// e.g. through a module-relative path.
if import.used.get() == Some(Used::Other)
if self.import_use_map.get(&import) == Some(&Used::Other)
|| self.effective_visibilities.is_exported(self.local_def_id(id))
{
return false;

View file

@ -445,8 +445,8 @@ impl<'a> PathSource<'a> {
Some(ExprKind::Call(call_expr, _)) => match &call_expr.kind {
// the case of `::some_crate()`
ExprKind::Path(_, path)
if path.segments.len() == 2
&& path.segments[0].ident.name == kw::PathRoot =>
if let [segment, _] = path.segments.as_slice()
&& segment.ident.name == kw::PathRoot =>
{
"external crate"
}
@ -2396,15 +2396,14 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
}
fn future_proof_import(&mut self, use_tree: &UseTree) {
let segments = &use_tree.prefix.segments;
if !segments.is_empty() {
let ident = segments[0].ident;
if let [segment, rest @ ..] = use_tree.prefix.segments.as_slice() {
let ident = segment.ident;
if ident.is_path_segment_keyword() || ident.span.is_rust_2015() {
return;
}
let nss = match use_tree.kind {
UseTreeKind::Simple(..) if segments.len() == 1 => &[TypeNS, ValueNS][..],
UseTreeKind::Simple(..) if rest.is_empty() => &[TypeNS, ValueNS][..],
_ => &[TypeNS],
};
let report_error = |this: &Self, ns| {
@ -4009,16 +4008,15 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
if this.should_report_errs() {
if candidates.is_empty() {
if path.len() == 2 && prefix_path.len() == 1 {
if path.len() == 2
&& let [segment] = prefix_path
{
// Delay to check whether methond name is an associated function or not
// ```
// let foo = Foo {};
// foo::bar(); // possibly suggest to foo.bar();
//```
err.stash(
prefix_path[0].ident.span,
rustc_errors::StashKey::CallAssocMethod,
);
err.stash(segment.ident.span, rustc_errors::StashKey::CallAssocMethod);
} else {
// When there is no suggested imports, we can just emit the error
// and suggestions immediately. Note that we bypass the usually error

View file

@ -650,14 +650,14 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
let typo_sugg = self
.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected)
.to_opt_suggestion();
if path.len() == 1
if let [segment] = path
&& !matches!(source, PathSource::Delegation)
&& self.self_type_is_available()
{
if let Some(candidate) =
self.lookup_assoc_candidate(ident, ns, is_expected, source.is_call())
{
let self_is_available = self.self_value_is_available(path[0].ident.span);
let self_is_available = self.self_value_is_available(segment.ident.span);
// Account for `Foo { field }` when suggesting `self.field` so we result on
// `Foo { field: self.field }`.
let pre = match source {
@ -665,7 +665,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
if expr
.fields
.iter()
.any(|f| f.ident == path[0].ident && f.is_shorthand) =>
.any(|f| f.ident == segment.ident && f.is_shorthand) =>
{
format!("{path_str}: ")
}
@ -1258,8 +1258,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
)
})
.collect();
if targets.len() == 1 {
let target = targets[0];
if let [target] = targets.as_slice() {
return Some(TypoSuggestion::single_item_from_ident(target.0.ident, target.1));
}
}
@ -2105,8 +2104,8 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
filter_fn: &impl Fn(Res) -> bool,
) -> TypoCandidate {
let mut names = Vec::new();
if path.len() == 1 {
let mut ctxt = path.last().unwrap().ident.span.ctxt();
if let [segment] = path {
let mut ctxt = segment.ident.span.ctxt();
// Search in lexical scope.
// Walk backwards up the ribs in scope and collect candidates.

View file

@ -1016,6 +1016,8 @@ pub struct Resolver<'a, 'tcx> {
partial_res_map: NodeMap<PartialRes>,
/// Resolutions for import nodes, which have multiple resolutions in different namespaces.
import_res_map: NodeMap<PerNS<Option<Res>>>,
/// An import will be inserted into this map if it has been used.
import_use_map: FxHashMap<Import<'a>, Used>,
/// Resolutions for labels (node IDs of their corresponding blocks or loops).
label_res_map: NodeMap<NodeId>,
/// Resolutions for lifetimes.
@ -1422,6 +1424,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
pat_span_map: Default::default(),
partial_res_map: Default::default(),
import_res_map: Default::default(),
import_use_map: Default::default(),
label_res_map: Default::default(),
lifetimes_res_map: Default::default(),
extra_lifetime_params_map: Default::default(),
@ -1839,7 +1842,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
/// Test if AmbiguityError ambi is any identical to any one inside ambiguity_errors
fn matches_previous_ambiguity_error(&mut self, ambi: &AmbiguityError<'_>) -> bool {
fn matches_previous_ambiguity_error(&self, ambi: &AmbiguityError<'_>) -> bool {
for ambiguity_error in &self.ambiguity_errors {
// if the span location and ident as well as its span are the same
if ambiguity_error.kind == ambi.kind
@ -1900,10 +1903,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
}
}
let old_used = import.used.get();
let new_used = Some(used);
if new_used > old_used {
import.used.set(new_used);
let old_used = self.import_use_map.entry(import).or_insert(used);
if *old_used < used {
*old_used = used;
}
if let Some(id) = import.id() {
self.used_imports.insert(id);

View file

@ -109,8 +109,8 @@ pub(crate) fn sub_namespace_match(
// `format!("{}", path)`, because that tries to insert
// line-breaks and is slow.
fn fast_print_path(path: &ast::Path) -> Symbol {
if path.segments.len() == 1 {
path.segments[0].ident.name
if let [segment] = path.segments.as_slice() {
segment.ident.name
} else {
let mut path_str = String::with_capacity(64);
for (i, segment) in path.segments.iter().enumerate() {
@ -738,10 +738,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// Possibly apply the macro helper hack
if deleg_impl.is_none()
&& kind == Some(MacroKind::Bang)
&& path.len() == 1
&& path[0].ident.span.ctxt().outer_expn_data().local_inner_macros
&& let [segment] = path.as_slice()
&& segment.ident.span.ctxt().outer_expn_data().local_inner_macros
{
let root = Ident::new(kw::DollarCrate, path[0].ident.span);
let root = Ident::new(kw::DollarCrate, segment.ident.span);
path.insert(0, Segment::from_ident(root));
}

View file

@ -912,16 +912,9 @@ mod parse {
match v {
None => false,
Some(s) => {
let parts = s.split('=').collect::<Vec<_>>();
if parts.len() != 2 {
return false;
}
let crate_name = parts[0].to_string();
let fuel = parts[1].parse::<u64>();
if fuel.is_err() {
return false;
}
*slot = Some((crate_name, fuel.unwrap()));
let [crate_name, fuel] = *s.split('=').collect::<Vec<_>>() else { return false };
let Ok(fuel) = fuel.parse::<u64>() else { return false };
*slot = Some((crate_name.to_string(), fuel));
true
}
}

View file

@ -3149,11 +3149,10 @@ impl Target {
if let Some(a) = o.as_array() {
for o in a {
if let Some(s) = o.as_str() {
let p = s.split('=').collect::<Vec<_>>();
if p.len() == 2 {
let k = p[0].to_string();
let v = p[1].to_string();
base.$key_name.to_mut().push((k.into(), v.into()));
if let [k, v] = *s.split('=').collect::<Vec<_>>() {
base.$key_name
.to_mut()
.push((k.to_string().into(), v.to_string().into()))
}
}
}

View file

@ -17,7 +17,7 @@ pub fn target() -> Target {
llvm_target: macos_llvm_target(arch).into(),
metadata: crate::spec::TargetMetadata {
description: Some("ARM64 macOS (11.0+, Big Sur+)".into()),
tier: Some(2),
tier: Some(1),
host_tools: Some(true),
std: Some(true),
},

View file

@ -944,8 +944,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// The current method call returns `Result<_, ()>`
&& self.can_eq(obligation.param_env, ty, found_ty)
// There's a single argument in the method call and it is a closure
&& args.len() == 1
&& let Some(arg) = args.get(0)
&& let [arg] = args
&& let hir::ExprKind::Closure(closure) = arg.kind
// The closure has a block for its body with no tail expression
&& let body = self.tcx.hir().body(closure.body)

View file

@ -73,10 +73,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
});
let impl_def_id_and_args = if self_match_impls.len() == 1 {
self_match_impls[0]
} else if fuzzy_match_impls.len() == 1 {
fuzzy_match_impls[0]
let impl_def_id_and_args = if let [impl_] = self_match_impls[..] {
impl_
} else if let [impl_] = fuzzy_match_impls[..] {
impl_
} else {
return None;
};

View file

@ -5300,7 +5300,8 @@ impl<'v> Visitor<'v> for FindTypeParam {
match ty.kind {
hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {}
hir::TyKind::Path(hir::QPath::Resolved(None, path))
if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
if let [segment] = path.segments
&& segment.ident.name == self.param =>
{
if !self.nested {
debug!(?ty, "FindTypeParam::visit_ty");

View file

@ -3,7 +3,7 @@
//! suggestions from rustc if you get anything slightly wrong in here, and overall
//! helps with clarity as we're also referring to `char` intentionally in here.
use crate::fmt::{self, Write};
use crate::fmt;
use crate::mem::transmute;
/// One of the 128 Unicode characters from U+0000 through U+007F,
@ -583,9 +583,10 @@ impl fmt::Display for AsciiChar {
#[unstable(feature = "ascii_char", issue = "110998")]
impl fmt::Debug for AsciiChar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[inline]
fn backslash(a: AsciiChar) -> ([AsciiChar; 4], u8) {
([AsciiChar::ReverseSolidus, a, AsciiChar::Null, AsciiChar::Null], 2)
use AsciiChar::{Apostrophe, Null, ReverseSolidus as Backslash};
fn backslash(a: AsciiChar) -> ([AsciiChar; 6], usize) {
([Apostrophe, Backslash, a, Apostrophe, Null, Null], 4)
}
let (buf, len) = match self {
@ -595,24 +596,17 @@ impl fmt::Debug for AsciiChar {
AsciiChar::LineFeed => backslash(AsciiChar::SmallN),
AsciiChar::ReverseSolidus => backslash(AsciiChar::ReverseSolidus),
AsciiChar::Apostrophe => backslash(AsciiChar::Apostrophe),
_ => {
let byte = self.to_u8();
if !byte.is_ascii_control() {
([*self, AsciiChar::Null, AsciiChar::Null, AsciiChar::Null], 1)
} else {
const HEX_DIGITS: [AsciiChar; 16] = *b"0123456789abcdef".as_ascii().unwrap();
_ if self.to_u8().is_ascii_control() => {
const HEX_DIGITS: [AsciiChar; 16] = *b"0123456789abcdef".as_ascii().unwrap();
let hi = HEX_DIGITS[usize::from(byte >> 4)];
let lo = HEX_DIGITS[usize::from(byte & 0xf)];
([AsciiChar::ReverseSolidus, AsciiChar::SmallX, hi, lo], 4)
}
let byte = self.to_u8();
let hi = HEX_DIGITS[usize::from(byte >> 4)];
let lo = HEX_DIGITS[usize::from(byte & 0xf)];
([Apostrophe, Backslash, AsciiChar::SmallX, hi, lo, Apostrophe], 6)
}
_ => ([Apostrophe, *self, Apostrophe, Null, Null, Null], 3),
};
f.write_char('\'')?;
for byte in &buf[..len as usize] {
f.write_str(byte.as_str())?;
}
f.write_char('\'')
f.write_str(buf[..len].as_str())
}
}

View file

@ -502,6 +502,8 @@ impl Waker {
#[must_use]
#[stable(feature = "futures_api", since = "1.36.0")]
pub fn will_wake(&self, other: &Waker) -> bool {
// We optimize this by comparing vtable addresses instead of vtable contents.
// This is permitted since the function is documented as best-effort.
let RawWaker { data: a_data, vtable: a_vtable } = self.waker;
let RawWaker { data: b_data, vtable: b_vtable } = other.waker;
a_data == b_data && ptr::eq(a_vtable, b_vtable)
@ -761,7 +763,11 @@ impl LocalWaker {
#[must_use]
#[unstable(feature = "local_waker", issue = "118959")]
pub fn will_wake(&self, other: &LocalWaker) -> bool {
self.waker == other.waker
// We optimize this by comparing vtable addresses instead of vtable contents.
// This is permitted since the function is documented as best-effort.
let RawWaker { data: a_data, vtable: a_vtable } = self.waker;
let RawWaker { data: b_data, vtable: b_vtable } = other.waker;
a_data == b_data && ptr::eq(a_vtable, b_vtable)
}
/// Creates a new `LocalWaker` from [`RawWaker`].

View file

@ -0,0 +1,28 @@
use core::ascii::Char;
use core::fmt::Write;
/// Tests Display implementation for ascii::Char.
#[test]
fn test_display() {
let want = (0..128u8).map(|b| b as char).collect::<String>();
let mut got = String::with_capacity(128);
for byte in 0..128 {
write!(&mut got, "{}", Char::from_u8(byte).unwrap()).unwrap();
}
assert_eq!(want, got);
}
/// Tests Debug implementation for ascii::Char.
#[test]
fn test_debug_control() {
for byte in 0..128u8 {
let mut want = format!("{:?}", byte as char);
// `char` uses `'\u{#}'` representation where ascii::char uses `'\x##'`.
// Transform former into the latter.
if let Some(rest) = want.strip_prefix("'\\u{") {
want = format!("'\\x{:0>2}'", rest.strip_suffix("}'").unwrap());
}
let chr = core::ascii::Char::from_u8(byte).unwrap();
assert_eq!(want, format!("{chr:?}"), "byte: {byte}");
}
}

View file

@ -122,6 +122,7 @@ mod alloc;
mod any;
mod array;
mod ascii;
mod ascii_char;
mod asserting;
mod async_iter;
mod atomic;

View file

@ -194,6 +194,7 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool {
let supported_platforms = [
// tier 1
("aarch64-unknown-linux-gnu", false),
("aarch64-apple-darwin", false),
("i686-pc-windows-gnu", false),
("i686-pc-windows-msvc", false),
("i686-unknown-linux-gnu", false),
@ -202,7 +203,6 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool {
("x86_64-pc-windows-gnu", true),
("x86_64-pc-windows-msvc", true),
// tier 2 with host tools
("aarch64-apple-darwin", false),
("aarch64-pc-windows-msvc", false),
("aarch64-unknown-linux-musl", false),
("arm-unknown-linux-gnueabi", false),

View file

@ -33,6 +33,7 @@ All tier 1 targets with host tools support the full standard library.
target | notes
-------|-------
`aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1, glibc 2.17+)
[`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+)
`i686-pc-windows-gnu` | 32-bit MinGW (Windows 10+, Windows Server 2016+) [^x86_32-floats-return-ABI]
`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+) [^x86_32-floats-return-ABI]
`i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+) [^x86_32-floats-return-ABI]
@ -86,7 +87,6 @@ so Rustup may install the documentation for a similar tier 1 target instead.
target | notes
-------|-------
[`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+)
`aarch64-pc-windows-msvc` | ARM64 Windows MSVC
`aarch64-unknown-linux-musl` | ARM64 Linux with musl 1.2.3
`arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2, glibc 2.17)

View file

@ -5,9 +5,6 @@ Apple macOS targets.
**Tier: 1**
- `x86_64-apple-darwin`: macOS on 64-bit x86.
**Tier: 2 (with Host Tools)**
- `aarch64-apple-darwin`: macOS on ARM64 (M1-family or later Apple Silicon CPUs).
## Target maintainers

View file

@ -212,7 +212,6 @@ degree documented below):
- All Rust [Tier 1 targets](https://doc.rust-lang.org/rustc/platform-support.html) are supported by
Miri. They are all checked on Miri's CI, and some (at least one per OS) are even checked on every
Rust PR, so the shipped Miri should always work on these targets.
- `aarch64-apple-darwin` is supported.
- `s390x-unknown-linux-gnu` is supported as our "big-endian target of choice".
- For every other target with OS `linux`, `macos`, or `windows`, Miri should generally work, but we
make no promises and we don't run tests for such targets.

View file

@ -137,7 +137,7 @@ case $HOST_TARGET in
MANY_SEEDS=16 TEST_TARGET=x86_64-pc-windows-gnu run_tests
;;
aarch64-apple-darwin)
# Host (tier 2)
# Host
GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
# Extra tier 1
MANY_SEEDS=64 TEST_TARGET=i686-pc-windows-gnu run_tests