Browse Source

Fix display for nodes (#1312)

* Fixed top level indents

* Fixed function definitions

* For loops display correctly now

* Await expressions display correctly

* Await calls no longer have a custom display() function

* Fixed async function declaration

* Added spacing to do-while loop

* Fixed for of loop

* Fixed object declaration formatter

* Fixed switch statements formatting

* Added operator fmt test

* Added array display test

* Added tests for await expressions. Unclear if let a = await blah() has been implemented yet, but I got parse errors when testing that

* Added block display test

* Added break formatting test

* Added a potential test for when block labels are added

* Added call formatting tests

* Added a testing utility function for formatting tests

* Using custom testing function instead of a bunch of asserts in formatting tests

* Improved formatting of failed parsing formatting tests

* Added conditional formatting tests

* Wrote function tests, and found out that functions are still horribly broken

* Fixed arrow function declaration

* Fixed the formatting for the rest of the functions. Async function expressions don't seem to be parsed correctly

* Re-ordered functions to match the output of StatementList

* Fixed async function expressions

* Added field formatting tests

* Added formatting test for 'for' loops

* For in loops now display their label if they have one

* Added test for the rest of the loops

* Added fmt for labels for all the types of loops

* Added test for new keyword formatting

* Added object formatting

* Partially fixed object display function

* Split Node::display into two functions, allowing objects to be displayed correctly

* Added a first implementation of a MethodDefinition formatter

* Added tests for the rest of object function fields (every branch of MethodDefinitionField)

* Operator test uses propper formatting test

* Added return statment formatter test

* Added spread argument formatting test

* Added switch statement formatting test

* Added a formatting test for templates

* Added a throw statement formatter test

* Added try catch test

* Removed unused import

* Formatting test now uses eprintln! instead of println!
pull/1334/head
macmv 3 years ago committed by GitHub
parent
commit
057cbb11b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      boa/src/syntax/ast/node/array/mod.rs
  2. 9
      boa/src/syntax/ast/node/array/tests.rs
  3. 14
      boa/src/syntax/ast/node/await_expr/mod.rs
  4. 9
      boa/src/syntax/ast/node/await_expr/tests.rs
  5. 3
      boa/src/syntax/ast/node/block/mod.rs
  6. 22
      boa/src/syntax/ast/node/block/tests.rs
  7. 31
      boa/src/syntax/ast/node/break_node/tests.rs
  8. 3
      boa/src/syntax/ast/node/call/mod.rs
  9. 10
      boa/src/syntax/ast/node/call/tests.rs
  10. 3
      boa/src/syntax/ast/node/conditional/mod.rs
  11. 13
      boa/src/syntax/ast/node/conditional/tests.rs
  12. 9
      boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs
  13. 20
      boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs
  14. 14
      boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs
  15. 12
      boa/src/syntax/ast/node/declaration/function_decl/mod.rs
  16. 21
      boa/src/syntax/ast/node/declaration/function_expr/mod.rs
  17. 29
      boa/src/syntax/ast/node/declaration/tests.rs
  18. 3
      boa/src/syntax/ast/node/field/mod.rs
  19. 10
      boa/src/syntax/ast/node/field/tests.rs
  20. 14
      boa/src/syntax/ast/node/iteration/continue_node/mod.rs
  21. 7
      boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs
  22. 8
      boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs
  23. 14
      boa/src/syntax/ast/node/iteration/for_loop/mod.rs
  24. 8
      boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs
  25. 73
      boa/src/syntax/ast/node/iteration/tests.rs
  26. 3
      boa/src/syntax/ast/node/iteration/while_loop/mod.rs
  27. 53
      boa/src/syntax/ast/node/mod.rs
  28. 3
      boa/src/syntax/ast/node/new/mod.rs
  29. 9
      boa/src/syntax/ast/node/new/tests.rs
  30. 31
      boa/src/syntax/ast/node/object/mod.rs
  31. 34
      boa/src/syntax/ast/node/object/tests.rs
  32. 25
      boa/src/syntax/ast/node/operator/tests.rs
  33. 3
      boa/src/syntax/ast/node/return_smt/mod.rs
  34. 16
      boa/src/syntax/ast/node/return_smt/tests.rs
  35. 16
      boa/src/syntax/ast/node/spread/tests.rs
  36. 5
      boa/src/syntax/ast/node/statement_list/mod.rs
  37. 13
      boa/src/syntax/ast/node/switch/mod.rs
  38. 36
      boa/src/syntax/ast/node/switch/tests.rs
  39. 17
      boa/src/syntax/ast/node/template/tests.rs
  40. 3
      boa/src/syntax/ast/node/throw/mod.rs
  41. 12
      boa/src/syntax/ast/node/throw/tests.rs
  42. 20
      boa/src/syntax/ast/node/try_node/tests.rs

3
boa/src/syntax/ast/node/array/mod.rs

@ -12,6 +12,9 @@ use std::fmt;
#[cfg(feature = "deser")] #[cfg(feature = "deser")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
/// An array is an ordered collection of data (either primitive or object depending upon the /// An array is an ordered collection of data (either primitive or object depending upon the
/// language). /// language).
/// ///

9
boa/src/syntax/ast/node/array/tests.rs

@ -0,0 +1,9 @@
#[test]
fn fmt() {
super::super::test_formatting(
r#"
let a = [1, 2, 3, "words", "more words"];
let b = [];
"#,
);
}

14
boa/src/syntax/ast/node/await_expr/mod.rs

@ -8,6 +8,9 @@ use std::fmt;
#[cfg(feature = "deser")] #[cfg(feature = "deser")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
/// An await expression is used within an async function to pause execution and wait for a /// An await expression is used within an async function to pause execution and wait for a
/// promise to resolve. /// promise to resolve.
/// ///
@ -31,14 +34,6 @@ impl Executable for AwaitExpr {
} }
} }
impl AwaitExpr {
/// Implements the display formatting with indentation.
pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
writeln!(f, "await ")?;
self.expr.display(f, indentation)
}
}
impl<T> From<T> for AwaitExpr impl<T> From<T> for AwaitExpr
where where
T: Into<Box<Node>>, T: Into<Box<Node>>,
@ -50,7 +45,8 @@ where
impl fmt::Display for AwaitExpr { impl fmt::Display for AwaitExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0) write!(f, "await ")?;
self.expr.display(f, 0)
} }
} }

