Skip to main content

sdivi_patterns/queries/
async_patterns.rs

1//! Node-kind and callee-text classification for async-concurrency patterns.
2//!
3//! ## Node-kind detection
4//!
5//! - `await_expression` — `.await` on a `Future` (Rust) or `await expr` (TS/JS)
6//!
7//! ## Callee-text detection (TypeScript / JavaScript only)
8//!
9//! Promise-chain call shapes registered at CALL_DISPATCH slot P1:
10//! - `.then(…)`, `.catch(…)`, `.finally(…)`
11//!
12//! Both paths together make `async_patterns` a **hybrid** category: `await_expression`
13//! nodes arrive via [`category_for_node_kind`](super::category_for_node_kind), and
14//! Promise-chain `call_expression` nodes arrive via [`matches_callee`] in
15//! [`super::classify_hint`]'s CALL_DISPATCH.
16
17use std::sync::LazyLock;
18
19use regex::Regex;
20
21/// Tree-sitter node kinds for async-concurrency patterns.
22///
23/// - `await_expression`: `.await` on a `Future`
24pub const NODE_KINDS: &[&str] = &["await_expression"];
25
26// TypeScript / JavaScript:
27//   \.(then|catch|finally)\(  — Promise chain calls.
28// No ^ anchor: the receiver expression precedes the dot, so the match is
29// suffix-anchored to the method name. This deliberately matches
30// "promise.then(" and "fetch(...).then(" without matching "getNextValue(".
31static TS_JS_RE: LazyLock<Regex> = LazyLock::new(|| {
32    Regex::new(r"\.(then|catch|finally)\(").expect("async_patterns TS/JS regex is valid")
33});
34
35/// Return `true` when `text` looks like an async-pattern call callee for `language`.
36///
37/// Covers Promise-chain `call_expression` shapes (`.then`, `.catch`, `.finally`)
38/// in TypeScript and JavaScript. `await_expression` nodes are routed via
39/// [`NODE_KINDS`] already; this function handles the remaining callee shapes.
40/// Other languages return `false` — their async primitives are node-kind-routed.
41///
42/// # Examples
43///
44/// ```rust
45/// use sdivi_patterns::queries::async_patterns::matches_callee;
46///
47/// assert!(matches_callee("promise.then(resolve)", "typescript"));
48/// assert!(matches_callee("fetch(url).catch(err => {})", "javascript"));
49/// assert!(!matches_callee("Math.max(a, b)", "typescript"));
50/// assert!(!matches_callee("promise.then(resolve)", "rust"));
51/// ```
52pub fn matches_callee(text: &str, language: &str) -> bool {
53    match language {
54        "typescript" | "javascript" => TS_JS_RE.is_match(text),
55        _ => false,
56    }
57}