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
30 changes: 29 additions & 1 deletion compiler/rustc_attr_parsing/src/attributes/cfg_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use rustc_ast::token::Token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrStyle, NodeId, token};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Diagnostic;
use rustc_errors::{Diagnostic, MultiSpan};
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::CfgEntry;
use rustc_hir::{AttrPath, Target};
Expand Down Expand Up @@ -76,8 +76,11 @@ pub fn parse_cfg_select(
lint_node_id: NodeId,
) -> Result<CfgSelectBranches, ErrorGuaranteed> {
let mut branches = CfgSelectBranches::default();
let mut branch_attr_error: Option<ErrorGuaranteed> = None;

while p.token != token::Eof {
reject_branch_outer_attrs(p, &mut branch_attr_error)?;

if p.eat_keyword(exp!(Underscore)) {
let underscore = p.prev_token;
p.expect(exp!(FatArrow)).map_err(|e| e.emit())?;
Expand Down Expand Up @@ -131,6 +134,10 @@ pub fn parse_cfg_select(
}
}

if let Some(guar) = branch_attr_error {
return Err(guar);
}

let it = branches
.reachable
.iter()
Expand All @@ -143,6 +150,27 @@ pub fn parse_cfg_select(
Ok(branches)
}

fn reject_branch_outer_attrs(
p: &mut Parser<'_>,
branch_attr_error: &mut Option<ErrorGuaranteed>,
) -> Result<(), ErrorGuaranteed> {
let Some(spans) = p.parse_cfg_select_branch_outer_attrs().map_err(|e| e.emit())? else {
return Ok(());
};

for (spans, msg) in [
(spans.doc_comments, "doc comments are not allowed on `cfg_select` branches"),
(spans.attrs, "attributes are not allowed on `cfg_select` branches"),
] {
if !spans.is_empty() {
branch_attr_error
.get_or_insert(p.dcx().struct_span_err(MultiSpan::from_spans(spans), msg).emit());
}
}

Ok(())
}

fn lint_unreachable(
p: &mut Parser<'_>,
predicates: impl Iterator<Item = CfgSelectPredicate>,
Expand Down
38 changes: 37 additions & 1 deletion compiler/rustc_parse/src/parser/cfg_select.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
use rustc_ast::token;
use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast::util::classify;
use rustc_ast::{AttrKind, token};
use rustc_errors::PResult;
use rustc_span::Span;

use crate::exp;
use crate::parser::{AttrWrapper, ForceCollect, Parser, Restrictions, Trailing, UsePreAttrPos};

#[derive(Default)]
pub struct CfgSelectBranchAttrSpans {
pub attrs: Vec<Span>,
pub doc_comments: Vec<Span>,
}

impl<'a> Parser<'a> {
/// Parses a `TokenTree` consisting either of `{ /* ... */ }` optionally followed by a comma
/// (and strip the braces and the optional comma) or an expression followed by a comma
Expand Down Expand Up @@ -36,4 +43,33 @@ impl<'a> Parser<'a> {
}
Ok(TokenStream::from_ast(&expr))
}

/// Parses outer attributes before a `cfg_select!` branch for recovery.
pub fn parse_cfg_select_branch_outer_attrs(
&mut self,
) -> PResult<'a, Option<CfgSelectBranchAttrSpans>> {
let attrs = self.parse_outer_attributes()?;
if attrs.is_empty() {
return Ok(None);
}

let mut spans = CfgSelectBranchAttrSpans::default();
for attr in attrs.take_for_recovery(self.psess) {
match attr.kind {
AttrKind::Normal(..) => spans.attrs.push(attr.span),
// `parse_outer_attributes` already emitted E0753 for inner doc comments before
// recovering them as outer doc-comment attributes.
AttrKind::DocComment(comment_kind, _)
if self.span_to_snippet(attr.span).ok().is_some_and(
|snippet| match comment_kind {
token::CommentKind::Line => snippet.starts_with("//!"),
token::CommentKind::Block => snippet.starts_with("/*!"),
},
) => {}
AttrKind::DocComment(..) => spans.doc_comments.push(attr.span),
}
}

Ok(Some(spans))
}
}
49 changes: 49 additions & 0 deletions tests/ui/macros/cfg_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,52 @@ cfg_select! {
//~^ ERROR expected one of `(`, `::`, `=>`, or `=`, found `!`
//~| WARN unexpected `cfg` condition name
}

// Regression test for https://github.com/rust-lang/rust/issues/155701.
cfg_select! {
/// doc comment
//~^ ERROR doc comments are not allowed on `cfg_select` branches
debug_assertions => {}
/// doc comment
//~^ ERROR doc comments are not allowed on `cfg_select` branches
_ => {}
}

cfg_select! {
#[cfg(false)]
Comment thread
qaijuang marked this conversation as resolved.
//~^ ERROR attributes are not allowed on `cfg_select` branches
debug_assertions => {}
_ => {}
}

cfg_select! {
#![cfg(false)]
//~^ ERROR an inner attribute is not permitted in this context
debug_assertions => {}
_ => {}
}

cfg_select! {
//! inner doc comment
//~^ ERROR expected outer doc comment
debug_assertions => {}
_ => {}
}

cfg_select! {
debug_assertions => {}
/// line1
//~^ ERROR doc comments are not allowed on `cfg_select` branches
// line2
/// line3
_ => {}
}

cfg_select! {
/// outer doc comment
//~^ ERROR doc comments are not allowed on `cfg_select` branches
//! inner doc comment
//~^ ERROR expected outer doc comment
debug_assertions => {}
_ => {}
}
72 changes: 70 additions & 2 deletions tests/ui/macros/cfg_select.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,74 @@ error: expected one of `(`, `::`, `=>`, or `=`, found `!`
LL | cfg!() => {}
| ^ expected one of `(`, `::`, `=>`, or `=`

error: doc comments are not allowed on `cfg_select` branches
--> $DIR/cfg_select.rs:208:5
|
LL | /// doc comment
| ^^^^^^^^^^^^^^^

error: doc comments are not allowed on `cfg_select` branches
--> $DIR/cfg_select.rs:211:5
|
LL | /// doc comment
| ^^^^^^^^^^^^^^^

error: attributes are not allowed on `cfg_select` branches
--> $DIR/cfg_select.rs:217:5
|
LL | #[cfg(false)]
| ^^^^^^^^^^^^^

error: an inner attribute is not permitted in this context
--> $DIR/cfg_select.rs:224:5
|
LL | #![cfg(false)]
Comment thread
JonathanBrouwer marked this conversation as resolved.
| ^^^^^^^^^^^^^^
|
= note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
= note: outer attributes, like `#[test]`, annotate the item following them

error[E0753]: expected outer doc comment
--> $DIR/cfg_select.rs:231:5
|
LL | //! inner doc comment
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
help: you might have meant to write a regular comment
|
LL - //! inner doc comment
LL + // inner doc comment
|

error: doc comments are not allowed on `cfg_select` branches
--> $DIR/cfg_select.rs:239:5
|
LL | /// line1
| ^^^^^^^^^
...
LL | /// line3
| ^^^^^^^^^

error[E0753]: expected outer doc comment
--> $DIR/cfg_select.rs:249:5
|
LL | //! inner doc comment
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
help: you might have meant to write a regular comment
|
LL - //! inner doc comment
LL + // inner doc comment
|

error: doc comments are not allowed on `cfg_select` branches
--> $DIR/cfg_select.rs:247:5
|
LL | /// outer doc comment
| ^^^^^^^^^^^^^^^^^^^^^

warning: unreachable configuration predicate
--> $DIR/cfg_select.rs:136:5
|
Expand Down Expand Up @@ -115,7 +183,7 @@ LL | cfg!() => {}
= help: to expect this configuration use `--check-cfg=cfg(cfg)`
= note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration

error: aborting due to 9 previous errors; 7 warnings emitted
error: aborting due to 17 previous errors; 7 warnings emitted

Some errors have detailed explanations: E0537, E0539.
Some errors have detailed explanations: E0537, E0539, E0753.
For more information about an error, try `rustc --explain E0537`.
Loading