9
boa/src/syntax/ast/node/await_expr/tests.rs

@ -0,0 +1,9 @@
#[test]
fn fmt() {
// TODO: `let a = await fn()` is invalid syntax as of writing. It should be tested here once implemented.
super::super::test_formatting(
r#"
await function_call();
"#,
);
}

3
boa/src/syntax/ast/node/block/mod.rs

@ -13,6 +13,9 @@ use std::fmt;
#[cfg(feature = "deser")] #[cfg(feature = "deser")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
/// A `block` statement (or compound statement in other languages) is used to group zero or /// A `block` statement (or compound statement in other languages) is used to group zero or
/// more statements. /// more statements.
/// ///

22
boa/src/syntax/ast/node/block/tests.rs

@ -0,0 +1,22 @@
#[test]
fn fmt() {
super::super::test_formatting(
r#"
{
let a = function_call();
console.log("hello");
}
another_statement();
"#,
);
// TODO: Once block labels are implemtned, this should be tested:
// super::super::test_formatting(
// r#"
// block_name: {
// let a = function_call();
// console.log("hello");
// }
// another_statement();
// "#,
// );
}

31
boa/src/syntax/ast/node/break_node/tests.rs

@ -17,3 +17,34 @@ fn check_post_state() {
&InterpreterState::Break(Some("label".into())) &InterpreterState::Break(Some("label".into()))
); );
} }
#[test]
fn fmt() {
// Blocks do not store their label, so we cannot test with
// the outer block having a label.
//
// TODO: Once block labels are implemented, this test should
// include them:
//
// ```
// outer: {
// while (true) {
// break outer;
// }
// skipped_call();
// }
// ```
super::super::test_formatting(
r#"
{
while (true) {
break outer;
}
skipped_call();
}
while (true) {
break;
}
"#,
);
}

3
boa/src/syntax/ast/node/call/mod.rs

@ -12,6 +12,9 @@ use std::fmt;
#[cfg(feature = "deser")] #[cfg(feature = "deser")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
/// Calling the function actually performs the specified actions with the indicated parameters. /// Calling the function actually performs the specified actions with the indicated parameters.
/// ///
/// Defining a function does not execute it. Defining it simply names the function and /// Defining a function does not execute it. Defining it simply names the function and

