Skip to content

Commit

Permalink
Move ast parsing tests from doctests
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed May 6, 2023
1 parent e1137a4 commit e7aa050
Show file tree
Hide file tree
Showing 60 changed files with 959 additions and 958 deletions.
2 changes: 1 addition & 1 deletion crates/rune-macros/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl FunctionAttrs {
return Err(syn::Error::new_spanned(ident, "Unsupported option"));
}

if input.parse::<Option<syn::Token![,]>>().is_none() {
if input.parse::<Option<syn::Token![,]>>()?.is_none() {
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/rune-macros/src/macro_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl Config {
return Err(syn::Error::new_spanned(ident, "Unsupported option"));
}

if input.parse::<Option<syn::Token![,]>>().is_none() {
if input.parse::<Option<syn::Token![,]>>()?.is_none() {
break;
}
}
Expand Down
92 changes: 42 additions & 50 deletions crates/rune/src/ast/attribute.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,48 @@
use crate::ast::prelude::*;

/// Attribute like `#[derive(Debug)]`
///
/// # Examples
///
/// ```
/// use rune::{ast, testing};
#[test]
fn ast_parse() {
use crate::testing::rt;

rt::<ast::Attribute>("#[foo = \"foo\"]");
rt::<ast::Attribute>("#[foo()]");
rt::<ast::Attribute>("#![foo]");
rt::<ast::Attribute>("#![cfg(all(feature = \"potato\"))]");
rt::<ast::Attribute>("#[x+1]");

const TEST_STRINGS: &[&str] = &[
"#[foo]",
"#[a::b::c]",
"#[foo = \"hello world\"]",
"#[foo = 1]",
"#[foo = 1.3]",
"#[foo = true]",
"#[foo = b\"bytes\"]",
"#[foo = (1, 2, \"string\")]",
"#[foo = #{\"a\": 1} ]",
r#"#[foo = Fred {"a": 1} ]"#,
r#"#[foo = a::Fred {"a": #{ "b": 2 } } ]"#,
"#[bar()]",
"#[bar(baz)]",
"#[derive(Debug, PartialEq, PartialOrd)]",
"#[tracing::instrument(skip(non_debug))]",
"#[zanzibar(a = \"z\", both = false, sasquatch::herring)]",
r#"#[doc = "multiline \
docs are neat"
]"#,
];

for s in TEST_STRINGS.iter() {
rt::<ast::Attribute>(s);
let withbang = s.replacen("#[", "#![", 1);
rt::<ast::Attribute>(&withbang);
}
}

/// Attributes like:
///
/// testing::roundtrip::<ast::Attribute>("#[foo = \"foo\"]");
/// testing::roundtrip::<ast::Attribute>("#[foo()]");
/// testing::roundtrip::<ast::Attribute>("#![foo]");
/// testing::roundtrip::<ast::Attribute>("#![cfg(all(feature = \"potato\"))]");
/// testing::roundtrip::<ast::Attribute>("#[x+1]");
/// ```
/// * `#[derive(Debug)]`.
/// * `#![doc = "test"]`.
#[derive(Debug, Clone, PartialEq, Eq, ToTokens, Spanned)]
#[non_exhaustive]
pub struct Attribute {
Expand Down Expand Up @@ -142,41 +172,3 @@ impl Peek for OuterAttribute {
}
}
}

