Skip to content

Commit

Permalink
Auto merge of rust-lang#127957 - matthiaskrgr:rollup-1u5ivck, r=matth…
Browse files Browse the repository at this point in the history
…iaskrgr

Rollup of 6 pull requests

Successful merges:

 - rust-lang#127350 (Parser: Suggest Placing the Return Type After Function Parameters)
 - rust-lang#127621 (Rewrite and rename `issue-22131` and `issue-26006` `run-make` tests to rmake)
 - rust-lang#127662 (When finding item gated behind a `cfg` flag, point at it)
 - rust-lang#127903 (`force_collect` improvements)
 - rust-lang#127932 (rustdoc: fix `current` class on sidebar modnav)
 - rust-lang#127943 (Don't allow unsafe statics outside of extern blocks)

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Jul 19, 2024
2 parents 11e5724 + 6fe68f8 commit 3811f40
Show file tree
Hide file tree
Showing 49 changed files with 490 additions and 146 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ ast_passes_unsafe_negative_impl = negative impls cannot be unsafe
.negative = negative because of this
.unsafe = unsafe because of this
ast_passes_unsafe_static =
static items cannot be declared with `unsafe` safety qualifier outside of `extern` block
ast_passes_visibility_not_permitted =
visibility qualifiers are not permitted here
.enum_variant = enum variants and their fields always share the visibility of the enum they are in
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,11 @@ impl<'a> AstValidator<'a> {
}
}