10
boa/src/syntax/ast/node/call/tests.rs

@ -0,0 +1,10 @@
#[test]
fn fmt() {
super::super::test_formatting(
r#"
call_1(1, 2, 3);
call_2("argument here");
call_3();
"#,
);
}

3
boa/src/syntax/ast/node/conditional/mod.rs

@ -4,3 +4,6 @@ pub mod conditional_op;
pub mod if_node; pub mod if_node;
pub use self::{conditional_op::ConditionalOp, if_node::If}; pub use self::{conditional_op::ConditionalOp, if_node::If};
#[cfg(test)]
mod tests;

13
boa/src/syntax/ast/node/conditional/tests.rs

@ -0,0 +1,13 @@
#[test]
fn fmt() {
super::super::test_formatting(
r#"
let a = true ? 5 : 6;
if (false) {
a = 10;
} else {
a = 20;
}
"#,
);
}

9
boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs

@ -61,8 +61,13 @@ impl ArrowFunctionDecl {
) -> fmt::Result { ) -> fmt::Result {
write!(f, "(")?; write!(f, "(")?;
join_nodes(f, &self.params)?; join_nodes(f, &self.params)?;
f.write_str(") => ")?; if self.body().is_empty() {
self.body.display(f, indentation) f.write_str(") => {}")
} else {
f.write_str(") => {\n")?;
self.body.display(f, indentation + 1)?;
write!(f, "{}}}", " ".repeat(indentation))
}
} }
} }

20
boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs

@ -64,19 +64,17 @@ impl AsyncFunctionDecl {
indentation: usize, indentation: usize,
) -> fmt::Result { ) -> fmt::Result {
match &self.name { match &self.name {
Some(name) => { Some(name) => write!(f, "async function {}(", name)?,
write!(f, "async function {}(", name)?; None => write!(f, "async function (")?,
}
None => {
write!(f, "async function (")?;
}
} }
join_nodes(f, &self.parameters)?; join_nodes(f, &self.parameters)?;
f.write_str(") {{")?; if self.body().is_empty() {
f.write_str(") {}")
self.body.display(f, indentation + 1)?; } else {
f.write_str(") {\n")?;
writeln!(f, "}}") self.body.display(f, indentation + 1)?;
write!(f, "{}}}", " ".repeat(indentation))
}
} }
} }

14
boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs

@ -64,17 +64,19 @@ impl AsyncFunctionExpr {
f: &mut fmt::Formatter<'_>, f: &mut fmt::Formatter<'_>,
indentation: usize, indentation: usize,
) -> fmt::Result { ) -> fmt::Result {
f.write_str("function")?; f.write_str("async function")?;
if let Some(ref name) = self.name { if let Some(ref name) = self.name {
write!(f, " {}", name)?; write!(f, " {}", name)?;
} }
f.write_str("(")?; f.write_str("(")?;
join_nodes(f, &self.parameters)?; join_nodes(f, &self.parameters)?;
f.write_str(") {{")?; if self.body().is_empty() {
f.write_str(") {}")
self.body.display(f, indentation + 1)?; } else {
f.write_str(") {\n")?;
writeln!(f, "}}") self.body.display(f, indentation + 1)?;
write!(f, "{}}}", " ".repeat(indentation))
}
} }
} }

12
boa/src/syntax/ast/node/declaration/function_decl/mod.rs

@ -75,11 +75,13 @@ impl FunctionDecl {
) -> fmt::Result { ) -> fmt::Result {
write!(f, "function {}(", self.name)?; write!(f, "function {}(", self.name)?;
join_nodes(f, &self.parameters)?; join_nodes(f, &self.parameters)?;
f.write_str(") {{")?; if self.body().is_empty() {
f.write_str(") {}")
self.body.display(f, indentation + 1)?; } else {
f.write_str(") {\n")?;
writeln!(f, "}}") self.body.display(f, indentation + 1)?;
write!(f, "{}}}", " ".repeat(indentation))
}
} }
} }

21
boa/src/syntax/ast/node/declaration/function_expr/mod.rs

