Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve cfg_if! parsing during module resolution #5341

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
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
82 changes: 50 additions & 32 deletions src/parse/macros/cfg_if.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::panic::{catch_unwind, AssertUnwindSafe};

use rustc_ast::ast;
use rustc_ast::token::{Delimiter, TokenKind};
use rustc_parse::parser::ForceCollect;
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_span::symbol::kw;

use crate::parse::macros::build_stream_parser;
Expand Down Expand Up @@ -31,40 +31,25 @@ fn parse_cfg_if_inner<'a>(

while parser.token.kind != TokenKind::Eof {
if process_if_cfg {
if !parser.eat_keyword(kw::If) {
return Err("Expected `if`");
}

if !matches!(parser.token.kind, TokenKind::Pound) {
return Err("Failed to parse attributes");
}

// Inner attributes are not actually syntactically permitted here, but we don't
// care about inner vs outer attributes in this position. Our purpose with this
// special case parsing of cfg_if macros is to ensure we can correctly resolve
// imported modules that may have a custom `path` defined.
//
// As such, we just need to advance the parser past the attribute and up to
// to the opening brace.
// See also https://github.com/rust-lang/rust/pull/79433
parser
.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
.map_err(|e| {
e.cancel();
"Failed to parse attributes"
})?;
}

if !parser.eat(&TokenKind::OpenDelim(Delimiter::Brace)) {
return Err("Expected an opening brace");
eat_if(&mut parser)?;
}

while parser.token != TokenKind::CloseDelim(Delimiter::Brace)
&& parser.token.kind != TokenKind::Eof
{
let item = match parser.parse_item(ForceCollect::No) {
Ok(Some(item_ptr)) => item_ptr.into_inner(),
Ok(None) => continue,
Ok(None) => {
if matches!(parser.token.kind, TokenKind::Ident(symbol, ..) if symbol == kw::If)
{
// eat a nested if
eat_if(&mut parser)?;
} else {
// Not sure what token we're on. To prevent infinite loops bump the parser
parser.bump();
}
continue;
}
Err(err) => {
err.cancel();
parser.psess.dcx.reset_err_count();
Expand All @@ -82,16 +67,49 @@ fn parse_cfg_if_inner<'a>(
return Err("Expected a closing brace");
}

if parser.eat(&TokenKind::Eof) {
break;
if matches!(parser.token.kind, TokenKind::Ident(symbol, ..) if symbol == kw::Else) {
// there might be an `else` after the `if`
parser.eat_keyword(kw::Else);
// there might be an opening brace after the `else`, but it might also be an `else if`
parser.eat(&TokenKind::OpenDelim(Delimiter::Brace));
}

if !parser.eat_keyword(kw::Else) {
return Err("Expected `else`");
if parser.eat(&TokenKind::Eof) {
break;
}

process_if_cfg = parser.token.is_keyword(kw::If);
}

Ok(items)
}

fn eat_if(parser: &mut Parser<'_>) -> Result<(), &'static str> {
if !parser.eat_keyword(kw::If) {
return Err("Expected `if`");
}

if !matches!(parser.token.kind, TokenKind::Pound) {
return Err("Failed to parse attributes");
}

// Inner attributes are not actually syntactically permitted here, but we don't
// care about inner vs outer attributes in this position. Our purpose with this
// special case parsing of cfg_if macros is to ensure we can correctly resolve
// imported modules that may have a custom `path` defined.
//
// As such, we just need to advance the parser past the attribute and up to
// to the opening brace.
// See also https://github.com/rust-lang/rust/pull/79433
parser
.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
.map_err(|e| {
e.cancel();
"Failed to parse attributes"
})?;

if !parser.eat(&TokenKind::OpenDelim(Delimiter::Brace)) {
return Err("Expected an opening brace");
}
Ok(())
}
2 changes: 2 additions & 0 deletions tests/source/issue-4442/a.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fn a ()
{println!("mod a")}
2 changes: 2 additions & 0 deletions tests/source/issue-4442/b.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fn b ()
{println!("mod b")}
2 changes: 2 additions & 0 deletions tests/source/issue-4442/c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fn c ()
{println!("mod c")}
2 changes: 2 additions & 0 deletions tests/source/issue-4442/d.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fn d ()
{println!("mod d")}
2 changes: 2 additions & 0 deletions tests/source/issue-4442/e.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fn e ()
{println!("mod e")}
26 changes: 26 additions & 0 deletions tests/source/issue-4442/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// main.rs
cfg_if::cfg_if! {
if #[cfg(not(feature = "client"))] {
mod a;
if #[cfg(feature = "server")] {
if #[cfg(not(feature = "client"))] {
if #[cfg(feature = "server")] {
if #[cfg(not(feature = "client"))] {
mod b;
} else {
mod c;
}
if #[cfg(feature = "server")] {
if #[cfg(not(feature = "client"))] {
if #[cfg(feature = "server")] {
mod d;
} else {
mod e;
}
}
}
}
}
}
}
}
16 changes: 16 additions & 0 deletions tests/source/issue_5413.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cfg_if! {
if
}

cfg_if! {
if #[a] { } else if
}

cfg_if! {
if #[a] { if }
}

fn main(
) {
println!("hello world!");
}
3 changes: 3 additions & 0 deletions tests/target/issue-4442/a.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn a() {
println!("mod a")
}
3 changes: 3 additions & 0 deletions tests/target/issue-4442/b.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn b() {
println!("mod b")
}
3 changes: 3 additions & 0 deletions tests/target/issue-4442/c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn c() {
println!("mod c")
}
3 changes: 3 additions & 0 deletions tests/target/issue-4442/d.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn d() {
println!("mod d")
}
3 changes: 3 additions & 0 deletions tests/target/issue-4442/e.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn e() {
println!("mod e")
}
26 changes: 26 additions & 0 deletions tests/target/issue-4442/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// main.rs
cfg_if::cfg_if! {
if #[cfg(not(feature = "client"))] {
mod a;
if #[cfg(feature = "server")] {
if #[cfg(not(feature = "client"))] {
if #[cfg(feature = "server")] {
if #[cfg(not(feature = "client"))] {
mod b;
} else {
mod c;
}
if #[cfg(feature = "server")] {
if #[cfg(not(feature = "client"))] {
if #[cfg(feature = "server")] {
mod d;
} else {
mod e;
}
}
}
}
}
}
}
}
15 changes: 15 additions & 0 deletions tests/target/issue_5413.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cfg_if! {
if
}

cfg_if! {
if #[a] { } else if
}

cfg_if! {
if #[a] { if }
}

fn main() {
println!("hello world!");
}
6 changes: 6 additions & 0 deletions tests/target/issue_5428.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// cfg-if: version 1.0.0
cfg_if::cfg_if! {
if #[cfg(windows)] {
compile_error!{"..."};
}
}
9 changes: 9 additions & 0 deletions tests/target/issue_6215.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cfg_if!(
if #[cfg(feature = "xxxx")] {
if #[cfg(feature = "yyyy")] {
pub const C1: u8 = 1;
} else {
pub const C2: u8 = 2;
}
}
);
Loading