1fad3a1d3Sopenharmony_ci//! Parse a Rust source file into a `syn::File` and print out a debug 2fad3a1d3Sopenharmony_ci//! representation of the syntax tree. 3fad3a1d3Sopenharmony_ci//! 4fad3a1d3Sopenharmony_ci//! Use the following command from this directory to test this program by 5fad3a1d3Sopenharmony_ci//! running it on its own source code: 6fad3a1d3Sopenharmony_ci//! 7fad3a1d3Sopenharmony_ci//! cargo run -- src/main.rs 8fad3a1d3Sopenharmony_ci//! 9fad3a1d3Sopenharmony_ci//! The output will begin with: 10fad3a1d3Sopenharmony_ci//! 11fad3a1d3Sopenharmony_ci//! File { 12fad3a1d3Sopenharmony_ci//! shebang: None, 13fad3a1d3Sopenharmony_ci//! attrs: [ 14fad3a1d3Sopenharmony_ci//! Attribute { 15fad3a1d3Sopenharmony_ci//! pound_token: Pound, 16fad3a1d3Sopenharmony_ci//! style: AttrStyle::Inner( 17fad3a1d3Sopenharmony_ci//! ... 18fad3a1d3Sopenharmony_ci//! } 19fad3a1d3Sopenharmony_ci 20fad3a1d3Sopenharmony_ciuse colored::Colorize; 21fad3a1d3Sopenharmony_ciuse std::borrow::Cow; 22fad3a1d3Sopenharmony_ciuse std::env; 23fad3a1d3Sopenharmony_ciuse std::ffi::OsStr; 24fad3a1d3Sopenharmony_ciuse std::fmt::{self, Display}; 25fad3a1d3Sopenharmony_ciuse std::fs; 26fad3a1d3Sopenharmony_ciuse std::io::{self, Write}; 27fad3a1d3Sopenharmony_ciuse std::path::{Path, PathBuf}; 28fad3a1d3Sopenharmony_ciuse std::process; 29fad3a1d3Sopenharmony_ci 30fad3a1d3Sopenharmony_cienum Error { 31fad3a1d3Sopenharmony_ci IncorrectUsage, 32fad3a1d3Sopenharmony_ci ReadFile(io::Error), 33fad3a1d3Sopenharmony_ci ParseFile { 34fad3a1d3Sopenharmony_ci error: syn::Error, 35fad3a1d3Sopenharmony_ci filepath: PathBuf, 36fad3a1d3Sopenharmony_ci source_code: String, 37fad3a1d3Sopenharmony_ci }, 38fad3a1d3Sopenharmony_ci} 39fad3a1d3Sopenharmony_ci 40fad3a1d3Sopenharmony_ciimpl Display for Error { 41fad3a1d3Sopenharmony_ci fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 42fad3a1d3Sopenharmony_ci use self::Error::*; 43fad3a1d3Sopenharmony_ci 44fad3a1d3Sopenharmony_ci match self { 45fad3a1d3Sopenharmony_ci IncorrectUsage => write!(f, "Usage: dump-syntax path/to/filename.rs"), 46fad3a1d3Sopenharmony_ci ReadFile(error) => write!(f, "Unable to read file: {}", error), 47fad3a1d3Sopenharmony_ci ParseFile { 48fad3a1d3Sopenharmony_ci error, 49fad3a1d3Sopenharmony_ci filepath, 50fad3a1d3Sopenharmony_ci source_code, 51fad3a1d3Sopenharmony_ci } => render_location(f, error, filepath, source_code), 52fad3a1d3Sopenharmony_ci } 53fad3a1d3Sopenharmony_ci } 54fad3a1d3Sopenharmony_ci} 55fad3a1d3Sopenharmony_ci 56fad3a1d3Sopenharmony_cifn main() { 57fad3a1d3Sopenharmony_ci if let Err(error) = try_main() { 58fad3a1d3Sopenharmony_ci let _ = writeln!(io::stderr(), "{}", error); 59fad3a1d3Sopenharmony_ci process::exit(1); 60fad3a1d3Sopenharmony_ci } 61fad3a1d3Sopenharmony_ci} 62fad3a1d3Sopenharmony_ci 63fad3a1d3Sopenharmony_cifn try_main() -> Result<(), Error> { 64fad3a1d3Sopenharmony_ci let mut args = env::args_os(); 65fad3a1d3Sopenharmony_ci let _ = args.next(); // executable name 66fad3a1d3Sopenharmony_ci 67fad3a1d3Sopenharmony_ci let filepath = match (args.next(), args.next()) { 68fad3a1d3Sopenharmony_ci (Some(arg), None) => PathBuf::from(arg), 69fad3a1d3Sopenharmony_ci _ => return Err(Error::IncorrectUsage), 70fad3a1d3Sopenharmony_ci }; 71fad3a1d3Sopenharmony_ci 72fad3a1d3Sopenharmony_ci let code = fs::read_to_string(&filepath).map_err(Error::ReadFile)?; 73fad3a1d3Sopenharmony_ci let syntax = syn::parse_file(&code).map_err({ 74fad3a1d3Sopenharmony_ci |error| Error::ParseFile { 75fad3a1d3Sopenharmony_ci error, 76fad3a1d3Sopenharmony_ci filepath, 77fad3a1d3Sopenharmony_ci source_code: code, 78fad3a1d3Sopenharmony_ci } 79fad3a1d3Sopenharmony_ci })?; 80fad3a1d3Sopenharmony_ci println!("{:#?}", syntax); 81fad3a1d3Sopenharmony_ci 82fad3a1d3Sopenharmony_ci Ok(()) 83fad3a1d3Sopenharmony_ci} 84fad3a1d3Sopenharmony_ci 85fad3a1d3Sopenharmony_ci// Render a rustc-style error message, including colors. 86fad3a1d3Sopenharmony_ci// 87fad3a1d3Sopenharmony_ci// error: Syn unable to parse file 88fad3a1d3Sopenharmony_ci// --> main.rs:40:17 89fad3a1d3Sopenharmony_ci// | 90fad3a1d3Sopenharmony_ci// 40 | fn fmt(&self formatter: &mut fmt::Formatter) -> fmt::Result { 91fad3a1d3Sopenharmony_ci// | ^^^^^^^^^ expected `,` 92fad3a1d3Sopenharmony_ci// 93fad3a1d3Sopenharmony_cifn render_location( 94fad3a1d3Sopenharmony_ci formatter: &mut fmt::Formatter, 95fad3a1d3Sopenharmony_ci err: &syn::Error, 96fad3a1d3Sopenharmony_ci filepath: &Path, 97fad3a1d3Sopenharmony_ci code: &str, 98fad3a1d3Sopenharmony_ci) -> fmt::Result { 99fad3a1d3Sopenharmony_ci let start = err.span().start(); 100fad3a1d3Sopenharmony_ci let mut end = err.span().end(); 101fad3a1d3Sopenharmony_ci 102fad3a1d3Sopenharmony_ci let code_line = match start.line.checked_sub(1).and_then(|n| code.lines().nth(n)) { 103fad3a1d3Sopenharmony_ci Some(line) => line, 104fad3a1d3Sopenharmony_ci None => return render_fallback(formatter, err), 105fad3a1d3Sopenharmony_ci }; 106fad3a1d3Sopenharmony_ci 107fad3a1d3Sopenharmony_ci if end.line > start.line { 108fad3a1d3Sopenharmony_ci end.line = start.line; 109fad3a1d3Sopenharmony_ci end.column = code_line.len(); 110fad3a1d3Sopenharmony_ci } 111fad3a1d3Sopenharmony_ci 112fad3a1d3Sopenharmony_ci let filename = filepath 113fad3a1d3Sopenharmony_ci .file_name() 114fad3a1d3Sopenharmony_ci .map(OsStr::to_string_lossy) 115fad3a1d3Sopenharmony_ci .unwrap_or(Cow::Borrowed("main.rs")); 116fad3a1d3Sopenharmony_ci 117fad3a1d3Sopenharmony_ci write!( 118fad3a1d3Sopenharmony_ci formatter, 119fad3a1d3Sopenharmony_ci "\n\ 120fad3a1d3Sopenharmony_ci {error}{header}\n\ 121fad3a1d3Sopenharmony_ci {indent}{arrow} {filename}:{linenum}:{colnum}\n\ 122fad3a1d3Sopenharmony_ci {indent} {pipe}\n\ 123fad3a1d3Sopenharmony_ci {label} {pipe} {code}\n\ 124fad3a1d3Sopenharmony_ci {indent} {pipe} {offset}{underline} {message}\n\ 125fad3a1d3Sopenharmony_ci ", 126fad3a1d3Sopenharmony_ci error = "error".red().bold(), 127fad3a1d3Sopenharmony_ci header = ": Syn unable to parse file".bold(), 128fad3a1d3Sopenharmony_ci indent = " ".repeat(start.line.to_string().len()), 129fad3a1d3Sopenharmony_ci arrow = "-->".blue().bold(), 130fad3a1d3Sopenharmony_ci filename = filename, 131fad3a1d3Sopenharmony_ci linenum = start.line, 132fad3a1d3Sopenharmony_ci colnum = start.column, 133fad3a1d3Sopenharmony_ci pipe = "|".blue().bold(), 134fad3a1d3Sopenharmony_ci label = start.line.to_string().blue().bold(), 135fad3a1d3Sopenharmony_ci code = code_line.trim_end(), 136fad3a1d3Sopenharmony_ci offset = " ".repeat(start.column), 137fad3a1d3Sopenharmony_ci underline = "^" 138fad3a1d3Sopenharmony_ci .repeat(end.column.saturating_sub(start.column).max(1)) 139fad3a1d3Sopenharmony_ci .red() 140fad3a1d3Sopenharmony_ci .bold(), 141fad3a1d3Sopenharmony_ci message = err.to_string().red(), 142fad3a1d3Sopenharmony_ci ) 143fad3a1d3Sopenharmony_ci} 144fad3a1d3Sopenharmony_ci 145fad3a1d3Sopenharmony_cifn render_fallback(formatter: &mut fmt::Formatter, err: &syn::Error) -> fmt::Result { 146fad3a1d3Sopenharmony_ci write!(formatter, "Unable to parse file: {}", err) 147fad3a1d3Sopenharmony_ci} 148