@ -76,11 +76,24 @@ impl FunctionExpr {
} }
f.write_str("(")?; f.write_str("(")?;
join_nodes(f, &self.parameters)?; join_nodes(f, &self.parameters)?;
f.write_str(") {{")?; f.write_str(") ")?;
self.display_block(f, indentation)
self.body.display(f, indentation + 1)?; }
writeln!(f, "}}") /// Displays the function's body. This includes the curly braces at the start and end.
/// This will not indent the first brace, but will indent the last brace.
pub(in crate::syntax::ast::node) fn display_block(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
if self.body().is_empty() {
f.write_str("{}")
} else {
f.write_str("{\n")?;
self.body.display(f, indentation + 1)?;
write!(f, "{}}}", " ".repeat(indentation))
}
} }
} }

29
boa/src/syntax/ast/node/declaration/tests.rs

@ -10,3 +10,32 @@ fn duplicate_function_name() {
assert_eq!(&exec(scenario), "12"); assert_eq!(&exec(scenario), "12");
} }
#[test]
fn fmt() {
super::super::test_formatting(
r#"
function func(a, b) {
console.log(a);
};
function func_2(a, b) {};
let arrow_func = (a, b) => {
console.log("in multi statement arrow");
console.log(b);
};
async function async_func(a, b) {
console.log(a);
};
pass_async_func(async function(a, b) {
console.log("in async callback", a);
});
pass_func(function(a, b) {
console.log("in callback", a);
});
let arrow_func_2 = (a, b) => {};
async function async_func_2(a, b) {};
pass_async_func(async function(a, b) {});
pass_func(function(a, b) {});
"#,
);
}

3
boa/src/syntax/ast/node/field/mod.rs

@ -4,3 +4,6 @@ pub mod get_const_field;
pub mod get_field; pub mod get_field;
pub use self::{get_const_field::GetConstField, get_field::GetField}; pub use self::{get_const_field::GetConstField, get_field::GetField};
#[cfg(test)]
mod tests;

10
boa/src/syntax/ast/node/field/tests.rs

@ -0,0 +1,10 @@
#[test]
fn fmt() {
super::super::test_formatting(
r#"
a.field_name;
a[5];
a["other_field_name"];
"#,
);
}

14
boa/src/syntax/ast/node/iteration/continue_node/mod.rs

@ -57,15 +57,11 @@ impl Executable for Continue {
impl fmt::Display for Continue { impl fmt::Display for Continue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(f, "continue")?;
f, if let Some(label) = self.label() {
"continue{}", write!(f, " {}", label)?;
if let Some(label) = self.label() { }
format!(" {}", label) Ok(())
} else {
String::new()
}
)
} }
} }

7
boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs

@ -64,9 +64,12 @@ impl DoWhileLoop {
f: &mut fmt::Formatter<'_>, f: &mut fmt::Formatter<'_>,
indentation: usize, indentation: usize,
) -> fmt::Result { ) -> fmt::Result {
write!(f, "do")?; if let Some(ref label) = self.label {
write!(f, "{}: ", label)?;
}
write!(f, "do ")?;
self.body().display(f, indentation)?; self.body().display(f, indentation)?;
write!(f, "while ({})", self.cond()) write!(f, " while ({})", self.cond())
} }
} }

8
boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs

@ -59,9 +59,11 @@ impl ForInLoop {
} }
pub fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result { pub fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
write!(f, "for ({} in {}) {{", self.variable, self.expr,)?; if let Some(ref label) = self.label {
self.body().display(f, indentation + 1)?; write!(f, "{}: ", label)?;
f.write_str("}") }
write!(f, "for ({} in {}) ", self.variable, self.expr)?;
self.body().display(f, indentation)
} }
} }

14
boa/src/syntax/ast/node/iteration/for_loop/mod.rs

