Skip to content

Commit

Permalink
Merge pull request #876 from dtolnay/shebang
Browse files Browse the repository at this point in the history
Match rustc's new shebang handling
  • Loading branch information
dtolnay committed Aug 3, 2020
2 parents 8b460ba + 30ffa3d commit 5647268
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 7 deletions.
20 changes: 13 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,9 @@ pub mod parse_macro_input;
#[cfg(all(feature = "parsing", feature = "printing"))]
pub mod spanned;

#[cfg(all(feature = "parsing", feature = "full"))]
mod whitespace;

mod gen {
/// Syntax tree traversal to walk a shared borrow of a syntax tree.
///
Expand Down Expand Up @@ -943,13 +946,16 @@ pub fn parse_file(mut content: &str) -> Result<File> {
}

let mut shebang = None;
if content.starts_with("#!") && !content.starts_with("#![") {
if let Some(idx) = content.find('\n') {
shebang = Some(content[..idx].to_string());
content = &content[idx..];
} else {
shebang = Some(content.to_string());
content = "";
if content.starts_with("#!") {
let rest = whitespace::skip(&content[2..]);
if !rest.starts_with('[') {
if let Some(idx) = content.find('\n') {
shebang = Some(content[..idx].to_string());
content = &content[idx..];
} else {
shebang = Some(content.to_string());
content = "";
}
}
}

Expand Down
65 changes: 65 additions & 0 deletions src/whitespace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
pub fn skip(mut s: &str) -> &str {
'skip: while !s.is_empty() {
let byte = s.as_bytes()[0];
if byte == b'/' {
if s.starts_with("//")
&& (!s.starts_with("///") || s.starts_with("////"))
&& !s.starts_with("//!")
{
if let Some(i) = s.find('\n') {
s = &s[i + 1..];
continue;
} else {
return "";
}
} else if s.starts_with("/**/") {
s = &s[4..];
continue;
} else if s.starts_with("/*")
&& (!s.starts_with("/**") || s.starts_with("/***"))
&& !s.starts_with("/*!")
{
let mut depth = 0;
let bytes = s.as_bytes();
let mut i = 0;
let upper = bytes.len() - 1;
while i < upper {
if bytes[i] == b'/' && bytes[i + 1] == b'*' {
depth += 1;
i += 1; // eat '*'
} else if bytes[i] == b'*' && bytes[i + 1] == b'/' {
depth -= 1;
if depth == 0 {
s = &s[i + 2..];
continue 'skip;
}
i += 1; // eat '/'
}
i += 1;
}
return s;
}
}
match byte {
b' ' | 0x09..=0x0d => {
s = &s[1..];
continue;
}
b if b <= 0x7f => {}
_ => {
let ch = s.chars().next().unwrap();
if is_whitespace(ch) {
s = &s[ch.len_utf8()..];
continue;
}
}
}
return s;
}
s
}

fn is_whitespace(ch: char) -> bool {
// Rust treats left-to-right mark and right-to-left mark as whitespace
ch.is_whitespace() || ch == '\u{200e}' || ch == '\u{200f}'
}
59 changes: 59 additions & 0 deletions tests/test_shebang.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#[macro_use]
mod macros;

#[test]
fn test_basic() {
let content = "#!/usr/bin/env rustx\nfn main() {}";
let file = syn::parse_file(content).unwrap();
snapshot!(file, @r###"
File {
shebang: Some("#!/usr/bin/env rustx"),
items: [
Item::Fn {
vis: Inherited,
sig: Signature {
ident: "main",
generics: Generics,
output: Default,
},
block: Block,
},
],
}
"###);
}

#[test]
fn test_comment() {
let content = "#!//am/i/a/comment\n[allow(dead_code)] fn main() {}";
let file = syn::parse_file(content).unwrap();
snapshot!(file, @r###"
File {
attrs: [
Attribute {
style: Inner,
path: Path {
segments: [
PathSegment {
ident: "allow",
arguments: None,
},
],
},
tokens: TokenStream(`( dead_code )`),
},
],
items: [
Item::Fn {
vis: Inherited,
sig: Signature {
ident: "main",
generics: Generics,
output: Default,
},
block: Block,
},
],
}
"###);
}

0 comments on commit 5647268

Please sign in to comment.