Skip to content

Commit b131b6f

Browse files
Rework how the disallowed qualifier lints are generated
Signed-off-by: Jonathan Brouwer <jonathantbrouwer@gmail.com>
1 parent 3160dfa commit b131b6f

File tree

9 files changed

+208
-284
lines changed

9 files changed

+208
-284
lines changed

compiler/rustc_parse/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,10 +299,12 @@ parse_float_literal_unsupported_base = {$base} float literal is not supported
299299
parse_fn_pointer_cannot_be_async = an `fn` pointer type cannot be `async`
300300
.label = `async` because of this
301301
.suggestion = remove the `async` qualifier
302+
.note = allowed qualifiers are: `unsafe` and `extern`
302303
303304
parse_fn_pointer_cannot_be_const = an `fn` pointer type cannot be `const`
304305
.label = `const` because of this
305306
.suggestion = remove the `const` qualifier
307+
.note = allowed qualifiers are: `unsafe` and `extern`
306308
307309
parse_fn_ptr_with_generics = function pointer types may not have generic parameters
308310
.suggestion = consider moving the lifetime {$arity ->

compiler/rustc_parse/src/errors.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2938,22 +2938,22 @@ pub(crate) struct DynAfterMut {
29382938

29392939
#[derive(Diagnostic)]
29402940
#[diag(parse_fn_pointer_cannot_be_const)]
2941+
#[note]
29412942
pub(crate) struct FnPointerCannotBeConst {
29422943
#[primary_span]
2943-
pub span: Span,
29442944
#[label]
2945-
pub qualifier: Span,
2945+
pub span: Span,
29462946
#[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")]
29472947
pub suggestion: Span,
29482948
}
29492949

29502950
#[derive(Diagnostic)]
29512951
#[diag(parse_fn_pointer_cannot_be_async)]
2952+
#[note]
29522953
pub(crate) struct FnPointerCannotBeAsync {
29532954
#[primary_span]
2954-
pub span: Span,
29552955
#[label]
2956-
pub qualifier: Span,
2956+
pub span: Span,
29572957
#[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")]
29582958
pub suggestion: Span,
29592959
}

compiler/rustc_parse/src/parser/item.rs

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use super::{
2323
AttrWrapper, ExpKeywordPair, ExpTokenPair, FollowedByType, ForceCollect, Parser, PathStyle,
2424
Recovered, Trailing, UsePreAttrPos,
2525
};
26-
use crate::errors::{self, MacroExpandsToAdtField};
26+
use crate::errors::{self, FnPointerCannotBeAsync, FnPointerCannotBeConst, MacroExpandsToAdtField};
2727
use crate::{exp, fluent_generated as fluent};
2828

2929
impl<'a> Parser<'a> {
@@ -2402,7 +2402,7 @@ impl<'a> Parser<'a> {
24022402
case: Case,
24032403
) -> PResult<'a, (Ident, FnSig, Generics, Option<P<FnContract>>, Option<P<Block>>)> {
24042404
let fn_span = self.token.span;
2405-
let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn`
2405+
let header = self.parse_fn_front_matter(vis, case, FrontMatterParsingMode::Function)?; // `const ... fn`
24062406
let ident = self.parse_ident()?; // `foo`
24072407
let mut generics = self.parse_generics()?; // `<'a, T, ...>`
24082408
let decl = match self.parse_fn_decl(
@@ -2658,16 +2658,37 @@ impl<'a> Parser<'a> {
26582658
///
26592659
/// `vis` represents the visibility that was already parsed, if any. Use
26602660
/// `Visibility::Inherited` when no visibility is known.
2661+
///
2662+
/// If `parsing_mode` is `FrontMatterParsingMode::FunctionPtrType`, we error on `const` and `async` qualifiers,
2663+
/// which are not allowed in function pointer types.
26612664
pub(super) fn parse_fn_front_matter(
26622665
&mut self,
26632666
orig_vis: &Visibility,
26642667
case: Case,
2668+
parsing_mode: FrontMatterParsingMode,
26652669
) -> PResult<'a, FnHeader> {
26662670
let sp_start = self.token.span;
26672671
let constness = self.parse_constness(case);
2672+
if parsing_mode == FrontMatterParsingMode::FunctionPtrType
2673+
&& let Const::Yes(const_span) = constness
2674+
{
2675+
self.dcx().emit_err(FnPointerCannotBeConst {
2676+
span: const_span,
2677+
suggestion: const_span.until(self.token.span),
2678+
});
2679+
}
26682680

26692681
let async_start_sp = self.token.span;
26702682
let coroutine_kind = self.parse_coroutine_kind(case);
2683+
if parsing_mode == FrontMatterParsingMode::FunctionPtrType
2684+
&& let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind
2685+
{
2686+
self.dcx().emit_err(FnPointerCannotBeAsync {
2687+
span: async_span,
2688+
suggestion: async_span.until(self.token.span),
2689+
});
2690+
}
2691+
// FIXME(gen_blocks): emit a similar error for `gen fn()`
26712692

26722693
let unsafe_start_sp = self.token.span;
26732694
let safety = self.parse_safety(case);
@@ -2703,6 +2724,11 @@ impl<'a> Parser<'a> {
27032724
enum WrongKw {
27042725
Duplicated(Span),
27052726
Misplaced(Span),
2727+
/// `MisplacedDisallowedQualifier` is only used instead of `Misplaced`,
2728+
/// when the misplaced keyword is disallowed by the current `FrontMatterParsingMode`.
2729+
/// In this case, we avoid generating the suggestion to swap around the keywords,
2730+
/// as we already generated a suggestion to remove the keyword earlier.
2731+
MisplacedDisallowedQualifier,
27062732
}
27072733

27082734
// We may be able to recover
@@ -2716,7 +2742,21 @@ impl<'a> Parser<'a> {
27162742
Const::Yes(sp) => Some(WrongKw::Duplicated(sp)),
27172743
Const::No => {
27182744
recover_constness = Const::Yes(self.token.span);
2719-
Some(WrongKw::Misplaced(async_start_sp))
2745+
match parsing_mode {
2746+
FrontMatterParsingMode::Function => {
2747+
Some(WrongKw::Misplaced(async_start_sp))
2748+
}
2749+
FrontMatterParsingMode::FunctionPtrType => {
2750+
self.dcx().emit_err(FnPointerCannotBeConst {
2751+
span: self.token.span,
2752+
suggestion: self
2753+
.token
2754+
.span
2755+
.with_lo(self.prev_token.span.hi()),
2756+
});
2757+
Some(WrongKw::MisplacedDisallowedQualifier)
2758+
}
2759+
}
27202760
}
27212761
}
27222762
} else if self.check_keyword(exp!(Async)) {
@@ -2742,7 +2782,21 @@ impl<'a> Parser<'a> {
27422782
closure_id: DUMMY_NODE_ID,
27432783
return_impl_trait_id: DUMMY_NODE_ID,
27442784
});
2745-
Some(WrongKw::Misplaced(unsafe_start_sp))
2785+
match parsing_mode {
2786+
FrontMatterParsingMode::Function => {
2787+
Some(WrongKw::Misplaced(async_start_sp))
2788+
}
2789+
FrontMatterParsingMode::FunctionPtrType => {
2790+
self.dcx().emit_err(FnPointerCannotBeAsync {
2791+
span: self.token.span,
2792+
suggestion: self
2793+
.token
2794+
.span
2795+
.with_lo(self.prev_token.span.hi()),
2796+
});
2797+
Some(WrongKw::MisplacedDisallowedQualifier)
2798+
}
2799+
}
27462800
}
27472801
}
27482802
} else if self.check_keyword(exp!(Unsafe)) {
@@ -2840,14 +2894,20 @@ impl<'a> Parser<'a> {
28402894

28412895
// FIXME(gen_blocks): add keyword recovery logic for genness
28422896

2843-
if wrong_kw.is_some()
2897+
if let Some(wrong_kw) = wrong_kw
28442898
&& self.may_recover()
28452899
&& self.look_ahead(1, |tok| tok.is_keyword_case(kw::Fn, case))
28462900
{
28472901
// Advance past the misplaced keyword and `fn`
28482902
self.bump();
28492903
self.bump();
2850-
err.emit();
2904+
// When we recover from a `MisplacedDisallowedQualifier`, we already emitted an error for the disallowed qualifier
2905+
// So we don't emit another error that the qualifier is unexpected.
2906+
if matches!(wrong_kw, WrongKw::MisplacedDisallowedQualifier) {
2907+
err.cancel();
2908+
} else {
2909+
err.emit();
2910+
}
28512911
return Ok(FnHeader {
28522912
constness: recover_constness,
28532913
safety: recover_safety,
@@ -3194,3 +3254,12 @@ enum IsMacroRulesItem {
31943254
Yes { has_bang: bool },
31953255
No,
31963256
}
3257+
3258+
#[derive(Copy, Clone, PartialEq, Eq)]
3259+
pub(super) enum FrontMatterParsingMode {
3260+
/// Parse the front matter of a function declaration
3261+
Function,
3262+
/// Parse the front matter of a function pointet type.
3263+
/// For function pointer types, the `const` and `async` keywords are not permitted.
3264+
FunctionPtrType,
3265+
}

compiler/rustc_parse/src/parser/ty.rs

Lines changed: 10 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ use thin_vec::{ThinVec, thin_vec};
1515
use super::{Parser, PathStyle, SeqSep, TokenType, Trailing};
1616
use crate::errors::{
1717
self, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType,
18-
FnPointerCannotBeAsync, FnPointerCannotBeConst, FnPtrWithGenerics, FnPtrWithGenericsSugg,
19-
HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime,
20-
NestedCVariadicType, ReturnTypesUseThinArrow,
18+
FnPtrWithGenerics, FnPtrWithGenericsSugg, HelpUseLatestEdition, InvalidDynKeyword,
19+
LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, NestedCVariadicType,
20+
ReturnTypesUseThinArrow,
2121
};
22+
use crate::parser::item::FrontMatterParsingMode;
2223
use crate::{exp, maybe_recover_from_interpolated_ty_qpath};
2324

2425
/// Signals whether parsing a type should allow `+`.
@@ -669,62 +670,16 @@ impl<'a> Parser<'a> {
669670
tokens: None,
670671
};
671672
let span_start = self.token.span;
672-
let ast::FnHeader { ext, safety, constness, coroutine_kind } =
673-
self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?;
674-
let fn_start_lo = self.prev_token.span.lo();
673+
let ast::FnHeader { ext, safety, .. } = self.parse_fn_front_matter(
674+
&inherited_vis,
675+
Case::Sensitive,
676+
FrontMatterParsingMode::FunctionPtrType,
677+
)?;
675678
if self.may_recover() && self.token == TokenKind::Lt {
676679
self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?;
677680
}
678681
let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?;
679-
let whole_span = lo.to(self.prev_token.span);
680-
681-
// Order/parsing of "front matter" follows:
682-
// `<constness> <coroutine_kind> <safety> <extern> fn()`
683-
// ^ ^ ^ ^ ^
684-
// | | | | fn_start_lo
685-
// | | | ext_sp.lo
686-
// | | safety_sp.lo
687-
// | coroutine_sp.lo
688-
// const_sp.lo
689-
if let ast::Const::Yes(const_span) = constness {
690-
let next_token_lo = if let Some(
691-
ast::CoroutineKind::Async { span, .. }
692-
| ast::CoroutineKind::Gen { span, .. }
693-
| ast::CoroutineKind::AsyncGen { span, .. },
694-
) = coroutine_kind
695-
{
696-
span.lo()
697-
} else if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety {
698-
span.lo()
699-
} else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext {
700-
span.lo()
701-
} else {
702-
fn_start_lo
703-
};
704-
let sugg_span = const_span.with_hi(next_token_lo);
705-
self.dcx().emit_err(FnPointerCannotBeConst {
706-
span: whole_span,
707-
qualifier: const_span,
708-
suggestion: sugg_span,
709-
});
710-
}
711-
if let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind {
712-
let next_token_lo = if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety
713-
{
714-
span.lo()
715-
} else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext {
716-
span.lo()
717-
} else {
718-
fn_start_lo
719-
};
720-
let sugg_span = async_span.with_hi(next_token_lo);
721-
self.dcx().emit_err(FnPointerCannotBeAsync {
722-
span: whole_span,
723-
qualifier: async_span,
724-
suggestion: sugg_span,
725-
});
726-
}
727-
// FIXME(gen_blocks): emit a similar error for `gen fn()`
682+
728683
let decl_span = span_start.to(self.prev_token.span);
729684
Ok(TyKind::BareFn(P(BareFnTy { ext, safety, generic_params: params, decl, decl_span })))
730685
}

tests/ui/parser/bad-fn-ptr-qualifier.fixed

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,12 @@ pub type FTT6 = for<'a> unsafe extern "C" fn();
2323
//~^ ERROR an `fn` pointer type cannot be `const`
2424
//~| ERROR an `fn` pointer type cannot be `async`
2525

26+
// Tests with qualifiers in the wrong order
27+
pub type W1 = unsafe fn();
28+
//~^ ERROR an `fn` pointer type cannot be `const`
29+
pub type W2 = unsafe fn();
30+
//~^ ERROR an `fn` pointer type cannot be `async`
31+
pub type W3 = for<'a> unsafe fn();
32+
//~^ ERROR an `fn` pointer type cannot be `const`
33+
2634
fn main() {}

tests/ui/parser/bad-fn-ptr-qualifier.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,9 @@ pub type FTT6 = for<'a> const async unsafe extern "C" fn();
2626
// Tests with qualifiers in the wrong order
2727
pub type W1 = unsafe const fn();
2828
//~^ ERROR an `fn` pointer type cannot be `const`
29-
//~| ERROR expected one of `extern` or `fn`, found keyword `const`
3029
pub type W2 = unsafe async fn();
3130
//~^ ERROR an `fn` pointer type cannot be `async`
32-
//~| ERROR expected one of `extern` or `fn`, found keyword `async`
3331
pub type W3 = for<'a> unsafe const fn();
3432
//~^ ERROR an `fn` pointer type cannot be `const`
35-
//~| ERROR expected one of `extern` or `fn`, found keyword `const`
3633

3734
fn main() {}

0 commit comments

Comments
 (0)