@ -69,23 +69,23 @@ impl ForLoop {
f: &mut fmt::Formatter<'_>, f: &mut fmt::Formatter<'_>,
indentation: usize, indentation: usize,
) -> fmt::Result { ) -> fmt::Result {
if let Some(ref label) = self.label {
write!(f, "{}: ", label)?;
}
f.write_str("for (")?; f.write_str("for (")?;
if let Some(init) = self.init() { if let Some(init) = self.init() {
fmt::Display::fmt(init, f)?; fmt::Display::fmt(init, f)?;
} }
f.write_str(";")?; f.write_str("; ")?;
if let Some(condition) = self.condition() { if let Some(condition) = self.condition() {
fmt::Display::fmt(condition, f)?; fmt::Display::fmt(condition, f)?;
} }
f.write_str(";")?; f.write_str("; ")?;
if let Some(final_expr) = self.final_expr() { if let Some(final_expr) = self.final_expr() {
fmt::Display::fmt(final_expr, f)?; fmt::Display::fmt(final_expr, f)?;
} }
writeln!(f, ") {{")?; write!(f, ") ")?;
self.inner.body().display(f, indentation)
self.inner.body().display(f, indentation + 1)?;
write!(f, "}}")
} }
pub fn label(&self) -> Option<&str> { pub fn label(&self) -> Option<&str> {

8
boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs

@ -59,9 +59,11 @@ impl ForOfLoop {
} }
pub fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result { pub fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
write!(f, "for ({} of {}) {{", self.variable, self.iterable)?; if let Some(ref label) = self.label {
self.body().display(f, indentation + 1)?; write!(f, "{}: ", label)?;
f.write_str("}") }
write!(f, "for ({} of {}) ", self.variable, self.iterable)?;
self.body().display(f, indentation)
} }
} }

73
boa/src/syntax/ast/node/iteration/tests.rs

@ -502,3 +502,76 @@ fn for_in_continue_label() {
"#; "#;
assert_eq!(&exec(scenario), "\"00\"") assert_eq!(&exec(scenario), "\"00\"")
} }
#[test]
fn fmt() {
// Labeled and unlabeled for in loops
super::super::test_formatting(
r#"
var str = "";
outer: for (let i in [1, 2]) {
for (let b in [2, 3, 4]) {
if (b === "1") {
continue outer;
}
str = str + b;
};
str = str + i;
};
str;
"#,
);
// Labeled and unlabeled for loops
super::super::test_formatting(
r#"
var str = "";
outer: for (let i = 0; i < 10; ++i) {
for (let j = 3; j < 6; ++j) {
if (j === "1") {
continue outer;
}
str = str + j;
};
str = str + i;
};
str;
"#,
);
// Labeled and unlabeled for of loops
super::super::test_formatting(
r#"
for (i of [1, 2, 3]) {
if (false) {
break;
}
};
label: for (i of [1, 2, 3]) {
if (false) {
break label;
}
};
"#,
);
// Labeled and unlabeled do while loops
super::super::test_formatting(
r#"
do {
break;
} while (true);
label: do {
break label;
} while (true);
"#,
);
// Labeled and unlabeled while loops
super::super::test_formatting(
r#"
while (true) {
break;
}
label: while (true) {
break label;
}
"#,
);
}

3
boa/src/syntax/ast/node/iteration/while_loop/mod.rs

@ -63,6 +63,9 @@ impl WhileLoop {
f: &mut fmt::Formatter<'_>, f: &mut fmt::Formatter<'_>,
indentation: usize, indentation: usize,
) -> fmt::Result { ) -> fmt::Result {
if let Some(ref label) = self.label {
write!(f, "{}: ", label)?;
}
write!(f, "while ({}) ", self.cond())?; write!(f, "while ({}) ", self.cond())?;
self.expr().display(f, indentation) self.expr().display(f, indentation)
} }

53
boa/src/syntax/ast/node/mod.rs

