diff options
Diffstat (limited to 'syn/examples/dump-syntax')
-rw-r--r-- | syn/examples/dump-syntax/Cargo.toml | 17 | ||||
-rw-r--r-- | syn/examples/dump-syntax/README.md | 28 | ||||
-rw-r--r-- | syn/examples/dump-syntax/src/main.rs | 149 |
3 files changed, 194 insertions, 0 deletions
diff --git a/syn/examples/dump-syntax/Cargo.toml b/syn/examples/dump-syntax/Cargo.toml new file mode 100644 index 0000000..0bc9f62 --- /dev/null +++ b/syn/examples/dump-syntax/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "dump-syntax" +version = "0.0.0" +authors = ["David Tolnay <dtolnay@gmail.com>"] +edition = "2018" +publish = false + +[dependencies] +colored = "1.7" +proc-macro2 = { version = "1.0", features = ["span-locations"] } + +[dependencies.syn] +path = "../.." +default-features = false +features = ["parsing", "full", "extra-traits"] + +[workspace] diff --git a/syn/examples/dump-syntax/README.md b/syn/examples/dump-syntax/README.md new file mode 100644 index 0000000..37c84d8 --- /dev/null +++ b/syn/examples/dump-syntax/README.md @@ -0,0 +1,28 @@ +Parse a Rust source file into a `syn::File` and print out a debug representation +of the syntax tree. + +Use the following command from this directory to test this program by running it +on its own source code: + +``` +cargo run -- src/main.rs +``` + +The output will begin with: + +``` +File { + shebang: None, + attrs: [ + Attribute { + pound_token: Pound, + style: Inner( + Bang + ), + bracket_token: Bracket, + path: Path { + leading_colon: None, + segments: [ + ... +} +``` diff --git a/syn/examples/dump-syntax/src/main.rs b/syn/examples/dump-syntax/src/main.rs new file mode 100644 index 0000000..240b7a2 --- /dev/null +++ b/syn/examples/dump-syntax/src/main.rs @@ -0,0 +1,149 @@ +//! Parse a Rust source file into a `syn::File` and print out a debug +//! representation of the syntax tree. +//! +//! Use the following command from this directory to test this program by +//! running it on its own source code: +//! +//! cargo run -- src/main.rs +//! +//! The output will begin with: +//! +//! File { +//! shebang: None, +//! attrs: [ +//! Attribute { +//! pound_token: Pound, +//! style: Inner( +//! ... +//! } + +use std::borrow::Cow; +use std::env; +use std::ffi::OsStr; +use std::fmt::{self, Display}; +use std::fs; +use std::io::{self, Write}; +use std::path::{Path, PathBuf}; +use std::process; + +use colored::Colorize; + +enum Error { + IncorrectUsage, + ReadFile(io::Error), + ParseFile { + error: syn::Error, + filepath: PathBuf, + source_code: String, + }, +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Error::*; + + match self { + IncorrectUsage => write!(f, "Usage: dump-syntax path/to/filename.rs"), + ReadFile(error) => write!(f, "Unable to read file: {}", error), + ParseFile { + error, + filepath, + source_code, + } => render_location(f, error, filepath, source_code), + } + } +} + +fn main() { + if let Err(error) = try_main() { + let _ = writeln!(io::stderr(), "{}", error); + process::exit(1); + } +} + +fn try_main() -> Result<(), Error> { + let mut args = env::args_os(); + let _ = args.next(); // executable name + + let filepath = match (args.next(), args.next()) { + (Some(arg), None) => PathBuf::from(arg), + _ => return Err(Error::IncorrectUsage), + }; + + let code = fs::read_to_string(&filepath).map_err(Error::ReadFile)?; + let syntax = syn::parse_file(&code).map_err({ + |error| Error::ParseFile { + error, + filepath, + source_code: code, + } + })?; + println!("{:#?}", syntax); + + Ok(()) +} + +// Render a rustc-style error message, including colors. +// +// error: Syn unable to parse file +// --> main.rs:40:17 +// | +// 40 | fn fmt(&self formatter: &mut fmt::Formatter) -> fmt::Result { +// | ^^^^^^^^^ expected `,` +// +fn render_location( + formatter: &mut fmt::Formatter, + err: &syn::Error, + filepath: &Path, + code: &str, +) -> fmt::Result { + let start = err.span().start(); + let mut end = err.span().end(); + + if start.line == end.line && start.column == end.column { + return render_fallback(formatter, err); + } + + let code_line = match code.lines().nth(start.line - 1) { + Some(line) => line, + None => return render_fallback(formatter, err), + }; + + if end.line > start.line { + end.line = start.line; + end.column = code_line.len(); + } + + let filename = filepath + .file_name() + .map(OsStr::to_string_lossy) + .unwrap_or(Cow::Borrowed("main.rs")); + + write!( + formatter, + "\n\ + {error}{header}\n\ + {indent}{arrow} {filename}:{linenum}:{colnum}\n\ + {indent} {pipe}\n\ + {label} {pipe} {code}\n\ + {indent} {pipe} {offset}{underline} {message}\n\ + ", + error = "error".red().bold(), + header = ": Syn unable to parse file".bold(), + indent = " ".repeat(start.line.to_string().len()), + arrow = "-->".blue().bold(), + filename = filename, + linenum = start.line, + colnum = start.column, + pipe = "|".blue().bold(), + label = start.line.to_string().blue().bold(), + code = code_line.trim_end(), + offset = " ".repeat(start.column), + underline = "^".repeat(end.column - start.column).red().bold(), + message = err.to_string().red(), + ) +} + +fn render_fallback(formatter: &mut fmt::Formatter, err: &syn::Error) -> fmt::Result { + write!(formatter, "Unable to parse file: {}", err) +} |