Skip to content

Commit 2e92c20

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 17003ce commit 2e92c20

File tree

13 files changed

+119
-24
lines changed

13 files changed

+119
-24
lines changed

src/parse/macros/cfg_if.rs

+42-24
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::{DelimToken, 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,32 +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-
// Inner attributes are not actually syntactically permitted here, but we don't
38-
// care about inner vs outer attributes in this position. Our purpose with this
39-
// special case parsing of cfg_if macros is to ensure we can correctly resolve
40-
// imported modules that may have a custom `path` defined.
41-
//
42-
// As such, we just need to advance the parser past the attribute and up to
43-
// to the opening brace.
44-
// See also https://github.com/rust-lang/rust/pull/79433
45-
parser
46-
.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
47-
.map_err(|_| "Failed to parse attributes")?;
48-
}
49-
50-
if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
51-
return Err("Expected an opening brace");
34+
eat_if(&mut parser)?;
5235
}
5336

5437
while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
5538
&& parser.token.kind != TokenKind::Eof
5639
{
5740
let item = match parser.parse_item(ForceCollect::No) {
5841
Ok(Some(item_ptr)) => item_ptr.into_inner(),
59-
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+
}
6053
Err(err) => {
6154
err.cancel();
6255
parser.sess.span_diagnostic.reset_err_count();
@@ -74,16 +67,41 @@ fn parse_cfg_if_inner<'a>(
7467
return Err("Expected a closing brace");
7568
}
7669

77-
if parser.eat(&TokenKind::Eof) {
78-
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(DelimToken::Brace));
7975
}
8076

81-
if !parser.eat_keyword(kw::Else) {
82-
return Err("Expected `else`");
77+
if parser.eat(&TokenKind::Eof) {
78+
break;
8379
}
8480

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

8884
Ok(items)
8985
}
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+
// Inner attributes are not actually syntactically permitted here, but we don't
92+
// care about inner vs outer attributes in this position. Our purpose with this
93+
// special case parsing of cfg_if macros is to ensure we can correctly resolve
94+
// imported modules that may have a custom `path` defined.
95+
//
96+
// As such, we just need to advance the parser past the attribute and up to
97+
// to the opening brace.
98+
// See also https://github.com/rust-lang/rust/pull/79433
99+
parser
100+
.parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
101+
.map_err(|_| "Failed to parse attributes")?;
102+
103+
if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
104+
return Err("Expected an opening brace");
105+
}
106+
Ok(())
107+
}

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)