@ -239,14 +239,28 @@ impl Node {
Self::This Self::This
} }
/// Implements the display formatting with indentation. /// Displays the value of the node with the given indentation. For example, an indent
/// level of 2 would produce this:
///
/// ```js
/// function hello() {
/// console.log("hello");
/// }
/// hello();
/// a = 2;
/// ```
fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result { fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
let indent = " ".repeat(indentation); let indent = " ".repeat(indentation);
match *self { match *self {
Self::Block(_) => {} Self::Block(_) => {}
_ => write!(f, "{}", indent)?, _ => write!(f, "{}", indent)?,
} }
self.display_no_indent(f, indentation)
}
/// Implements the display formatting with indentation. This will not prefix the value with
/// any indentation. If you want to prefix this with proper indents, use [`display`](Self::display).
fn display_no_indent(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
match *self { match *self {
Self::Call(ref expr) => Display::fmt(expr, f), Self::Call(ref expr) => Display::fmt(expr, f),
Self::Const(ref c) => write!(f, "{}", c), Self::Const(ref c) => write!(f, "{}", c),
@ -285,7 +299,7 @@ impl Node {
Self::ConstDeclList(ref decl) => Display::fmt(decl, f), Self::ConstDeclList(ref decl) => Display::fmt(decl, f),
Self::AsyncFunctionDecl(ref decl) => decl.display(f, indentation), Self::AsyncFunctionDecl(ref decl) => decl.display(f, indentation),
Self::AsyncFunctionExpr(ref expr) => expr.display(f, indentation), Self::AsyncFunctionExpr(ref expr) => expr.display(f, indentation),
Self::AwaitExpr(ref expr) => expr.display(f, indentation), Self::AwaitExpr(ref expr) => Display::fmt(expr, f),
Self::Empty => write!(f, ";"), Self::Empty => write!(f, ";"),
} }
} }
@ -588,3 +602,38 @@ pub enum MethodDefinitionKind {
unsafe impl Trace for MethodDefinitionKind { unsafe impl Trace for MethodDefinitionKind {
empty_trace!(); empty_trace!();
} }
/// This parses the given source code, and then makes sure that
/// the resulting StatementList is formatted in the same manner
/// as the source code. This is expected to have a preceding
/// newline.
///
/// This is a utility function for tests. It was made in case people
/// are using different indents in their source files. This fixes
/// any strings which may have been changed in a different indent
/// level.
#[cfg(test)]
fn test_formatting(source: &'static str) {
// Remove preceding newline.
let source = &source[1..];
// Find out how much the code is indented
let first_line = &source[..source.find('\n').unwrap()];
let trimmed_first_line = first_line.trim();
let characters_to_remove = first_line.len() - trimmed_first_line.len();
let scenario = source
.lines()
.map(|l| &l[characters_to_remove..]) // Remove preceding whitespace from each line
.collect::<Vec<&'static str>>()
.join("\n");
let result = format!("{}", crate::parse(&scenario, false).unwrap());
if scenario != result {
eprint!("========= Expected:\n{}", scenario);
eprint!("========= Got:\n{}", result);
// Might be helpful to find differing whitespace
eprintln!("========= Expected: {:?}", scenario);
eprintln!("========= Got: {:?}", result);
panic!("parsing test did not give the correct result (see above)");
}
}

3
boa/src/syntax/ast/node/new/mod.rs

@ -11,6 +11,9 @@ use std::fmt;
#[cfg(feature = "deser")] #[cfg(feature = "deser")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
/// The `new` operator lets developers create an instance of a user-defined object type or of /// The `new` operator lets developers create an instance of a user-defined object type or of
/// one of the built-in object types that has a constructor function. /// one of the built-in object types that has a constructor function.
/// ///

9
boa/src/syntax/ast/node/new/tests.rs

@ -0,0 +1,9 @@
#[test]
fn fmt() {
super::super::test_formatting(
r#"
function MyClass() {};
let inst = new MyClass();
"#,
);
}

31
boa/src/syntax/ast/node/object/mod.rs

@ -4,7 +4,7 @@ use crate::{
exec::Executable, exec::Executable,
gc::{Finalize, Trace}, gc::{Finalize, Trace},
property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor}, property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor},
syntax::ast::node::{MethodDefinitionKind, Node, PropertyDefinition}, syntax::ast::node::{join_nodes, MethodDefinitionKind, Node, PropertyDefinition},
BoaProfiler, Context, Result, Value, BoaProfiler, Context, Result, Value,
}; };
use std::fmt; use std::fmt;
@ -15,6 +15,9 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "vm")] #[cfg(feature = "vm")]
use crate::vm::{compilation::CodeGen, Compiler, Instruction}; use crate::vm::{compilation::CodeGen, Compiler, Instruction};
#[cfg(test)]
mod tests;
/// Objects in JavaScript may be defined as an unordered collection of related data, of /// Objects in JavaScript may be defined as an unordered collection of related data, of
/// primitive or reference types, in the form of “key: value” pairs. /// primitive or reference types, in the form of “key: value” pairs.
/// ///
@ -53,24 +56,36 @@ impl Object {
indent: usize, indent: usize,
) -> fmt::Result { ) -> fmt::Result {
f.write_str("{\n")?; f.write_str("{\n")?;
let indentation = " ".repeat(indent + 1);
for property in self.properties().iter() { for property in self.properties().iter() {
match property { match property {
PropertyDefinition::IdentifierReference(key) => { PropertyDefinition::IdentifierReference(key) => {
write!(f, "{} {},", indent, key)?; writeln!(f, "{}{},", indentation, key)?;
} }
PropertyDefinition::Property(key, value) => { PropertyDefinition::Property(key, value) => {
write!(f, "{} {}: {},", indent, key, value)?; write!(f, "{}{}: ", indentation, key,)?;
value.display_no_indent(f, indent + 1)?;
writeln!(f, ",")?;
} }
PropertyDefinition::SpreadObject(key) => { PropertyDefinition::SpreadObject(key) => {
write!(f, "{} ...{},", indent, key)?; writeln!(f, "{}...{},", indentation, key)?;
} }
PropertyDefinition::MethodDefinition(_kind, _key, _node) => { PropertyDefinition::MethodDefinition(kind, key, node) => {
// TODO: Implement display for PropertyDefinition::MethodDefinition. write!(f, "{}", indentation)?;
unimplemented!("Display for PropertyDefinition::MethodDefinition"); match &kind {
MethodDefinitionKind::Get => write!(f, "get ")?,
MethodDefinitionKind::Set => write!(f, "set ")?,
MethodDefinitionKind::Ordinary => (),
}
write!(f, "{}(", key)?;
join_nodes(f, &node.parameters())?;
write!(f, ") ")?;
node.display_block(f, indent + 1)?;
writeln!(f, ",")?;
} }
} }
} }
f.write_str("}") write!(f, "{}}}", " ".repeat(indent))
} }
} }