#[cfg(test)]
mod tests {
use crate::ast;
use crate::parse::parse_all;
use crate::SourceId;

#[test]
fn test_parse_attribute() {
const TEST_STRINGS: &[&str] = &[
"#[foo]",
"#[a::b::c]",
"#[foo = \"hello world\"]",
"#[foo = 1]",
"#[foo = 1.3]",
"#[foo = true]",
"#[foo = b\"bytes\"]",
"#[foo = (1, 2, \"string\")]",
"#[foo = #{\"a\": 1} ]",
r#"#[foo = Fred {"a": 1} ]"#,
r#"#[foo = a::Fred {"a": #{ "b": 2 } } ]"#,
"#[bar()]",
"#[bar(baz)]",
"#[derive(Debug, PartialEq, PartialOrd)]",
"#[tracing::instrument(skip(non_debug))]",
"#[zanzibar(a = \"z\", both = false, sasquatch::herring)]",
r#"#[doc = "multiline \
docs are neat"
]"#,
];

for s in TEST_STRINGS.iter() {
parse_all::<ast::Attribute>(s, SourceId::empty(), false).expect(s);
let withbang = s.replacen("#[", "#![", 1);
parse_all::<ast::Attribute>(&withbang, SourceId::empty(), false).expect(&withbang);
}
}
}
65 changes: 35 additions & 30 deletions crates/rune/src/ast/block.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,41 @@
use crate::ast::prelude::*;

/// A block of expressions.
///
/// ```
/// use rune::{ast, testing};
///
/// let expr = testing::roundtrip::<ast::ExprBlock>("{}");
/// assert_eq!(expr.block.statements.len(), 0);
///
/// let expr = testing::roundtrip::<ast::ExprBlock>("{ 42 }");
/// assert_eq!(expr.block.statements.len(), 1);
///
/// let block = testing::roundtrip::<ast::Block>("{ foo }");
/// assert_eq!(block.statements.len(), 1);
///
/// let block = testing::roundtrip::<ast::Block>("{ foo; }");
/// assert_eq!(block.statements.len(), 1);
///
/// let expr = testing::roundtrip::<ast::ExprBlock>("#[retry] { 42 }");
/// assert_eq!(expr.block.statements.len(), 1);
/// assert_eq!(expr.attributes.len(), 1);
///
/// let block = testing::roundtrip::<ast::Block>(r#"
/// {
/// let foo = 42;
/// let bar = "string";
/// baz
/// }
/// "#);
#[test]
fn ast_parse() {
use crate::testing::rt;

let expr = rt::<ast::ExprBlock>("{}");
assert_eq!(expr.block.statements.len(), 0);

let expr = rt::<ast::ExprBlock>("{ 42 }");
assert_eq!(expr.block.statements.len(), 1);

let block = rt::<ast::Block>("{ foo }");
assert_eq!(block.statements.len(), 1);

let block = rt::<ast::Block>("{ foo; }");
assert_eq!(block.statements.len(), 1);

let expr = rt::<ast::ExprBlock>("#[retry] { 42 }");
assert_eq!(expr.block.statements.len(), 1);
assert_eq!(expr.attributes.len(), 1);

let block = rt::<ast::Block>(
r#"
{
let foo = 42;
let bar = "string";
baz
}
"#,
);

assert_eq!(block.statements.len(), 3);
}

