ressa 0.8.2

An ECMAscript parser
Documentation
#![cfg(feature = "moz_central")]

use ressa::{Builder, Error};
use std::path::Path;
use walkdir::WalkDir;
#[cfg(windows)]
static ESPARSE: &str = "node_modules/.bin/esparse.cmd";
#[cfg(not(windows))]
static ESPARSE: &str = "node_modules/.bin/esparse";

#[test]
fn moz_central() {
    let moz_central_path = Path::new("./moz-central");
    if !moz_central_path.exists() {
        panic!("Unable to run this test without the files in ./moz-central see CONTRIBUTING.md for more information");
    }
    let failures = walk(&moz_central_path);
    let fail_count = failures.iter().filter(|(_, ok_list)| !ok_list).count();
    for (msg, _) in failures.iter().filter(|(_, ok_list)| *ok_list) {
        println!("W-{}", msg);
    }
    if fail_count > 0 {
        eprintln!("----------");
        eprintln!("FAILURES");
        eprintln!("----------");
        for (msg, _) in failures.iter().filter(|(_, ok_list)| !ok_list) {
            eprintln!("{}", msg);
        }
        panic!("Failed to parse {} moz_central files", fail_count);
    }
}

fn walk(path: &Path) -> Vec<(String, bool)> {
    let mut ret = Vec::new();
    for file_path in WalkDir::new(path).into_iter() {
        let file_path = file_path.expect(&format!("Error for file {}", path.display()));
        if file_path.path().is_file() {
            let test = if let Some(ext) = file_path.path().extension() {
                ext == "js"
            } else {
                false
            };
            if !test {
                continue;
            }
            if let Err(e) = run(&file_path.path()) {
                let loc = match &e {
                    Error::UnexpectedToken(ref pos, _)
                    | Error::UnableToReinterpret(ref pos, _, _)
                    | Error::Redecl(ref pos, _)
                    | Error::OperationError(ref pos, _)
                    | Error::InvalidGetterParams(ref pos)
                    | Error::InvalidSetterParams(ref pos)
                    | Error::NonStrictFeatureInStrictContext(ref pos, _)
                    | Error::InvalidImportError(ref pos)
                    | Error::InvalidExportError(ref pos)
                    | Error::InvalidUseOfContextualKeyword(ref pos, _)
                    | Error::TryWithNoCatchOrFinally(ref pos)
                    | Error::InvalidCatchArg(ref pos)
                    | Error::ThrowWithNoArg(ref pos)
                    | Error::UnknownOptionalLabel(ref pos, _, _)
                    | Error::InvalidOptionalLabel(ref pos)
                    | Error::UseOfModuleFeatureOutsideOfModule(ref pos, _) => format!(
                        "{}:{}:{}",
                        &file_path.path().to_str().unwrap(),
                        pos.line,
                        pos.column
                    ),
                    _ => format!("{}", file_path.path().display()),
                };
                let mut msg = format!("Parse Failure {}\n\t\"{}\"", e, loc);
                let ok_list = match ::std::process::Command::new(ESPARSE)
                    .arg(file_path.path())
                    .output()
                {
                    Ok(op) => {
                        if !op.status.success() {
                            let mut msg2 = format!(
                                "esparse failure: \nstderr: {:?}",
                                String::from_utf8_lossy(&op.stderr)
                            );
                            msg2.push_str(&format!(
                                "stdout: {:?}",
                                String::from_utf8_lossy(&op.stdout)
                            ));
                            Some(msg2)
                        } else {
                            let name = file_path.file_name();
                            let failures_path = Path::new("failures");
                            if !failures_path.exists() {
                                std::fs::create_dir_all(&failures_path)
                                    .expect("Failed to create out path for failures");
                            }
                            let mut out_path = failures_path.join(name);
                            out_path.set_extension("json");
                            ::std::fs::write(
                                &out_path,
                                String::from_utf8_lossy(&op.stdout).to_string(),
                            )
                            .expect(&format!("failed to wirte {}", out_path.display()));
                            None
                        }
                    }
                    Err(e) => {
                        panic!("failed to exec esparse {}", e);
                    }
                };
                let ok_list = if let Some(msg2) = ok_list {
                    msg.push_str(&format!("\n{}", msg2));
                    true
                } else {
                    false
                };
                ret.push((msg, ok_list));
            }
        }
    }
    ret
}

fn run(file: &Path) -> Result<(), Error> {
    // Named regex groups
    if file.ends_with("bug1640487.js") || file.ends_with("bug1640592.js") {
        return Ok(());
    }
    let mut contents = ::std::fs::read_to_string(file)?;
    if contents.starts_with("|") {
        // bad comment
        contents = format!("//{}", contents);
    }
    if let Some(first) = contents.lines().next() {
        if first.contains("SyntaxError") {
            return Ok(());
        }
        //--> in last line
        if first.contains("error:InternalError") {
            contents = contents.replace("-->", "//");
        }
    }
    let module = contents.starts_with("// |jit-test| module");
    let b = Builder::new();
    let parser = b.js(&contents).module(module).build()?;
    for part in parser {
        let _part = part?;
    }
    Ok(())
}