34
boa/src/syntax/ast/node/object/tests.rs

@ -0,0 +1,34 @@
#[test]
fn fmt() {
super::super::test_formatting(
r#"
let other = {
c: 10,
};
let inst = {
val: 5,
b: "hello world",
nested: {
a: 5,
b: 6,
},
...other,
say_hi: function() {
console.log("hello!");
},
get a() {
return this.val + 1;
},
set a(new_value) {
this.val = new_value;
},
say_hello(msg) {
console.log("hello " + msg);
},
};
inst.a = 20;
inst.a;
inst.say_hello("humans");
"#,
);
}

25
boa/src/syntax/ast/node/operator/tests.rs

@ -113,3 +113,28 @@ fn logical_assignment() {
assert_eq!(&exec(scenario), "20"); assert_eq!(&exec(scenario), "20");
} }
#[test]
fn fmt() {
super::super::test_formatting(
r#"
let a = 20;
a += 10;
a -= 10;
a *= 10;
a **= 10;
a /= 10;
a %= 10;
a &= 10;
a |= 10;
a ^= 10;
a <<= 10;
a >>= 10;
a >>>= 10;
a &&= 10;
a ||= 10;
a ??= 10;
a;
"#,
);
}

3
boa/src/syntax/ast/node/return_smt/mod.rs

@ -9,6 +9,9 @@ use std::fmt;
#[cfg(feature = "deser")] #[cfg(feature = "deser")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
/// The `return` statement ends function execution and specifies a value to be returned to the /// The `return` statement ends function execution and specifies a value to be returned to the
/// function caller. /// function caller.
/// ///

16
boa/src/syntax/ast/node/return_smt/tests.rs

@ -0,0 +1,16 @@
#[test]
fn fmt() {
super::super::test_formatting(
r#"
function say_hello(msg) {
if (msg === "") {
return 0;
}
console.log("hello " + msg);
return;
};
say_hello("");
say_hello("world");
"#,
);
}

16
boa/src/syntax/ast/node/spread/tests.rs

@ -29,3 +29,19 @@ fn spread_with_call() {
"#; "#;
assert_eq!(&exec(scenario), r#""message""#); assert_eq!(&exec(scenario), r#""message""#);
} }
#[test]
fn fmt() {
super::super::test_formatting(
r#"
function f(m) {
return m;
};
function g(...args) {
return f(...args);
};
let a = g("message");
a;
"#,
);
}

5
boa/src/syntax/ast/node/statement_list/mod.rs

