use std::collections::HashSet;
use std::rc::Rc;
use tree_sitter::Node;
use super::rs_sites::Ctx;
use crate::parse;
#[derive(Default)]
pub(super) struct Names {
pub values: HashSet<String>,
pub items: HashSet<String>,
}
#[derive(Default)]
pub(super) struct TreeNames {
pub inside: Names,
pub outward: Names,
pub own_self: bool,
}
impl TreeNames {
pub fn narrow(self, ctx: &Ctx) -> Ctx {
let mut inner = ctx.clone();
inner.invocation_locals = Rc::new(self.inside.values);
inner.invocation_shadow = Rc::new(self.inside.items);
if self.own_self {
inner.self_impl = None;
inner.self_trait = None;
}
inner
}
}
const ITEM_KEYWORDS: &[&str] = &[
"fn", "struct", "enum", "union", "trait", "type", "mod", "const", "static",
];
pub(super) fn is_statement(invocation: Node) -> bool {
invocation
.parent()
.is_some_and(|p| matches!(p.kind(), "expression_statement" | "block"))
}
pub(super) fn tree_names(invocation: Node, src: &[u8]) -> TreeNames {
let mut names = TreeNames::default();
let mut stack: Vec<Node> = Vec::new();
let mut cursor = invocation.walk();
for child in invocation.named_children(&mut cursor) {
if child.kind() == "token_tree" {
stack.push(child);
}
}
while let Some(tree) = stack.pop() {
let mut cursor = tree.walk();
let tokens: Vec<Node> = tree.children(&mut cursor).collect();
let level = Level {
tokens: &tokens,
fields: struct_body(tree, src),
};
level.read(src, &mut names);
stack.extend(tokens.iter().filter(|t| t.kind() == "token_tree").copied());
}
names
}
struct Level<'a, 't> {
tokens: &'a [Node<'t>],
fields: bool,
}
fn struct_body(tree: Node, src: &[u8]) -> bool {
let braced = tree.child(0).is_some_and(|c| c.kind() == "{");
braced
&& tree.prev_sibling().is_some_and(|p| {
is_name(p.kind()) && text_of(p, src).starts_with(|c: char| c.is_uppercase())
})
}
fn is_name(kind: &str) -> bool {
matches!(kind, "identifier" | "default" | "union" | "auto" | "raw")
}
fn text_of(node: Node, src: &[u8]) -> String {
let text = parse::node_text(node, src);
text.strip_prefix("r#").unwrap_or(&text).to_string()
}
impl Level<'_, '_> {
fn read(&self, src: &[u8], names: &mut TreeNames) {
let tokens = self.tokens;
let generics = generic_params(tokens);
let mut angle: i64 = 0;
let mut odd_pipes = false;
for (i, token) in tokens.iter().enumerate() {
match token.kind() {
"impl" | "trait" => names.own_self = true,
"use" => self.imports(i + 1, src, names),
"<" => angle += 1,
">" => angle = (angle - 1).max(0),
">>" => angle = (angle - 2).max(0),
"|" => odd_pipes = !odd_pipes,
_ => {}
}
if !is_name(token.kind()) {
continue;
}
if self.fields && !odd_pipes && opens_field(tokens, i) {
continue;
}
let text = text_of(*token, src);
if generics.contains(&i) {
names.inside.items.insert(text);
} else if declares_item(tokens, i) {
self.add(names, text, false);
} else if is_bare(tokens, i) && text != "Self" {
if text.starts_with(|c: char| c.is_lowercase() || c == '_') {
self.add(names, text, true);
} else if angle == 0 && !in_type_position(tokens, i) {
self.add(names, text, false);
}
}
}
}
fn add(&self, names: &mut TreeNames, text: String, value: bool) {
if value {
names.inside.values.insert(text.clone());
}
names.inside.items.insert(text.clone());
names.outward.items.insert(text.clone());
names.outward.values.insert(text);
}
fn imports(&self, start: usize, src: &[u8], names: &mut TreeNames) {
let mut stack: Vec<Node> = Vec::new();
for token in self.tokens.iter().skip(start) {
if matches!(token.kind(), ";" | "use") {
break;
}
stack.push(*token);
}
while let Some(node) = stack.pop() {
if is_name(node.kind()) {
let text = text_of(node, src);
self.add(names, text.clone(), true);
self.add(names, text, false);
}
if node.kind() == "token_tree" {
let mut cursor = node.walk();
stack.extend(node.children(&mut cursor));
}
}
}
}
fn in_type_position(tokens: &[Node], i: usize) -> bool {
i.checked_sub(1).is_some_and(|p| {
matches!(
tokens[p].kind(),
":" | "->" | "as" | "&" | "dyn" | "impl" | "for" | "mutable_specifier"
)
})
}
fn opens_field(tokens: &[Node], i: usize) -> bool {
let after_start = i > 0 && matches!(tokens[i - 1].kind(), "{" | ",");
after_start && tokens.get(i + 1).is_some_and(|n| n.kind() == ":")
}
fn declares_item(tokens: &[Node], i: usize) -> bool {
let kind = |k: usize| tokens[k].kind();
match i.checked_sub(1) {
Some(p) if ITEM_KEYWORDS.contains(&kind(p)) => true,
Some(p) if kind(p) == "mutable_specifier" => p > 0 && kind(p - 1) == "static",
_ => false,
}
}
fn is_bare(tokens: &[Node], i: usize) -> bool {
let prev = i.checked_sub(1).map(|p| tokens[p].kind());
if prev.is_some_and(|p| matches!(p, "." | "::" | "$" | "'")) {
return false;
}
match tokens.get(i + 1) {
Some(next) if matches!(next.kind(), "::" | "!") => false,
Some(next) if next.kind() == "token_tree" => next.child(0).map(|c| c.kind()) != Some("("),
_ => true,
}
}
fn generic_params(tokens: &[Node]) -> HashSet<usize> {
let mut out = HashSet::new();
let mut open: Vec<bool> = Vec::new();
for (k, node) in tokens.iter().enumerate() {
match node.kind() {
"<" => {
let before = k.checked_sub(1).map(|p| tokens[p].kind());
let after_item = k >= 2
&& before.is_some_and(is_name)
&& ITEM_KEYWORDS.contains(&tokens[k - 2].kind());
open.push(before == Some("impl") || after_item);
}
">" => {
open.pop();
}
">>" => {
open.pop();
open.pop();
}
kind if is_name(kind) => {
let declares = open.last() == Some(&true)
&& matches!(tokens[k - 1].kind(), "<" | "," | "const");
if declares {
out.insert(k);
}
}
_ => {}
}
}
out
}
pub(super) fn angle_closes(tokens: &[Node]) -> Vec<Option<usize>> {
let mut closes = vec![None; tokens.len()];
let mut open: Vec<usize> = Vec::new();
for (k, node) in tokens.iter().enumerate() {
let shut = match node.kind() {
"<" => {
open.push(k);
0
}
">" => 1,
">>" => 2,
_ => 0,
};
for _ in 0..shut {
if let Some(o) = open.pop() {
closes[o] = Some(k);
}
}
}
closes
}