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) +} | 