@ -41,11 +41,10 @@ impl StatementList {
f: &mut fmt::Formatter<'_>, f: &mut fmt::Formatter<'_>,
indentation: usize, indentation: usize,
) -> fmt::Result { ) -> fmt::Result {
let indent = " ".repeat(indentation);
// Print statements // Print statements
for node in self.items.iter() { for node in self.items.iter() {
f.write_str(&indent)?; // We rely on the node to add the correct indent.
node.display(f, indentation + 1)?; node.display(f, indentation)?;
match node { match node {
Node::Block(_) | Node::If(_) | Node::Switch(_) | Node::WhileLoop(_) => {} Node::Block(_) | Node::If(_) | Node::Switch(_) | Node::WhileLoop(_) => {}

13
boa/src/syntax/ast/node/switch/mod.rs

@ -105,19 +105,20 @@ impl Switch {
pub(in crate::syntax::ast::node) fn display( pub(in crate::syntax::ast::node) fn display(
&self, &self,
f: &mut fmt::Formatter<'_>, f: &mut fmt::Formatter<'_>,
indent: usize, indentation: usize,
) -> fmt::Result { ) -> fmt::Result {
let indent = " ".repeat(indentation);
writeln!(f, "switch ({}) {{", self.val())?; writeln!(f, "switch ({}) {{", self.val())?;
for e in self.cases().iter() { for e in self.cases().iter() {
writeln!(f, "{}case {}:", indent, e.condition())?; writeln!(f, "{} case {}:", indent, e.condition())?;
e.body().display(f, indent)?; e.body().display(f, indentation + 2)?;
} }
if let Some(ref default) = self.default { if let Some(ref default) = self.default {
writeln!(f, "{}default:", indent)?; writeln!(f, "{} default:", indent)?;
default.display(f, indent + 1)?; default.display(f, indentation + 2)?;
} }
writeln!(f, "{}}}", indent) write!(f, "{}}}", indent)
} }
} }

36
boa/src/syntax/ast/node/switch/tests.rs

@ -206,3 +206,39 @@ fn bigger_switch_example() {
assert_eq!(&exec(&scenario), val); assert_eq!(&exec(&scenario), val);
} }
} }
#[test]
fn fmt() {
super::super::test_formatting(
r#"
let a = 3;
let b = "unknown";
switch (a) {
case 0:
b = "Mon";
break;
case 1:
b = "Tue";
break;
case 2:
b = "Wed";
break;
case 3:
b = "Thurs";
break;
case 4:
b = "Fri";
break;
case 5:
b = "Sat";
break;
case 6:
b = "Sun";
break;
default:
b = "Unknown";
}
b;
"#,
);
}

17
boa/src/syntax/ast/node/template/tests.rs

@ -29,3 +29,20 @@ fn tagged_template() {
r#"[ "result: ", " & ", "", "result: ", " \x26 ", "", 10, 20 ]"# r#"[ "result: ", " & ", "", "result: ", " \x26 ", "", 10, 20 ]"#
); );
} }
#[test]
fn fmt() {
super::super::test_formatting(
r#"
function tag(t, ...args) {
let a = [];
a = a.concat([t[0], t[1], t[2]]);
a = a.concat([t.raw[0], t.raw[1], t.raw[2]]);
a = a.concat([args[0], args[1]]);
return a;
};
let a = 10;
tag`result: ${a} \x26 ${a + 10}`;
"#,
);
}

3
boa/src/syntax/ast/node/throw/mod.rs

@ -9,6 +9,9 @@ use std::fmt;
#[cfg(feature = "deser")] #[cfg(feature = "deser")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(test)]
mod tests;
/// The `throw` statement throws a user-defined exception. /// The `throw` statement throws a user-defined exception.
/// ///
/// Syntax: `throw expression;` /// Syntax: `throw expression;`

12
boa/src/syntax/ast/node/throw/tests.rs

@ -0,0 +1,12 @@
#[test]
fn fmt() {
super::super::test_formatting(
r#"
try {
throw "hello";
} catch(e) {
console.log(e);
};
"#,
);
}

20
boa/src/syntax/ast/node/try_node/tests.rs

@ -93,3 +93,23 @@ fn catch_binding_finally() {
"#; "#;
assert_eq!(&exec(scenario), "30"); assert_eq!(&exec(scenario), "30");
} }
#[test]
fn fmt() {
super::super::test_formatting(
r#"
try {
throw "hello";
} catch(e) {
console.log(e);
} finally {
console.log("things");
};
try {
throw "hello";
} catch {
console.log("something went wrong");
};
"#,
);
}

Loading…
Cancel
Save