use rustc_ast::token::{LitKind, TokenKind};
use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_span::Span;
pub(crate) fn find_template_literal(tokens: &TokenStream) -> Option<Span> {
let mut argument_len: usize = 0;
let mut argument_lead_literal: Option<Span> = None;
let mut found: Option<Span> = None;
let finish_argument = |len: usize, lead: Option<Span>, found: &mut Option<Span>| {
if found.is_none() && len == 1 {
*found = lead;
}
};
for tree in tokens.iter() {
if is_top_level_comma(tree) {
finish_argument(argument_len, argument_lead_literal, &mut found);
argument_len = 0;
argument_lead_literal = None;
continue;
}
if argument_len == 0 {
argument_lead_literal = cooked_str_literal_span(tree);
}
argument_len += 1;
}
finish_argument(argument_len, argument_lead_literal, &mut found);
found
}
fn is_top_level_comma(tree: &TokenTree) -> bool {
matches!(tree, TokenTree::Token(token, _) if token.kind == TokenKind::Comma)
}
fn cooked_str_literal_span(tree: &TokenTree) -> Option<Span> {
let TokenTree::Token(token, _) = tree else {
return None;
};
let TokenKind::Literal(literal) = token.kind else {
return None;
};
matches!(literal.kind, LitKind::Str).then_some(token.span)
}
pub(crate) fn find_all_cooked_str_literals(tokens: &TokenStream) -> Vec<Span> {
let mut spans = Vec::new();
collect_cooked_str_literals(tokens, &mut spans);
spans
}
fn collect_cooked_str_literals(tokens: &TokenStream, spans: &mut Vec<Span>) {
for tree in tokens.iter() {
if let Some(span) = cooked_str_literal_span(tree) {
spans.push(span);
} else if let TokenTree::Delimited(_, _, _, inner) = tree {
collect_cooked_str_literals(inner, spans);
}
}
}