diff --git a/boa_ast/src/keyword.rs b/boa_ast/src/keyword/mod.rs similarity index 99% rename from boa_ast/src/keyword.rs rename to boa_ast/src/keyword/mod.rs index 8ded9b6d38..f1c36ec113 100644 --- a/boa_ast/src/keyword.rs +++ b/boa_ast/src/keyword/mod.rs @@ -14,6 +14,9 @@ use boa_interner::Sym; use boa_macros::utf16; use std::{convert::TryFrom, error, fmt, str::FromStr}; +#[cfg(test)] +mod tests; + /// List of keywords recognized by the JavaScript grammar. /// /// See the [module-level documentation][self] for more details. @@ -604,11 +607,8 @@ impl fmt::Display for KeywordError { } // This is important for other errors to wrap this one. -impl error::Error for KeywordError { - fn description(&self) -> &str { - "invalid token" - } -} +impl error::Error for KeywordError {} + impl FromStr for Keyword { type Err = KeywordError; fn from_str(s: &str) -> Result { diff --git a/boa_ast/src/keyword/tests.rs b/boa_ast/src/keyword/tests.rs new file mode 100644 index 0000000000..182f685e65 --- /dev/null +++ b/boa_ast/src/keyword/tests.rs @@ -0,0 +1,354 @@ +#![allow(clippy::cognitive_complexity)] + +use super::*; + +/// Gets a list of all the keywords. +fn all_keywords() -> impl Iterator { + [ + Keyword::Await, + Keyword::Async, + Keyword::Break, + Keyword::Case, + Keyword::Catch, + Keyword::Class, + Keyword::Continue, + Keyword::Const, + Keyword::Debugger, + Keyword::Default, + Keyword::Delete, + Keyword::Do, + Keyword::Else, + Keyword::Enum, + Keyword::Export, + Keyword::Extends, + Keyword::False, + Keyword::Finally, + Keyword::For, + Keyword::Function, + Keyword::If, + Keyword::In, + Keyword::InstanceOf, + Keyword::Import, + Keyword::Let, + Keyword::New, + Keyword::Null, + Keyword::Of, + Keyword::Return, + Keyword::Super, + Keyword::Switch, + Keyword::This, + Keyword::Throw, + Keyword::True, + Keyword::Try, + Keyword::TypeOf, + Keyword::Var, + Keyword::Void, + Keyword::While, + Keyword::With, + Keyword::Yield, + ] + .into_iter() +} + +#[test] +fn as_binary_op() { + for k in all_keywords() { + match k.as_binary_op() { + Some(BinaryOp::Relational(RelationalOp::InstanceOf)) => { + assert_eq!(k, Keyword::InstanceOf); + } + Some(BinaryOp::Relational(RelationalOp::In)) => assert_eq!(k, Keyword::In), + None => { + assert_ne!(k, Keyword::InstanceOf); + assert_ne!(k, Keyword::In); + } + _ => unreachable!("unknown binary operator for keyword {k:?} found"), + } + } +} + +#[test] +fn as_str() { + for k in all_keywords() { + match k.as_str() { + ("await", utf16) => { + assert_eq!(k, Keyword::Await); + assert_eq!(utf16, utf16!("await")); + } + ("async", utf16) => { + assert_eq!(k, Keyword::Async); + assert_eq!(utf16, utf16!("async")); + } + ("break", utf16) => { + assert_eq!(k, Keyword::Break); + assert_eq!(utf16, utf16!("break")); + } + ("case", utf16) => { + assert_eq!(k, Keyword::Case); + assert_eq!(utf16, utf16!("case")); + } + ("catch", utf16) => { + assert_eq!(k, Keyword::Catch); + assert_eq!(utf16, utf16!("catch")); + } + ("class", utf16) => { + assert_eq!(k, Keyword::Class); + assert_eq!(utf16, utf16!("class")); + } + ("continue", utf16) => { + assert_eq!(k, Keyword::Continue); + assert_eq!(utf16, utf16!("continue")); + } + ("const", utf16) => { + assert_eq!(k, Keyword::Const); + assert_eq!(utf16, utf16!("const")); + } + ("debugger", utf16) => { + assert_eq!(k, Keyword::Debugger); + assert_eq!(utf16, utf16!("debugger")); + } + ("default", utf16) => { + assert_eq!(k, Keyword::Default); + assert_eq!(utf16, utf16!("default")); + } + ("delete", utf16) => { + assert_eq!(k, Keyword::Delete); + assert_eq!(utf16, utf16!("delete")); + } + ("do", utf16) => { + assert_eq!(k, Keyword::Do); + assert_eq!(utf16, utf16!("do")); + } + ("else", utf16) => { + assert_eq!(k, Keyword::Else); + assert_eq!(utf16, utf16!("else")); + } + ("enum", utf16) => { + assert_eq!(k, Keyword::Enum); + assert_eq!(utf16, utf16!("enum")); + } + ("extends", utf16) => { + assert_eq!(k, Keyword::Extends); + assert_eq!(utf16, utf16!("extends")); + } + ("export", utf16) => { + assert_eq!(k, Keyword::Export); + assert_eq!(utf16, utf16!("export")); + } + ("false", utf16) => { + assert_eq!(k, Keyword::False); + assert_eq!(utf16, utf16!("false")); + } + ("finally", utf16) => { + assert_eq!(k, Keyword::Finally); + assert_eq!(utf16, utf16!("finally")); + } + ("for", utf16) => { + assert_eq!(k, Keyword::For); + assert_eq!(utf16, utf16!("for")); + } + ("function", utf16) => { + assert_eq!(k, Keyword::Function); + assert_eq!(utf16, utf16!("function")); + } + ("if", utf16) => { + assert_eq!(k, Keyword::If); + assert_eq!(utf16, utf16!("if")); + } + ("in", utf16) => { + assert_eq!(k, Keyword::In); + assert_eq!(utf16, utf16!("in")); + } + ("instanceof", utf16) => { + assert_eq!(k, Keyword::InstanceOf); + assert_eq!(utf16, utf16!("instanceof")); + } + ("import", utf16) => { + assert_eq!(k, Keyword::Import); + assert_eq!(utf16, utf16!("import")); + } + ("let", utf16) => { + assert_eq!(k, Keyword::Let); + assert_eq!(utf16, utf16!("let")); + } + ("new", utf16) => { + assert_eq!(k, Keyword::New); + assert_eq!(utf16, utf16!("new")); + } + ("null", utf16) => { + assert_eq!(k, Keyword::Null); + assert_eq!(utf16, utf16!("null")); + } + ("of", utf16) => { + assert_eq!(k, Keyword::Of); + assert_eq!(utf16, utf16!("of")); + } + ("return", utf16) => { + assert_eq!(k, Keyword::Return); + assert_eq!(utf16, utf16!("return")); + } + ("super", utf16) => { + assert_eq!(k, Keyword::Super); + assert_eq!(utf16, utf16!("super")); + } + ("switch", utf16) => { + assert_eq!(k, Keyword::Switch); + assert_eq!(utf16, utf16!("switch")); + } + ("this", utf16) => { + assert_eq!(k, Keyword::This); + assert_eq!(utf16, utf16!("this")); + } + ("throw", utf16) => { + assert_eq!(k, Keyword::Throw); + assert_eq!(utf16, utf16!("throw")); + } + ("true", utf16) => { + assert_eq!(k, Keyword::True); + assert_eq!(utf16, utf16!("true")); + } + ("try", utf16) => { + assert_eq!(k, Keyword::Try); + assert_eq!(utf16, utf16!("try")); + } + ("typeof", utf16) => { + assert_eq!(k, Keyword::TypeOf); + assert_eq!(utf16, utf16!("typeof")); + } + ("var", utf16) => { + assert_eq!(k, Keyword::Var); + assert_eq!(utf16, utf16!("var")); + } + ("void", utf16) => { + assert_eq!(k, Keyword::Void); + assert_eq!(utf16, utf16!("void")); + } + ("while", utf16) => { + assert_eq!(k, Keyword::While); + assert_eq!(utf16, utf16!("while")); + } + ("with", utf16) => { + assert_eq!(k, Keyword::With); + assert_eq!(utf16, utf16!("with")); + } + ("yield", utf16) => { + assert_eq!(k, Keyword::Yield); + assert_eq!(utf16, utf16!("yield")); + } + (_, _) => unreachable!("unknown keyword {k:?} found"), + } + } +} + +#[test] +fn to_sym() { + for k in all_keywords() { + match k.to_sym() { + Sym::AWAIT => assert_eq!(k, Keyword::Await), + Sym::ASYNC => assert_eq!(k, Keyword::Async), + Sym::BREAK => assert_eq!(k, Keyword::Break), + Sym::CASE => assert_eq!(k, Keyword::Case), + Sym::CATCH => assert_eq!(k, Keyword::Catch), + Sym::CLASS => assert_eq!(k, Keyword::Class), + Sym::CONTINUE => assert_eq!(k, Keyword::Continue), + Sym::CONST => assert_eq!(k, Keyword::Const), + Sym::DEBUGGER => assert_eq!(k, Keyword::Debugger), + Sym::DEFAULT => assert_eq!(k, Keyword::Default), + Sym::DELETE => assert_eq!(k, Keyword::Delete), + Sym::DO => assert_eq!(k, Keyword::Do), + Sym::ELSE => assert_eq!(k, Keyword::Else), + Sym::ENUM => assert_eq!(k, Keyword::Enum), + Sym::EXPORT => assert_eq!(k, Keyword::Export), + Sym::EXTENDS => assert_eq!(k, Keyword::Extends), + Sym::FALSE => assert_eq!(k, Keyword::False), + Sym::FINALLY => assert_eq!(k, Keyword::Finally), + Sym::FOR => assert_eq!(k, Keyword::For), + Sym::FUNCTION => assert_eq!(k, Keyword::Function), + Sym::IF => assert_eq!(k, Keyword::If), + Sym::IN => assert_eq!(k, Keyword::In), + Sym::INSTANCEOF => assert_eq!(k, Keyword::InstanceOf), + Sym::IMPORT => assert_eq!(k, Keyword::Import), + Sym::LET => assert_eq!(k, Keyword::Let), + Sym::NEW => assert_eq!(k, Keyword::New), + Sym::NULL => assert_eq!(k, Keyword::Null), + Sym::OF => assert_eq!(k, Keyword::Of), + Sym::RETURN => assert_eq!(k, Keyword::Return), + Sym::SUPER => assert_eq!(k, Keyword::Super), + Sym::SWITCH => assert_eq!(k, Keyword::Switch), + Sym::THIS => assert_eq!(k, Keyword::This), + Sym::THROW => assert_eq!(k, Keyword::Throw), + Sym::TRUE => assert_eq!(k, Keyword::True), + Sym::TRY => assert_eq!(k, Keyword::Try), + Sym::TYPEOF => assert_eq!(k, Keyword::TypeOf), + Sym::VAR => assert_eq!(k, Keyword::Var), + Sym::VOID => assert_eq!(k, Keyword::Void), + Sym::WHILE => assert_eq!(k, Keyword::While), + Sym::WITH => assert_eq!(k, Keyword::With), + Sym::YIELD => assert_eq!(k, Keyword::Yield), + _ => unreachable!("unknown keyword {k:?} found"), + } + } +} + +#[test] +fn try_into_binary_op() { + for k in all_keywords() { + match k { + Keyword::InstanceOf | Keyword::In => assert!(BinaryOp::try_from(k).is_ok()), + Keyword::Await + | Keyword::Async + | Keyword::Break + | Keyword::Case + | Keyword::Catch + | Keyword::Class + | Keyword::Continue + | Keyword::Const + | Keyword::Debugger + | Keyword::Default + | Keyword::Delete + | Keyword::Do + | Keyword::Else + | Keyword::Enum + | Keyword::Export + | Keyword::Extends + | Keyword::False + | Keyword::Finally + | Keyword::For + | Keyword::Function + | Keyword::If + | Keyword::Import + | Keyword::Let + | Keyword::New + | Keyword::Null + | Keyword::Of + | Keyword::Return + | Keyword::Super + | Keyword::Switch + | Keyword::This + | Keyword::Throw + | Keyword::True + | Keyword::Try + | Keyword::TypeOf + | Keyword::Var + | Keyword::Void + | Keyword::While + | Keyword::With + | Keyword::Yield => assert!(BinaryOp::try_from(k).is_err()), + } + } +} + +#[test] +fn from_str() { + for k in all_keywords() { + let str = k.as_str().0; + assert_eq!(str.parse::().unwrap(), k); + } + + for invalid in ["", "testing", "invalid keyword"] { + let result = invalid.parse::(); + let error = result.unwrap_err(); + + assert_eq!(error.to_string(), "invalid token"); + } +}