Skip to content
Open
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
85 changes: 85 additions & 0 deletions compiler/rustc_parse/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4700,3 +4700,88 @@ pub(crate) struct SuggestIntroduceTypeParameter {
pub span: Span,
pub parameters: String,
}

#[derive(Diagnostic)]
pub(crate) enum CStyleReferenceMut {
#[diag("`mut` must be written after the lifetime")]
WithLifetime {
#[primary_span]
span: Span,
#[subdiagnostic]
sugg: CStyleReferenceMutLifetimeSugg,
},
#[diag("reference types must be written as `&mut T`")]
Plain {
#[primary_span]
span: Span,
#[subdiagnostic]
sugg: CStyleReferenceMutPlainSugg,
},
}

#[derive(Subdiagnostic)]
#[multipart_suggestion("put `mut` after the lifetime", applicability = "machine-applicable")]
pub(crate) struct CStyleReferenceMutLifetimeSugg {
#[suggestion_part(code = "")]
pub remove: Span,
#[suggestion_part(code = " mut")]
pub insert: Span,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion("put the `&` before `mut`", applicability = "machine-applicable")]
pub(crate) struct CStyleReferenceMutPlainSugg {
#[suggestion_part(code = "")]
pub remove: Span,
#[suggestion_part(code = "&")]
pub insert: Span,
}

#[derive(Diagnostic)]
#[diag("reference types must be written as `&T`")]
pub(crate) struct CStyleReference {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sugg: CStyleReferenceSugg,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion("put the `&` before the type", applicability = "machine-applicable")]
pub(crate) struct CStyleReferenceSugg {
#[suggestion_part(code = "")]
pub remove: Span,
#[suggestion_part(code = "&")]
pub insert: Span,
}

#[derive(Diagnostic)]
#[diag("reference types must be written as `&{$mutbl}expr`")]
pub(crate) struct CStyleReferenceExpr {
#[primary_span]
pub span: Span,
pub mutbl: String,
#[subdiagnostic]
pub sugg: Option<CStyleReferenceExprSugg>,
}

#[derive(Subdiagnostic)]
pub(crate) enum CStyleReferenceExprSugg {
#[multipart_suggestion("put the `&` before the `expr`", applicability = "machine-applicable")]
Shared {
#[suggestion_part(code = "")]
removal: Span,
#[suggestion_part(code = "&")]
insert: Span,
},
#[multipart_suggestion(
"put the `&mut` before the `expr`",
applicability = "machine-applicable"
)]
Mut {
#[suggestion_part(code = "")]
removal: Span,
#[suggestion_part(code = "&mut ")]
insert: Span,
},
}
65 changes: 62 additions & 3 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{
self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode,
Block, BlockCheckMode, Expr, ExprKind, GenericArg, GenericArgs, Generics, Item, ItemKind,
Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind,
Block, BlockCheckMode, BorrowKind, Expr, ExprKind, GenericArg, GenericArgs, Generics, Item,
ItemKind, MutTy, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty,
TyKind,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
Expand All @@ -27,7 +28,8 @@ use super::{
};
use crate::diagnostics::{
AddParen, AmbiguousPlus, AsyncMoveBlockIn2015, AsyncUseBlockIn2015, AttributeOnParamType,
AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi,
AwaitSuggestion, BadQPathStage2, BadTypePlus, BadTypePlusSub, CStyleReference,
CStyleReferenceExpr, CStyleReferenceExprSugg, CStyleReferenceSugg, ColonAsSemi,
ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg,
DocCommentDoesNotDocumentAnything, DocCommentOnParamType, DoubleColonInBound,
ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, ExprParenthesesNeeded, FoundPathInGenerics,
Expand Down Expand Up @@ -1593,6 +1595,31 @@ impl<'a> Parser<'a> {
}
}

/// If a user writes `T&` instead of `&T`, this method
/// attempts to recover and provide a helpful error message.
pub(super) fn maybe_recover_from_c_style_reference(&mut self, ty: Box<Ty>) -> Box<Ty> {
if self.token == token::And
&& self.may_recover()
&& !self.look_ahead(1, |t| t.can_begin_type() || t.is_keyword(kw::Mut))
{
self.bump();

let ref_span = self.prev_token.span;

self.dcx().emit_err(CStyleReference {
span: ref_span,
sugg: CStyleReferenceSugg { remove: ref_span, insert: ty.span.shrink_to_lo() },
});

self.mk_ty(
ty.span.to(ref_span),
TyKind::Ref(None, MutTy { ty, mutbl: Mutability::Not }),
)
} else {
ty
}
}

/// Rust has no ternary operator (`cond ? then : else`). Parse it and try
/// to recover from it if `then` and `else` are valid expressions. Returns
/// an err if this appears to be a ternary expression.
Expand Down Expand Up @@ -3187,4 +3214,36 @@ impl<'a> Parser<'a> {
new_error
})
}

