Skip to content

Commit c3ab010

Browse files
committed
Improve cfg_if! parsing during module resolution
Fixes 4442 * Prevent Infinite loop when reaching tokens that can't be parsed as items. For example, nested `if #[..]` checks. * Handle arbitrarily nested `if #[..]`, `else if #[..]`, `else` checks.
1 parent e494418 commit c3ab010

File tree

13 files changed

+127
-32
lines changed

13 files changed

+127
-32
lines changed

src/parse/macros/cfg_if.rs

+50-32
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::panic::{catch_unwind, AssertUnwindSafe};
22

33
use rustc_ast::ast;
44
use rustc_ast::token::{Delimiter, TokenKind};
5-
use rustc_parse::parser::ForceCollect;
5+
use rustc_parse::parser::{ForceCollect, Parser};
66
use rustc_span::symbol::kw;
77

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

3232
while parser.token.kind != TokenKind::Eof {
3333
if process_if_cfg {
34-
if !parser.eat_keyword(kw::If) {
35-
return Err("Expected `if`");
36-
}
37-
38-
if !matches!(parser.token.kind, TokenKind::Pound) {
39-
return Err("Failed to parse attributes");
40-
}
41-
42-
// Inner attributes are not actually syntactically permitted here, but we don't
43-
// care about inner vs outer attributes in this position. Our purpose with this
44-
// special case parsing of cfg_if macros is to ensure we can correctly resolve
45-
// imported modules that may have a custom `path` defined.
46-
//
47-
// As such, we just need to advance the parser past the attribute and up to
48-
// to the opening brace.
49-
// See also https://github.com/rust-lang/rust/pull/79433
50-
parser
51-
.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
52-
.map_err(|e| {
53-
e.cancel();
54-
"Failed to parse attributes"
55-
})?;
56-
}
57-
58-
if !parser.eat(&TokenKind::OpenDelim(Delimiter::Brace)) {
59-
return Err("Expected an opening brace");
34+
eat_if(&mut parser)?;
6035
}
6136

6237
while parser.token != TokenKind::CloseDelim(Delimiter::Brace)
6338
&& parser.token.kind != TokenKind::Eof
6439
{
6540
let item = match parser.parse_item(ForceCollect::No) {
6641
Ok(Some(item_ptr)) => item_ptr.into_inner(),
67-
Ok(None) => continue,
42+
Ok(None) => {
43+
if matches!(parser.token.kind, TokenKind::Ident(symbol, ..) if symbol == kw::If)
44+
{
45+
// eat a nested if
46+
eat_if(&mut parser)?;
47+
} else {
48+
// Not sure what token we're on. To prevent infinite loops bump the parser
49+
parser.bump();
50+
}
51+
continue;
52+
}
6853
Err(err) => {
6954
err.cancel();
7055
parser.psess.dcx.reset_err_count();
@@ -82,16 +67,49 @@ fn parse_cfg_if_inner<'a>(
8267
return Err("Expected a closing brace");
8368
}
8469

85-
if parser.eat(&TokenKind::Eof) {
86-
break;
70+
if matches!(parser.token.kind, TokenKind::Ident(symbol, ..) if symbol == kw::Else) {
71+
// there might be an `else` after the `if`
72+
parser.eat_keyword(kw::Else);
73+
// there might be an opening brace after the `else`, but it might also be an `else if`
74+
parser.eat(&TokenKind::OpenDelim(Delimiter::Brace));
8775
}
8876

89-
if !parser.eat_keyword(kw::Else) {
90-
return Err("Expected `else`");
77+
if parser.eat(&TokenKind::Eof) {
78+
break;
9179
}
9280

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

9684
Ok(items)
9785
}
86+
87+
fn eat_if(parser: &mut Parser<'_>) -> Result<(), &'static str> {
88+
if !parser.eat_keyword(kw::If) {
89+
return Err("Expected `if`");
90+
}
91+
92+
if !matches!(parser.token.kind, TokenKind::Pound) {
93+
return Err("Failed to parse attributes");
94+
}
95+
96+
// Inner attributes are not actually syntactically permitted here, but we don't
97+
// care about inner vs outer attributes in this position. Our purpose with this
98+
// special case parsing of cfg_if macros is to ensure we can correctly resolve
99+
// imported modules that may have a custom `path` defined.
100+
//
101+
// As such, we just need to advance the parser past the attribute and up to
102+
// to the opening brace.
103+
// See also https://github.com/rust-lang/rust/pull/79433
104+
parser
105+
.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
106+
.map_err(|e| {
107+
e.cancel();
108+
"Failed to parse attributes"
109+
})?;
110+
111+
if !parser.eat(&TokenKind::OpenDelim(Delimiter::Brace)) {
112+
return Err("Expected an opening brace");
113+
}
114+
Ok(())
115+
}

tests/source/issue-4442/a.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fn a ()
2+
{println!("mod a")}

tests/source/issue-4442/b.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fn b ()
2+
{println!("mod b")}

tests/source/issue-4442/c.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fn c ()
2+
{println!("mod c")}

tests/source/issue-4442/d.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fn d ()
2+
{println!("mod d")}

tests/source/issue-4442/e.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
fn e ()
2+
{println!("mod e")}

tests/source/issue-4442/lib.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// main.rs
2+
cfg_if::cfg_if! {
3+
if #[cfg(not(feature = "client"))] {
4+
mod a;
5+
if #[cfg(feature = "server")] {
6+
if #[cfg(not(feature = "client"))] {
7+
if #[cfg(feature = "server")] {
8+
if #[cfg(not(feature = "client"))] {
9+
mod b;
10+
} else {
11+
mod c;
12+
}
13+
if #[cfg(feature = "server")] {
14+
if #[cfg(not(feature = "client"))] {
15+
if #[cfg(feature = "server")] {
16+
mod d;
17+
} else {
18+
mod e;
19+
}
20+
}
21+
}
22+
}
23+
}
24+
}
25+
}
26+
}

tests/target/issue-4442/a.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn a() {
2+
println!("mod a")
3+
}

tests/target/issue-4442/b.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn b() {
2+
println!("mod b")
3+
}

tests/target/issue-4442/c.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn c() {
2+
println!("mod c")
3+
}

tests/target/issue-4442/d.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn d() {
2+
println!("mod d")
3+
}

tests/target/issue-4442/e.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn e() {
2+
println!("mod e")
3+
}

tests/target/issue-4442/lib.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// main.rs
2+
cfg_if::cfg_if! {
3+
if #[cfg(not(feature = "client"))] {
4+
mod a;
5+
if #[cfg(feature = "server")] {
6+
if #[cfg(not(feature = "client"))] {
7+
if #[cfg(feature = "server")] {
8+
if #[cfg(not(feature = "client"))] {
9+
mod b;
10+
} else {
11+
mod c;
12+
}
13+
if #[cfg(feature = "server")] {
14+
if #[cfg(not(feature = "client"))] {
15+
if #[cfg(feature = "server")] {
16+
mod d;
17+
} else {
18+
mod e;
19+
}
20+
}
21+
}
22+
}
23+
}
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)