use super::*;
use crate::parse_only;
fn parse(source: &str) -> Result<File, Vec<crate::error::CompilerError>> {
parse_only(source)
}
fn find_offset_of(source: &str, pattern: &str) -> Result<usize, Box<dyn std::error::Error>> {
source
.find(pattern)
.ok_or_else(|| format!("Pattern {pattern:?} not found in source").into())
}
#[test]
fn test_find_struct_definition() -> Result<(), Box<dyn std::error::Error>> {
let source = "struct User { name: String }";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "User")?;
let ctx = find_node_at_offset(&file, offset);
let is_struct = matches!(ctx.node, NodeAtPosition::StructDef(_))
|| ctx
.parents
.iter()
.any(|p| matches!(p, NodeAtPosition::StructDef(_)));
if !is_struct && !matches!(ctx.node, NodeAtPosition::Identifier(_)) {
return Err(format!("Expected StructDef or Identifier, got {:?}", ctx.node).into());
}
Ok(())
}
#[test]
fn test_find_trait_definition() -> Result<(), Box<dyn std::error::Error>> {
let source = "trait Named { name: String }";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "Named")?;
let ctx = find_node_at_offset(&file, offset);
let is_trait = matches!(ctx.node, NodeAtPosition::TraitDef(_))
|| ctx
.parents
.iter()
.any(|p| matches!(p, NodeAtPosition::TraitDef(_)));
if !is_trait && !matches!(ctx.node, NodeAtPosition::Identifier(_)) {
return Err(format!("Expected TraitDef or Identifier, got {:?}", ctx.node).into());
}
Ok(())
}
#[test]
fn test_find_enum_definition() -> Result<(), Box<dyn std::error::Error>> {
let source = "enum Status { active, inactive }";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "Status")?;
let ctx = find_node_at_offset(&file, offset);
let is_enum = matches!(ctx.node, NodeAtPosition::EnumDef(_))
|| ctx
.parents
.iter()
.any(|p| matches!(p, NodeAtPosition::EnumDef(_)));
if !is_enum && !matches!(ctx.node, NodeAtPosition::Identifier(_)) {
return Err(format!("Expected EnumDef or Identifier, got {:?}", ctx.node).into());
}
Ok(())
}
#[test]
fn test_find_field_in_struct() -> Result<(), Box<dyn std::error::Error>> {
let source = "struct User { name: String }";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "name")?;
let ctx = find_node_at_offset(&file, offset);
if !matches!(
ctx.node,
NodeAtPosition::StructField(_) | NodeAtPosition::Identifier(_)
) {
return Err(format!("Expected StructField or Identifier, got {:?}", ctx.node).into());
}
Ok(())
}
#[test]
fn test_find_type_in_field() -> Result<(), Box<dyn std::error::Error>> {
let source = "struct User { name: String }";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "String")?;
let ctx = find_node_at_offset(&file, offset);
let is_valid = matches!(
ctx.node,
NodeAtPosition::Type(_)
| NodeAtPosition::Identifier(_)
| NodeAtPosition::StructField(_)
| NodeAtPosition::StructDef(_)
);
if !is_valid {
return Err(format!(
"Expected Type/Identifier/StructField/StructDef, got {:?}",
ctx.node
)
.into());
}
Ok(())
}
#[test]
fn test_find_let_binding() -> Result<(), Box<dyn std::error::Error>> {
let source = "let x = 42";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "x")?;
let ctx = find_node_at_offset(&file, offset);
if !matches!(ctx.node, NodeAtPosition::Identifier(_)) {
return Err(format!("Expected Identifier, got {:?}", ctx.node).into());
}
if !ctx
.parents
.iter()
.any(|n| matches!(n, NodeAtPosition::LetBinding(_)))
{
return Err("Expected LetBinding in parents".into());
}
Ok(())
}
#[test]
fn test_enclosing_definition_in_struct_field() -> Result<(), Box<dyn std::error::Error>> {
let source = "struct User { name: String }";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "name")?;
let ctx = find_node_at_offset(&file, offset);
let enclosing = ctx.enclosing_definition();
if enclosing.is_none() {
return Err("Expected enclosing definition but got None".into());
}
if !matches!(enclosing, Some(NodeAtPosition::StructDef(_))) {
return Err(format!("Expected StructDef enclosing, got {enclosing:?}").into());
}
Ok(())
}
#[test]
fn test_enclosing_definition_outside_struct() -> Result<(), Box<dyn std::error::Error>> {
let source = "let x = 42";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "42")?;
let ctx = find_node_at_offset(&file, offset);
let enclosing = ctx.enclosing_definition();
if enclosing.is_some() {
return Err(format!("Expected no enclosing definition, got {enclosing:?}").into());
}
Ok(())
}
#[test]
fn test_is_in_expression() -> Result<(), Box<dyn std::error::Error>> {
let source = "let x = 1 + 2";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "1 +")?;
let ctx = find_node_at_offset(&file, offset);
let has_expression = ctx.is_in_expression()
|| matches!(ctx.node, NodeAtPosition::Expression(_))
|| ctx
.parents
.iter()
.any(|p| matches!(p, NodeAtPosition::Expression(_)));
if !has_expression && !matches!(ctx.node, NodeAtPosition::LetBinding(_)) {
return Err(format!("Expected expression context, got {:?}", ctx.node).into());
}
Ok(())
}
#[test]
fn test_is_in_type_position() -> Result<(), Box<dyn std::error::Error>> {
let source = "struct User { name: String }";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "String")?;
let ctx = find_node_at_offset(&file, offset);
let _ = ctx.is_in_type_position();
Ok(())
}
#[test]
fn test_find_enum_variant() -> Result<(), Box<dyn std::error::Error>> {
let source = "enum Status { active, inactive }";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "active")?;
let ctx = find_node_at_offset(&file, offset);
if !matches!(
ctx.node,
NodeAtPosition::EnumVariant(_) | NodeAtPosition::Identifier(_)
) {
return Err(format!("Expected EnumVariant or Identifier, got {:?}", ctx.node).into());
}
Ok(())
}
#[test]
fn test_find_node_at_file_start() -> Result<(), Box<dyn std::error::Error>> {
let source = "struct A { }";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let ctx = find_node_at_offset(&file, 0);
if matches!(ctx.node, NodeAtPosition::None) {
return Err("Expected some node at offset 0, got None".into());
}
Ok(())
}
#[test]
fn test_find_node_past_end() -> Result<(), Box<dyn std::error::Error>> {
let source = "struct A { }";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let ctx = find_node_at_offset(&file, 10000);
if !matches!(ctx.node, NodeAtPosition::File | NodeAtPosition::None) {
return Err(format!("Expected File or None past end, got {:?}", ctx.node).into());
}
Ok(())
}
#[test]
fn test_parents_chain() -> Result<(), Box<dyn std::error::Error>> {
let source = r"
struct User {
name: String,
age: I32
}
";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "age")?;
let ctx = find_node_at_offset(&file, offset);
let has_struct_parent = ctx
.parents
.iter()
.any(|p| matches!(p, NodeAtPosition::StructDef(_)));
if !has_struct_parent {
return Err("Expected StructDef in parents chain".into());
}
Ok(())
}
#[test]
fn test_find_use_statement() -> Result<(), Box<dyn std::error::Error>> {
let source = "use foo::bar";
let file = parse(source).map_err(|e| format!("parse failed: {e:?}"))?;
let offset = find_offset_of(source, "foo")?;
let ctx = find_node_at_offset(&file, offset);
if !matches!(
ctx.node,
NodeAtPosition::Identifier(_) | NodeAtPosition::UseStatement(_)
) {
return Err(format!("Expected Identifier or UseStatement, got {:?}", ctx.node).into());
}
Ok(())
}