pub(super) fn recover_from_c_style_reference(&mut self, lhs: Box<Expr>) -> Box<Expr> {
let ref_span = self.prev_token.span;

let mutbl = self.parse_mutability();
let prefix = mutbl.prefix_str();

let op_span = ref_span.to(self.prev_token.span);
let span = lhs.span.to(op_span);

if matches!(lhs.kind, ExprKind::Binary(..) | ExprKind::Cast(..)) {
self.dcx().emit_err(CStyleReferenceExpr {
span,
mutbl: prefix.to_string(),
sugg: None,
});
return lhs;
}

let insert = lhs.span.shrink_to_lo();
let sugg = match mutbl {
Mutability::Mut => CStyleReferenceExprSugg::Mut { removal: op_span, insert },
Mutability::Not => CStyleReferenceExprSugg::Shared { removal: op_span, insert },
};
self.dcx().emit_err(CStyleReferenceExpr {
span,
mutbl: prefix.to_string(),
sugg: Some(sugg),
});

self.mk_expr(span, ExprKind::AddrOf(BorrowKind::Ref, mutbl, lhs))
}
}
7 changes: 7 additions & 0 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,13 @@ impl<'a> Parser<'a> {
continue;
}

// Look for C-style reference expressions like
// `x&`, `x &mut` and recover
if self.prev_token == token::And && self.may_recover() && !self.token.can_begin_expr() {
lhs = self.recover_from_c_style_reference(lhs);
continue;
}

let op_span = op.span;
let op = op.node;
// Special cases:
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_parse/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1810,6 +1810,12 @@ impl<'a> Parser<'a> {
}
None
}

fn is_c_style_reference_start(&self) -> bool {
self.token.is_keyword(kw::Mut)
&& self.may_recover()
&& self.look_ahead(1, |t| *t == token::And)
}
}

// Metavar captures of various kinds. The more complex node kinds (e.g. `Item`, `Expr`) store
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,7 @@ impl<'a> Parser<'a> {
} else if self.check_const_arg() {
// Parse const argument.
GenericArg::Const(self.parse_const_arg()?)
} else if self.check_type() {
} else if self.check_type() || self.is_c_style_reference_start() {
// Parse type argument.

// Proactively create a parser snapshot enabling us to rewind and try to reparse the
Expand Down
59 changes: 54 additions & 5 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ use thin_vec::{ThinVec, thin_vec};

use super::{Parser, PathStyle, SeqSep, TokenType, Trailing};
use crate::diagnostics::{
self, AttributeOnEmptyType, AttributeOnType, DynAfterMut, ExpectedFnPathFoundFnKeyword,
ExpectedMutOrConstInRawPointerType, FnPtrWithGenerics, FnPtrWithGenericsSugg,
HelpUseLatestEdition, InvalidCVariadicType, InvalidDynKeyword, LifetimeAfterMut,
NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow,
self, AttributeOnEmptyType, AttributeOnType, CStyleReferenceMut,
CStyleReferenceMutLifetimeSugg, CStyleReferenceMutPlainSugg, DynAfterMut,
ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType, FnPtrWithGenerics,
FnPtrWithGenericsSugg, HelpUseLatestEdition, InvalidCVariadicType, InvalidDynKeyword,
LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType,
ReturnTypesUseThinArrow,
};
use crate::parser::{FnContext, FnParseMode, FrontMatterParsingMode};
use crate::{exp, maybe_recover_from_interpolated_ty_qpath};
Expand Down Expand Up @@ -408,6 +410,8 @@ impl<'a> Parser<'a> {
&& self.look_ahead(1, |t| *t == token::Star)
{
self.parse_ty_c_style_pointer()?
} else if self.is_c_style_reference_start() {
self.parse_ty_c_style_reference()?
} else if self.check_path() {
self.parse_path_start_ty(lo, allow_plus, ty_generics)?
} else if self.can_begin_bound() {
Expand Down Expand Up @@ -439,7 +443,11 @@ impl<'a> Parser<'a> {

// Try to recover from use of `+` with incorrect priority.
match allow_plus {
AllowPlus::Yes => self.maybe_recover_from_bad_type_plus(&ty)?,
AllowPlus::Yes => {
self.maybe_recover_from_bad_type_plus(&ty)?;
// Try to recover from `T&`.
ty = self.maybe_recover_from_c_style_reference(ty);
}
AllowPlus::No => self.maybe_report_ambiguous_plus(impl_dyn_multi, &ty),
}
if let RecoverQuestionMark::Yes = recover_question_mark {
Expand Down Expand Up @@ -624,6 +632,47 @@ impl<'a> Parser<'a> {
unreachable!("this could never happen")
}

/// Parses a reference with a C-style typo
/// Only for `mut`
fn parse_ty_c_style_reference(&mut self) -> PResult<'a, TyKind> {
let kw_span = self.token.span;
let mutbl = self.parse_mutability();
let ref_span = self.token.span;

self.bump(); // `&`

let (lifetime, err) = if self.token.is_lifetime() {
let lifetime = self.expect_lifetime();
(
Some(lifetime),
CStyleReferenceMut::WithLifetime {
span: kw_span,
sugg: CStyleReferenceMutLifetimeSugg {
remove: kw_span.until(ref_span),
insert: lifetime.ident.span.shrink_to_hi(),
},
},
)
} else {
(
None,
CStyleReferenceMut::Plain {
span: kw_span,
sugg: CStyleReferenceMutPlainSugg {
remove: ref_span,
insert: kw_span.shrink_to_lo(),
},
},
)
};

let ty = self.parse_ty_no_question_mark_recover()?;

self.dcx().emit_err(err);

return Ok(TyKind::Ref(lifetime, MutTy { ty, mutbl }));
}

/// Parses a raw pointer type: `*[const | mut] $type`.
fn parse_ty_ptr(&mut self) -> PResult<'a, TyKind> {
let mutbl = self.parse_mut_or_const().unwrap_or_else(|| {
Expand Down
47 changes: 47 additions & 0 deletions tests/ui/did_you_mean/c-style-reference-exprs-issue-101487.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Tests for https://github.com/rust-lang/rust/issues/101487
// `&` should go before the expression in C-style references
//@ run-rustfix

#![allow(unused)]

static N: i32 = 0;
fn func2() -> &'static i32 {
&N
//~^ ERROR reference types must be written as `&expr`
//~| HELP put the `&` before the `expr`
}

macro_rules! m {
($e:expr) => { $e };
($($t:tt)*) => { 0 };
}
fn func1(num: &mut i32) {}

fn main() {
let x = 12;
let _ptr = &x;
//~^ ERROR reference types must be written as `&expr`
//~| HELP put the `&` before the `expr`

let mut y = 34;
func1(&mut y );
//~^ ERROR reference types must be written as `&mut expr`
//~| HELP put the `&mut` before the `expr`

let arr = [&x, &y];
//~^ ERROR reference types must be written as `&expr`
//~| HELP put the `&` before the `expr`
//~| ERROR reference types must be written as `&expr`
//~| HELP put the `&` before the `expr`

// Normal bitwise operations, should not have any errors
let b = x & y;

m!(x & y);

let _ptr2 = &x;
let c = x & *_ptr2;
let d = x & !y;
let e = x & (y);
let f = x & &y;
}
47 changes: 47 additions & 0 deletions tests/ui/did_you_mean/c-style-reference-exprs-issue-101487.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Tests for https://github.com/rust-lang/rust/issues/101487
// `&` should go before the expression in C-style references
//@ run-rustfix

#![allow(unused)]

static N: i32 = 0;
fn func2() -> &'static i32 {
N&
//~^ ERROR reference types must be written as `&expr`
//~| HELP put the `&` before the `expr`
}

macro_rules! m {
($e:expr) => { $e };
($($t:tt)*) => { 0 };
}
fn func1(num: &mut i32) {}

fn main() {
let x = 12;
let _ptr = x&;
//~^ ERROR reference types must be written as `&expr`
//~| HELP put the `&` before the `expr`

let mut y = 34;
func1(y &mut);
//~^ ERROR reference types must be written as `&mut expr`
//~| HELP put the `&mut` before the `expr`

let arr = [x&, y&];
//~^ ERROR reference types must be written as `&expr`
//~| HELP put the `&` before the `expr`
//~| ERROR reference types must be written as `&expr`
//~| HELP put the `&` before the `expr`

// Normal bitwise operations, should not have any errors
let b = x & y;

m!(x & y);

let _ptr2 = &x;
let c = x & *_ptr2;
let d = x & !y;
let e = x & (y);
let f = x & &y;
}
Loading
Loading