/// This ensures that items can only be `unsafe` (or unmarked) outside of extern
/// blocks.
///
/// This additionally ensures that within extern blocks, items can only be
/// `safe`/`unsafe` inside of a `unsafe`-adorned extern block.
fn check_item_safety(&self, span: Span, safety: Safety) {
match self.extern_mod_safety {
Some(extern_safety) => {
Expand Down Expand Up @@ -1177,6 +1182,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
ItemKind::Static(box StaticItem { expr, safety, .. }) => {
self.check_item_safety(item.span, *safety);
if matches!(safety, Safety::Unsafe(_)) {
self.dcx().emit_err(errors::UnsafeStatic { span: item.span });
}

if expr.is_none() {
self.dcx().emit_err(errors::StaticWithoutBody {
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,13 @@ pub struct InvalidSafetyOnBareFn {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_unsafe_static)]
pub struct UnsafeStatic {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_bound_in_context)]
pub struct BoundInContext<'a> {
Expand Down
16 changes: 9 additions & 7 deletions compiler/rustc_builtin_macros/src/cmdline_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::errors;
use rustc_ast::attr::mk_attr;
use rustc_ast::token;
use rustc_ast::{self as ast, AttrItem, AttrStyle};
use rustc_parse::parser::ForceCollect;
use rustc_parse::{new_parser_from_source_str, unwrap_or_emit_fatal};
use rustc_session::parse::ParseSess;
use rustc_span::FileName;
Expand All @@ -17,13 +18,14 @@ pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) {
));

let start_span = parser.token.span;
let AttrItem { unsafety, path, args, tokens: _ } = match parser.parse_attr_item(false) {
Ok(ai) => ai,
Err(err) => {
err.emit();
continue;
}
};
let AttrItem { unsafety, path, args, tokens: _ } =
match parser.parse_attr_item(ForceCollect::No) {
Ok(ai) => ai,
Err(err) => {
err.emit();
continue;
}
};
let end_span = parser.token.span;
if parser.token != token::Eof {
psess.dcx().emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) });
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,8 @@ parse_mismatched_closing_delimiter = mismatched closing delimiter: `{$delimiter}
.label_opening_candidate = closing delimiter possibly meant for this
.label_unclosed = unclosed delimiter
parse_misplaced_return_type = place the return type after the function parameters
parse_missing_comma_after_match_arm = expected `,` following `match` arm
.suggestion = missing a comma here to end this `match` arm
Expand Down
22 changes: 15 additions & 7 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1502,6 +1502,20 @@ pub(crate) struct FnPtrWithGenerics {
pub sugg: Option<FnPtrWithGenericsSugg>,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(
parse_misplaced_return_type,
style = "verbose",
applicability = "maybe-incorrect"
)]
pub(crate) struct MisplacedReturnType {
#[suggestion_part(code = " {snippet}")]
pub fn_params_end: Span,
pub snippet: String,
#[suggestion_part(code = "")]
pub ret_ty_span: Span,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")]
pub(crate) struct FnPtrWithGenericsSugg {
Expand All @@ -1516,7 +1530,6 @@ pub(crate) struct FnPtrWithGenericsSugg {

pub(crate) struct FnTraitMissingParen {
pub span: Span,
pub machine_applicable: bool,
}

impl Subdiagnostic for FnTraitMissingParen {
Expand All @@ -1526,16 +1539,11 @@ impl Subdiagnostic for FnTraitMissingParen {
_: &F,
) {
diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren);
let applicability = if self.machine_applicable {
Applicability::MachineApplicable
} else {
Applicability::MaybeIncorrect
};
diag.span_suggestion_short(
self.span.shrink_to_hi(),
crate::fluent_generated::parse_add_paren,
"()",
applicability,
Applicability::MachineApplicable,
);
}
}
Expand Down
15 changes: 7 additions & 8 deletions compiler/rustc_parse/src/parser/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ impl<'a> Parser<'a> {
if this.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer };

this.expect(&token::OpenDelim(Delimiter::Bracket))?;
let item = this.parse_attr_item(false)?;
let item = this.parse_attr_item(ForceCollect::No)?;
this.expect(&token::CloseDelim(Delimiter::Bracket))?;
let attr_sp = lo.to(this.prev_token.span);

Expand Down Expand Up @@ -248,16 +248,15 @@ impl<'a> Parser<'a> {
/// PATH
/// PATH `=` UNSUFFIXED_LIT
/// The delimiters or `=` are still put into the resulting token stream.
pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::AttrItem> {
pub fn parse_attr_item(&mut self, force_collect: ForceCollect) -> PResult<'a, ast::AttrItem> {
maybe_whole!(self, NtMeta, |attr| attr.into_inner());

let do_parse = |this: &mut Self| {
let do_parse = |this: &mut Self, _empty_attrs| {
let is_unsafe = this.eat_keyword(kw::Unsafe);
let unsafety = if is_unsafe {
let unsafe_span = this.prev_token.span;
this.psess.gated_spans.gate(sym::unsafe_attributes, unsafe_span);
this.expect(&token::OpenDelim(Delimiter::Parenthesis))?;

ast::Safety::Unsafe(unsafe_span)
} else {
ast::Safety::Default
Expand All @@ -268,10 +267,10 @@ impl<'a> Parser<'a> {
if is_unsafe {
this.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
}
Ok(ast::AttrItem { unsafety, path, args, tokens: None })
Ok((ast::AttrItem { unsafety, path, args, tokens: None }, false))
};
// Attr items don't have attributes
if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) }
// Attr items don't have attributes.
self.collect_tokens_trailing_token(AttrWrapper::empty(), force_collect, do_parse)
}

/// Parses attributes that appear after the opening of an item. These should
Expand Down Expand Up @@ -340,7 +339,7 @@ impl<'a> Parser<'a> {
let mut expanded_attrs = Vec::with_capacity(1);
while self.token.kind != token::Eof {
let lo = self.token.span;
let item = self.parse_attr_item(true)?;
let item = self.parse_attr_item(ForceCollect::Yes)?;
expanded_attrs.push((item, lo.to(self.prev_token.span)));
if !self.eat(&token::Comma) {
break;
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ impl<'a> Parser<'a> {
&mut self,
edible: &[TokenKind],
inedible: &[TokenKind],
) -> PResult<'a, Recovered> {
) -> PResult<'a, ErrorGuaranteed> {
debug!("expected_one_of_not_found(edible: {:?}, inedible: {:?})", edible, inedible);
fn tokens_to_string(tokens: &[TokenType]) -> String {
let mut i = tokens.iter();
Expand Down Expand Up @@ -533,7 +533,7 @@ impl<'a> Parser<'a> {
sugg: ExpectedSemiSugg::ChangeToSemi(self.token.span),
});
self.bump();
return Ok(Recovered::Yes(guar));
return Ok(guar);
} else if self.look_ahead(0, |t| {
t == &token::CloseDelim(Delimiter::Brace)
|| ((t.can_begin_expr() || t.can_begin_item())
Expand All @@ -557,7 +557,7 @@ impl<'a> Parser<'a> {
unexpected_token_label: Some(self.token.span),
sugg: ExpectedSemiSugg::AddSemi(span),
});
return Ok(Recovered::Yes(guar));
return Ok(guar);
}
}

Expand Down Expand Up @@ -712,7 +712,7 @@ impl<'a> Parser<'a> {
if self.check_too_many_raw_str_terminators(&mut err) {
if expected.contains(&TokenType::Token(token::Semi)) && self.eat(&token::Semi) {
let guar = err.emit();
return Ok(Recovered::Yes(guar));
return Ok(guar);
} else {
return Err(err);
}
Expand Down
124 changes: 96 additions & 28 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use rustc_span::edit_distance::edit_distance;
use rustc_span::edition::Edition;
use rustc_span::source_map;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::ErrorGuaranteed;
use rustc_span::{Span, DUMMY_SP};
use std::fmt::Write;
use std::mem;
Expand Down Expand Up @@ -2332,14 +2333,106 @@ impl<'a> Parser<'a> {
}
}
};

// Store the end of function parameters to give better diagnostics
// inside `parse_fn_body()`.
let fn_params_end = self.prev_token.span.shrink_to_hi();

generics.where_clause = self.parse_where_clause()?; // `where T: Ord`

// `fn_params_end` is needed only when it's followed by a where clause.
let fn_params_end =
if generics.where_clause.has_where_token { Some(fn_params_end) } else { None };

let mut sig_hi = self.prev_token.span;
let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body)?; // `;` or `{ ... }`.
// Either `;` or `{ ... }`.
let body =
self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?;
let fn_sig_span = sig_lo.to(sig_hi);
Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body))
}

/// Provide diagnostics when function body is not found
fn error_fn_body_not_found(
&mut self,
ident_span: Span,
req_body: bool,
fn_params_end: Option<Span>,
) -> PResult<'a, ErrorGuaranteed> {
let expected = if req_body {
&[token::OpenDelim(Delimiter::Brace)][..]
} else {
&[token::Semi, token::OpenDelim(Delimiter::Brace)]
};
match self.expected_one_of_not_found(&[], expected) {
Ok(error_guaranteed) => Ok(error_guaranteed),
Err(mut err) => {
if self.token.kind == token::CloseDelim(Delimiter::Brace) {
// The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in
// the AST for typechecking.
err.span_label(ident_span, "while parsing this `fn`");
Ok(err.emit())
} else if self.token.kind == token::RArrow
&& let Some(fn_params_end) = fn_params_end
{
// Instead of a function body, the parser has encountered a right arrow
// preceded by a where clause.

// Find whether token behind the right arrow is a function trait and
// store its span.
let fn_trait_span =
[sym::FnOnce, sym::FnMut, sym::Fn].into_iter().find_map(|symbol| {
if self.prev_token.is_ident_named(symbol) {
Some(self.prev_token.span)
} else {
None
}
});

// Parse the return type (along with the right arrow) and store its span.
// If there's a parse error, cancel it and return the existing error
// as we are primarily concerned with the
// expected-function-body-but-found-something-else error here.
let arrow_span = self.token.span;
let ty_span = match self.parse_ret_ty(
AllowPlus::Yes,
RecoverQPath::Yes,
RecoverReturnSign::Yes,
) {
Ok(ty_span) => ty_span.span().shrink_to_hi(),
Err(parse_error) => {
parse_error.cancel();
return Err(err);
}
};
let ret_ty_span = arrow_span.to(ty_span);

if let Some(fn_trait_span) = fn_trait_span {
// Typo'd Fn* trait bounds such as
// fn foo<F>() where F: FnOnce -> () {}
err.subdiagnostic(errors::FnTraitMissingParen { span: fn_trait_span });
} else if let Ok(snippet) = self.psess.source_map().span_to_snippet(ret_ty_span)
{
// If token behind right arrow is not a Fn* trait, the programmer
// probably misplaced the return type after the where clause like
// `fn foo<T>() where T: Default -> u8 {}`
err.primary_message(
"return type should be specified after the function parameters",
);
err.subdiagnostic(errors::MisplacedReturnType {
fn_params_end,
snippet,
ret_ty_span,
});
}
Err(err)
} else {
Err(err)
}
}
}
}

/// Parse the "body" of a function.
/// This can either be `;` when there's no body,
/// or e.g. a block when the function is a provided one.
Expand All @@ -2349,6 +2442,7 @@ impl<'a> Parser<'a> {
ident: &Ident,
sig_hi: &mut Span,
req_body: bool,
fn_params_end: Option<Span>,
) -> PResult<'a, Option<P<Block>>> {
let has_semi = if req_body {
self.token.kind == TokenKind::Semi
Expand Down Expand Up @@ -2377,33 +2471,7 @@ impl<'a> Parser<'a> {
});
(AttrVec::new(), Some(self.mk_block_err(span, guar)))
} else {
let expected = if req_body {
&[token::OpenDelim(Delimiter::Brace)][..]
} else {
&[token::Semi, token::OpenDelim(Delimiter::Brace)]
};
if let Err(mut err) = self.expected_one_of_not_found(&[], expected) {
if self.token.kind == token::CloseDelim(Delimiter::Brace) {
// The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in
// the AST for typechecking.
err.span_label(ident.span, "while parsing this `fn`");
err.emit();
} else {
// check for typo'd Fn* trait bounds such as
// fn foo<F>() where F: FnOnce -> () {}
if self.token.kind == token::RArrow {
let machine_applicable = [sym::FnOnce, sym::FnMut, sym::Fn]
.into_iter()
.any(|s| self.prev_token.is_ident_named(s));

err.subdiagnostic(errors::FnTraitMissingParen {
span: self.prev_token.span,
machine_applicable,
});
}
return Err(err);
}
}
self.error_fn_body_not_found(ident.span, req_body, fn_params_end)?;
(AttrVec::new(), None)
};
attrs.extend(inner_attrs);
Expand Down
Loading

0 comments on commit 3811f40

Please sign in to comment.