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:
commit
c9bd03cb72
57 changed files with 480 additions and 453 deletions
|
@ -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<_>>>()?;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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("{");
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(" + "))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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))))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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!() };
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, "]")
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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`].
|
||||
|
|
28
library/core/tests/ascii_char.rs
Normal file
28
library/core/tests/ascii_char.rs
Normal 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}");
|
||||
}
|
||||
}
|
|
@ -122,6 +122,7 @@ mod alloc;
|
|||
mod any;
|
||||
mod array;
|
||||
mod ascii;
|
||||
mod ascii_char;
|
||||
mod asserting;
|
||||
mod async_iter;
|
||||
mod atomic;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue