Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2282,9 +2282,9 @@ checksum = "803ec87c9cfb29b9d2633f20cba1f488db3fd53f2158b1024cbefb47ba05d413"

[[package]]
name = "libc"
version = "0.2.189"
version = "0.2.191"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"

[[package]]
name = "libffi"
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/diagnostics/region_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ impl RegionName {
RegionNameHighlight::CannotMatchHirTy(span, type_name),
mir_description,
) => {
diag.span_label(*span, format!("return type{mir_description} is {type_name}"));
diag.span_label(*span, format!("return type{mir_description} is `{type_name}`"));
}
RegionNameSource::AnonRegionFromYieldTy(span, type_name) => {
diag.span_label(*span, format!("yield type is {type_name}"));
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ impl<'a> TraitDef<'a> {
if self.is_const && cx.ecfg.features.staged_api() {
let rustc_const_unstable = cx.path_name(self.span, sym::rustc_const_unstable);

// #[rustc_const_unstable(feature = "derive_const", issue = "118304")]
// #[rustc_const_unstable(feature = "derive_const", issue = "143874")]
attrs.push(
cx.attr_nested(
rustc_ast::AttrItem {
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ declare_features! (
(internal, profiler_runtime, "1.18.0", None),
/// Allows using `rustc_*` attributes (RFC 572).
(internal, rustc_attrs, "1.0.0", None),
/// Allows using internal rustdoc features like `doc(keyword)`.
(internal, rustdoc_internals, "1.58.0", None),
/// Allows using the `#[stable]` and `#[unstable]` attributes.
(internal, staged_api, "1.0.0", None),
/// Perma-unstable, only used in the test suite for binders (`for<'a>`).
Expand Down Expand Up @@ -322,8 +324,6 @@ declare_features! (
(internal, repr_simd, "1.4.0", Some(27731)),
/// Allows using compiler's own crates.
(unstable, rustc_private, "1.0.0", Some(27812)),
/// Allows using internal rustdoc features like `doc(keyword)`.
(internal, rustdoc_internals, "1.58.0", Some(90418)),
/// Allows using the `rustdoc::missing_doc_code_examples` lint
(unstable, rustdoc_missing_doc_code_examples, "1.31.0", Some(101730)),
/// Introduces a hierarchy of `Sized` traits (RFC 3729).
Expand Down Expand Up @@ -666,7 +666,7 @@ declare_features! (
/// Allows providing names to parameters of `impl Fn` etc
(unstable, named_fn_trait_parameters, "1.99.0", Some(158499)),
/// Allows specifying the as-needed link modifier
(unstable, native_link_modifiers_as_needed, "1.53.0", Some(81490)),
(unstable, native_link_modifiers_as_needed, "1.53.0", Some(99424)),
/// Allow negative trait implementations.
(unstable, negative_impls, "1.44.0", Some(68318)),
/// Allows the `!` pattern.
Expand Down Expand Up @@ -725,7 +725,7 @@ declare_features! (
/// Allows the use of the `sanitize` attribute.
(unstable, sanitize, "1.91.0", Some(39699)),
/// Allows the use of SIMD types in functions declared in `extern` blocks.
(unstable, simd_ffi, "1.0.0", Some(27731)),
(unstable, simd_ffi, "1.0.0", Some(63068)),
/// Target features on sparc.
(unstable, sparc_target_feature, "1.84.0", Some(132783)),
/// Allows specialization of implementations (RFC 1210).
Expand Down
40 changes: 40 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let mut err = fn_call_diag_ctxt.initial_final_diagnostic();
fn_call_diag_ctxt.suggest_confusable(&mut err);
fn_call_diag_ctxt.detect_missing_turbofish(&mut err);

// As we encounter issues, keep track of what we want to provide for the suggestion.

Expand Down Expand Up @@ -3227,6 +3228,45 @@ impl<'a, 'tcx> ArgMatchingCtxt<'a, 'tcx> {
self.annotate_alternative_method_deref(err, self.call_expr, None);
}

fn detect_missing_turbofish(&self, err: &mut Diag<'_>) {
if self.tcx.dcx().has_errors().is_none() {
return;
}
let mut iter = self.provided_args.iter().peekable();
while let Some(arg) = iter.next() {
let hir::ExprKind::Binary(lt, lt_left, lt_right) = arg.kind else {
continue;
};
let hir::BinOpKind::Lt = lt.node else { continue };
let hir::ExprKind::Path(_) = lt_left.kind else { continue };
let hir::ExprKind::Path(_) = lt_right.kind else { continue };

let Some(mut next) = iter.next() else { break };
loop {
match next.kind {
hir::ExprKind::Binary(gt, gt_left, gt_right)
if let hir::BinOpKind::Gt = gt.node
&& let hir::ExprKind::Path(_) = gt_left.kind
&& let hir::ExprKind::Call(..) = gt_right.kind =>
{
// We've encountered a likely missing turbofish error, for which we've already
// emitted a more targeted diagnostic during name resolution making this one
// redundant (`LateResolutionVisitor::detect_missing_turbofish`).
err.downgrade_to_delayed_bug();
}
hir::ExprKind::Path(_) => {
if let Some(n) = iter.next() {
next = n;
continue;
}
}
_ => {}
}
break;
}
}
}

/// A "softer" version of the `demand_compatible`, which checks types without persisting them,
/// and treats error types differently
/// This will allow us to "probe" for other argument orders that would likely have been correct
Expand Down
15 changes: 9 additions & 6 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2964,9 +2964,14 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
let mut mod_inner_attrs_start = None;
match item.kind {
ItemKind::Mod(..) => {
// We only handle outer doc comments for modules here.
// We only handle outer attributes which can contain intra-doc links (doc comments
// and `deprecated`) for modules here.
let attrs = if let Some(pos) = item.attrs.iter().position(|a| {
a.doc_resolution_scope().is_some_and(|style| style == AttrStyle::Inner)
|| matches!(
a.kind,
AttrKind::Normal(ref n)
if n.item.name() == Some(sym::deprecated) && a.style == AttrStyle::Inner)
}) {
mod_inner_attrs_start = Some(pos);
&item.attrs[..pos]
Expand Down Expand Up @@ -5166,6 +5171,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
}

/// Handles paths that may refer to associated items.
#[tracing::instrument(skip(self, source), level = "debug")]
fn resolve_qpath(
&mut self,
qself: &Option<Box<QSelf>>,
Expand All @@ -5174,11 +5180,6 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
finalize: Finalize,
source: PathSource<'_, 'ast, 'ra>,
) -> Result<Option<PartialRes>, Spanned<ResolutionError<'ra>>> {
debug!(
"resolve_qpath(qself={:?}, path={:?}, ns={:?}, finalize={:?})",
qself, path, ns, finalize,
);

if let Some(qself) = qself {
if qself.position == 0 {
// This is a case like `<T>::B`, where there is no
Expand Down Expand Up @@ -5578,6 +5579,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
self.resolve_expr(arg, None);
}
self.visit_path_segment(seg);
self.detect_missing_turbofish(&args);
}

ExprKind::Call(ref callee, ref arguments) => {
Expand All @@ -5599,6 +5601,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
self.resolve_expr(argument, None);
}
}
self.detect_missing_turbofish(&arguments);
}
ExprKind::FormatArgs(ref args) => {
for arg in args.arguments.all_args() {
Expand Down
164 changes: 164 additions & 0 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1768,6 +1768,170 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
}
}

/// When encountering `Ty < Ty, Ty > call()` in function and method call arguments, suggest
/// using the turbofish and silence redundant resolve errors.
pub(in crate::late) fn detect_missing_turbofish(&mut self, arguments: &'ast [Box<Expr>]) {
if self.use_injections.is_empty() {
// Avoid doing unnecessary work, we would have had resolve errors by now if this mistake
// had happened.
return;
}
let mut iter = arguments.iter().peekable();
// Look through every call argument expression, when encountering `a < b`, try to find a
// closing `c > ::d()` that would make the whole thing be syntactically valid as a
// fully-qualified path `a::<b, c>::d()`.
while let Some(arg) = iter.next() {
// foo(left < right, bar, baz>::qux())
// ^^^^ ^^^^^
let ExprKind::Binary(lt, left, right) = &arg.kind else { continue };
// foo(left < right, bar, baz>::qux())
// ^
let ast::BinOpKind::Lt = lt.node else {
continue;
};
// foo(left < right, bar, baz>::qux())
// ^^^^ is a path
let ExprKind::Path(_, lt_left) = &left.kind else { continue };
// foo(a::B < right, bar, baz>::qux())
// ^ ^ lt_left_segments
let lt_left_segments: Vec<_> = lt_left.segments.iter().map(|seg| seg.into()).collect();
// `a::B` is a type-like.
let lt_res = if let PathResult::NonModule(res) =
self.resolve_path(&lt_left_segments, Some(TypeNS), None, PathSource::Expr(None))
&& let Some(lt_res) = res.full_res()
{
lt_res
} else {
continue;
};

// Same thing we do for `left`, for `right`:
// foo(left < right, bar, baz>::qux())
// ^^^^^ is a path
let ExprKind::Path(_, lt_right) = &right.kind else { continue };
let lt_right_segments: Vec<_> =
lt_right.segments.iter().map(|seg| seg.into()).collect();
if let PathResult::NonModule(res) =
self.resolve_path(&lt_right_segments, Some(TypeNS), None, PathSource::Expr(None))
&& let Some(_res) = res.full_res()
{
} else {
continue;
}

// foo(left < right, bar, baz>::qux())
// next is ^^^ or ^^^^^^^^^^^
let Some(mut next) = iter.next() else {
break;
};
let mut other_path_spans = vec![];
loop {
match &next.kind {
// foo(left < right, bar, baz>::qux())
// ^^^^^^^^^
ExprKind::Binary(gt, gt_left, gt_right)
// foo(left < right, bar, baz>::qux())
// ^
if let ast::BinOpKind::Gt = gt.node
// foo(left < right, bar, baz>::qux())
// ^^^
&& let ExprKind::Path(_, gt_left) = &gt_left.kind
// foo(left < right, bar, baz>::qux())
// ^^^^^^^
&& let ExprKind::Call(call_expr, _) = &gt_right.kind
&& let ExprKind::Path(_, call_path) = &call_expr.kind
&& let gt_left_segments = gt_left
.segments
.iter()
.map(|seg| seg.into())
.collect::<Vec<_>>()
&& let PathResult::NonModule(res) = self.resolve_path(
&gt_left_segments,
Some(TypeNS),
None,
PathSource::Expr(None),
)
&& let Some(_res) = res.full_res() =>
{
// We've encountered an expression where we have `Ty < Ty, Ty > call(...)`.
// A binop between two types is non-sensical in (current?) Rust, and the
// presence of two binops separated by a comma of less-than followed by
// greater-than heavily implies a missing turbofish in an expression.
let mut any = false;
let spans: Vec<Span> = lt_left_segments
.iter()
.map(|s| s.ident.span)
.chain(lt_right_segments.iter().map(|s| s.ident.span))
.chain(gt_left_segments.iter().map(|s| s.ident.span))
.chain(gt_left_segments.iter().map(|s| s.ident.span))
.chain(call_path.segments.iter().map(|seg| seg.span()))
.chain(other_path_spans.iter().cloned())
.collect();
for injection in self.use_injections.iter_mut() {
// Silence all already constructed resolution errors that are contained
// by the likely missing turbofish.
if injection.path.iter().any(|s| spans.contains(&s.ident.span)) {
any = true;
injection.err.downgrade_to_delayed_bug();
}
}
if any {
let sp = MultiSpan::from(vec![lt.span, gt.span]);
let mut err =
self.r.tcx.dcx().struct_span_err(sp, "can't compare two types");
err.span_label(
gt.span,
"these are parsed as \"less than\" and \"greater than\"",
);
let name = if let Some(def_id) = lt_res.opt_def_id()
&& let Some(name) = self.r.tcx.opt_item_name(def_id)
{
name.to_string()
} else {
lt_right_segments
.iter()
.map(|s| s.ident.name.to_string())
.collect::<Vec<String>>()
.join("::")
};
err.span_suggestion_verbose(
lt.span.shrink_to_lo(),
format!(
"you likely intended to write type `{name}` with type parameters, but \
type parameters in expression contexts require the use of the \
\"turbofish\" `::<>`",
),
"::".to_string(),
Applicability::MachineApplicable,
);
err.emit();
}
}

// foo(left < right, bat, bar, baz>::qux())
// ^^^
ExprKind::Path(_, path) => {
// foo(left < right, bat, bar, baz>::qux())
// ^^^ or ^^^^^^^^^^^
if let Some(n) = iter.next() {
next = n;
other_path_spans.extend(path.segments.iter().map(|seg| seg.ident.span));
// Only case where we don't break from the `loop` advancing the parsed
// call arguments , we've encountered a path that is likely one of the
// type parameters for the missing turbofish.
continue;
}
}

_ => {}
}
// This doesn't look like a turbofish, look for more missing turbofishes on the
// following arguments.
break;
}
}
}

fn suggest_at_operator_in_slice_pat_with_range(&self, err: &mut Diag<'_>, path: &[Segment]) {
let Some(pat) = self.diag_metadata.current_pat else { return };
let (bound, side, range) = match &pat.kind {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,7 @@ symbols! {
deref_target,
derive,
derive_const,
derive_const_issue: "118304",
derive_const_issue: "143874",
derive_default_enum,
derive_from,
derive_smart_pointer,
Expand Down
2 changes: 1 addition & 1 deletion library/alloc/src/collections/vec_deque/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl<'a, T> Iter<'a, T> {
///
/// assert_eq!(iter.as_slices(), (&[9, 10][..], &[0, 1][..]));
/// ```
#[unstable(feature = "vec_deque_iter_as_slices", issue = "123947")]
#[unstable(feature = "vec_deque_iter_as_slices", issue = "133253")]
pub fn as_slices(&self) -> (&'a [T], &'a [T]) {
(self.i1.as_slice(), self.i2.as_slice())
}
Expand Down
6 changes: 3 additions & 3 deletions library/alloc/src/collections/vec_deque/iter_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl<'a, T> IterMut<'a, T> {
/// slices.1[0] = 24;
/// assert_eq!(deque.as_slices(), (&[8, 42, 10][..], &[24, 1, 2][..]));
/// ```
#[unstable(feature = "vec_deque_iter_as_slices", issue = "123947")]
#[unstable(feature = "vec_deque_iter_as_slices", issue = "133253")]
pub fn into_slices(self) -> (&'a mut [T], &'a mut [T]) {
(self.i1.into_slice(), self.i2.into_slice())
}
Expand Down Expand Up @@ -86,7 +86,7 @@ impl<'a, T> IterMut<'a, T> {
///
/// assert_eq!(iter.as_slices(), (&[9, 10][..], &[0, 1][..]));
/// ```
#[unstable(feature = "vec_deque_iter_as_slices", issue = "123947")]
#[unstable(feature = "vec_deque_iter_as_slices", issue = "133253")]
pub fn as_slices(&self) -> (&[T], &[T]) {
(self.i1.as_slice(), self.i2.as_slice())
}
Expand Down Expand Up @@ -122,7 +122,7 @@ impl<'a, T> IterMut<'a, T> {
/// iter.as_mut_slices().1[0] = 24;
/// assert_eq!(deque.as_slices(), (&[8, 42, 10][..], &[24, 1, 2][..]));
/// ```
#[unstable(feature = "vec_deque_iter_as_slices", issue = "123947")]
#[unstable(feature = "vec_deque_iter_as_slices", issue = "133253")]
pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) {
(self.i1.as_mut_slice(), self.i2.as_mut_slice())
}
Expand Down
Loading
Loading