/// A block of statements.
///
/// assert_eq!(block.statements.len(), 3);
/// ```
/// * `{ (<stmt>)* }`.
#[derive(Debug, Clone, PartialEq, Eq, ToTokens, Spanned, Opaque)]
#[non_exhaustive]
pub struct Block {
Expand Down
18 changes: 10 additions & 8 deletions crates/rune/src/ast/condition.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use crate::ast::prelude::*;

#[test]
fn ast_parse() {
use crate::testing::rt;

rt::<ast::Condition>("true");
rt::<ast::Condition>("let [a, ..] = v");
}

/// The condition in an if statement.
///
/// # Examples
///
/// ```
/// use rune::{ast, testing};
///
/// testing::roundtrip::<ast::Condition>("true");
/// testing::roundtrip::<ast::Condition>("let [a, ..] = v");
/// ```
/// * `true`.
/// * `let Some(<pat>) = <expr>`.
#[derive(Debug, Clone, PartialEq, Eq, ToTokens, Spanned)]
#[non_exhaustive]
pub enum Condition {
Expand Down
128 changes: 65 additions & 63 deletions crates/rune/src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,50 @@ use core::ops;

use crate::ast::prelude::*;

#[test]
fn ast_parse() {
use crate::testing::rt;

rt::<ast::Expr>("()");
rt::<ast::Expr>("foo[\"foo\"]");
rt::<ast::Expr>("foo.bar()");
rt::<ast::Expr>("var()");
rt::<ast::Expr>("var");
rt::<ast::Expr>("42");
rt::<ast::Expr>("1 + 2 / 3 - 4 * 1");
rt::<ast::Expr>("foo[\"bar\"]");
rt::<ast::Expr>("let var = 42");
rt::<ast::Expr>("let var = \"foo bar\"");
rt::<ast::Expr>("var[\"foo\"] = \"bar\"");
rt::<ast::Expr>("let var = objects[\"foo\"] + 1");
rt::<ast::Expr>("var = 42");

let expr = rt::<ast::Expr>(
r#"
if 1 { } else { if 2 { } else { } }
"#,
);
assert!(matches!(expr, ast::Expr::If(..)));

// Chained function calls.
rt::<ast::Expr>("foo.bar.baz()");
rt::<ast::Expr>("foo[0][1][2]");
rt::<ast::Expr>("foo.bar()[0].baz()[1]");

rt::<ast::Expr>("42 is int::int");
rt::<ast::Expr>("{ let x = 1; x }");

let expr = rt::<ast::Expr>("#[cfg(debug_assertions)] { assert_eq(x, 32); }");
assert!(
matches!(expr, ast::Expr::Block(b) if b.attributes.len() == 1 && b.block.statements.len() == 1)
);

rt::<ast::Expr>("#{\"foo\": b\"bar\"}");
rt::<ast::Expr>("Disco {\"never_died\": true }");
rt::<ast::Expr>("(false, 1, 'n')");
rt::<ast::Expr>("[false, 1, 'b']");
}

/// Indicator that an expression should be parsed with an eager brace.
#[derive(Debug, Clone, Copy)]
pub(crate) struct EagerBrace(bool);
Expand Down Expand Up @@ -347,48 +391,6 @@ impl Expr {
}
}

/// Parsing a block expression.
///
/// # Examples
///
/// ```
/// use rune::{ast, testing};
///
/// testing::roundtrip::<ast::Expr>("()");
/// testing::roundtrip::<ast::Expr>("foo[\"foo\"]");
/// testing::roundtrip::<ast::Expr>("foo.bar()");
/// testing::roundtrip::<ast::Expr>("var()");
/// testing::roundtrip::<ast::Expr>("var");
/// testing::roundtrip::<ast::Expr>("42");
/// testing::roundtrip::<ast::Expr>("1 + 2 / 3 - 4 * 1");
/// testing::roundtrip::<ast::Expr>("foo[\"bar\"]");
/// testing::roundtrip::<ast::Expr>("let var = 42");
/// testing::roundtrip::<ast::Expr>("let var = \"foo bar\"");
/// testing::roundtrip::<ast::Expr>("var[\"foo\"] = \"bar\"");
/// testing::roundtrip::<ast::Expr>("let var = objects[\"foo\"] + 1");
/// testing::roundtrip::<ast::Expr>("var = 42");
///
/// let expr = testing::roundtrip::<ast::Expr>(r#"
/// if 1 { } else { if 2 { } else { } }
/// "#);
/// assert!(matches!(expr, ast::Expr::If(..)));
///
/// // Chained function calls.
/// testing::roundtrip::<ast::Expr>("foo.bar.baz()");
/// testing::roundtrip::<ast::Expr>("foo[0][1][2]");
/// testing::roundtrip::<ast::Expr>("foo.bar()[0].baz()[1]");
///
/// testing::roundtrip::<ast::Expr>("42 is int::int");
/// testing::roundtrip::<ast::Expr>("{ let x = 1; x }");
///
/// let expr = testing::roundtrip::<ast::Expr>("#[cfg(debug_assertions)] { assert_eq(x, 32); }");
/// assert!(matches!(expr, ast::Expr::Block(b) if b.attributes.len() == 1 && b.block.statements.len() == 1));
///
/// testing::roundtrip::<ast::Expr>("#{\"foo\": b\"bar\"}");
/// testing::roundtrip::<ast::Expr>("Disco {\"never_died\": true }");
/// testing::roundtrip::<ast::Expr>("(false, 1, 'n')");
/// testing::roundtrip::<ast::Expr>("[false, 1, 'b']");
/// ```
impl Parse for Expr {
fn parse(p: &mut Parser<'_>) -> Result<Self> {
Self::parse_with(p, EAGER_BRACE, EAGER_BINARY, CALLABLE)
Expand Down Expand Up @@ -793,47 +795,47 @@ fn paren_group(p: &mut Parser<'_>, attributes: Vec<ast::Attribute>) -> Result<Ex
#[cfg(test)]
mod tests {
use crate::ast;
use crate::testing::roundtrip;
use crate::testing::rt;

#[test]
fn test_expr_if() {
let expr = roundtrip::<ast::Expr>(r#"if true {} else {}"#);
let expr = rt::<ast::Expr>(r#"if true {} else {}"#);
assert!(matches!(expr, ast::Expr::If(..)));

let expr = roundtrip::<ast::Expr>("if 1 { } else { if 2 { } else { } }");
let expr = rt::<ast::Expr>("if 1 { } else { if 2 { } else { } }");
assert!(matches!(expr, ast::Expr::If(..)));
}

#[test]
fn test_expr_while() {
let expr = roundtrip::<ast::Expr>(r#"while true {}"#);
let expr = rt::<ast::Expr>(r#"while true {}"#);
assert!(matches!(expr, ast::Expr::While(..)));
}

#[test]
fn test_expr() {
roundtrip::<ast::Expr>("foo[\"foo\"]");
roundtrip::<ast::Expr>("foo.bar()");
roundtrip::<ast::Expr>("var()");
roundtrip::<ast::Expr>("var");
roundtrip::<ast::Expr>("42");
roundtrip::<ast::Expr>("1 + 2 / 3 - 4 * 1");
roundtrip::<ast::Expr>("foo[\"bar\"]");
roundtrip::<ast::Expr>("let var = 42");
roundtrip::<ast::Expr>("let var = \"foo bar\"");
roundtrip::<ast::Expr>("var[\"foo\"] = \"bar\"");
roundtrip::<ast::Expr>("let var = objects[\"foo\"] + 1");
roundtrip::<ast::Expr>("var = 42");
rt::<ast::Expr>("foo[\"foo\"]");
rt::<ast::Expr>("foo.bar()");
rt::<ast::Expr>("var()");
rt::<ast::Expr>("var");
rt::<ast::Expr>("42");
rt::<ast::Expr>("1 + 2 / 3 - 4 * 1");
rt::<ast::Expr>("foo[\"bar\"]");
rt::<ast::Expr>("let var = 42");
rt::<ast::Expr>("let var = \"foo bar\"");
rt::<ast::Expr>("var[\"foo\"] = \"bar\"");
rt::<ast::Expr>("let var = objects[\"foo\"] + 1");
rt::<ast::Expr>("var = 42");

// Chained function calls.
roundtrip::<ast::Expr>("foo.bar.baz()");
roundtrip::<ast::Expr>("foo[0][1][2]");
roundtrip::<ast::Expr>("foo.bar()[0].baz()[1]");
roundtrip::<ast::Expr>("42 is int::int");
rt::<ast::Expr>("foo.bar.baz()");
rt::<ast::Expr>("foo[0][1][2]");
rt::<ast::Expr>("foo.bar()[0].baz()[1]");
rt::<ast::Expr>("42 is int::int");
}

#[test]
fn test_macro_call_chain() {
roundtrip::<ast::Expr>("format!(\"{}\", a).bar()");
rt::<ast::Expr>("format!(\"{}\", a).bar()");
}
}
Loading

0 comments on commit e7aa050

Please sign in to comment.