use crate::syntax::{SyntaxKind, SyntaxNode};
pub struct ScopeModifier {
pub kind: SyntaxKind,
pub outer: SyntaxNode,
}
pub fn scope_modifier(const_stmt: &SyntaxNode) -> Option<ScopeModifier> {
let is_modifier = |kind| matches!(kind, SyntaxKind::GLOBAL_STMT | SyntaxKind::LOCAL_STMT);
if let Some(parent) = const_stmt.parent().filter(|p| is_modifier(p.kind())) {
return Some(ScopeModifier {
kind: parent.kind(),
outer: parent,
});
}
let child = const_stmt.children().find(|c| is_modifier(c.kind()))?;
Some(ScopeModifier {
kind: child.kind(),
outer: const_stmt.clone(),
})
}
pub fn is_unlowered_context(node: &SyntaxNode) -> bool {
matches!(
node.kind(),
SyntaxKind::QUOTE_EXPR | SyntaxKind::QUOTE_SYM | SyntaxKind::MACRO_CALL
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::parse;
fn const_stmt(src: &str) -> SyntaxNode {
parse(src)
.cst
.descendants()
.find(|n| n.kind() == SyntaxKind::CONST_STMT)
.expect("a `const` statement")
}
#[test]
fn scope_modifier_reads_both_orders() {
for (src, kind) in [
("global const x = 1\n", SyntaxKind::GLOBAL_STMT),
("const global x = 1\n", SyntaxKind::GLOBAL_STMT),
("local const x = 1\n", SyntaxKind::LOCAL_STMT),
("const local x = 1\n", SyntaxKind::LOCAL_STMT),
] {
let modifier = scope_modifier(&const_stmt(src)).expect("a modifier");
assert_eq!(modifier.kind, kind, "for {src:?}");
assert_eq!(modifier.outer.text().to_string(), src.trim_end());
}
}
#[test]
fn scope_modifier_is_none_for_a_plain_const() {
assert!(scope_modifier(&const_stmt("const x = 1\n")).is_none());
assert!(scope_modifier(&const_stmt("mutable struct S\n const x::Int\nend\n")).is_none());
}
}