Skip to main content

leekscript_analysis/
node_helpers.rs

1//! Helpers to extract names and structure from syntax nodes (`VarDecl`, `FunctionDecl`, `ClassDecl`, etc.).
2
3use sipha::red::{SyntaxElement, SyntaxNode, SyntaxToken};
4use sipha::types::{IntoSyntaxKind, Span};
5
6use super::scope::MemberVisibility;
7use leekscript_core::syntax::{Kind, FIELD_RHS};
8use leekscript_core::Type;
9
10/// Declaration kind for a variable declaration node.
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum VarDeclKind {
13    Var,
14    Global,
15    Const,
16    Let,
17    /// Typed form (e.g. `integer x = 0`) with no leading keyword.
18    Typed,
19}
20
21/// Info extracted from a `NodeVarDecl` (var/global/const/let or typed).
22pub struct VarDeclInfo {
23    pub kind: VarDeclKind,
24    pub name: String,
25    pub name_span: Span,
26}
27
28/// Collect identifier tokens (name, span) from node's subtree in source order, stopping at first "=".
29fn idents_before_assign(
30    node: &SyntaxNode,
31    idents: &mut Vec<(String, Span)>,
32    first_token_text: &mut Option<String>,
33) -> bool {
34    use sipha::red::SyntaxElement;
35    for elem in node.children() {
36        match elem {
37            SyntaxElement::Token(t) if !t.is_trivia() => {
38                if first_token_text.is_none() {
39                    *first_token_text = Some(t.text().to_string());
40                }
41                if t.text() == "=" {
42                    return true;
43                }
44                if t.kind_as::<Kind>() == Some(Kind::TokIdent) {
45                    idents.push((t.text().to_string(), t.text_range()));
46                }
47            }
48            SyntaxElement::Node(n) => {
49                if idents_before_assign(&n, idents, first_token_text) {
50                    return true;
51                }
52            }
53            _ => {}
54        }
55    }
56    false
57}
58
59/// Returns the right-hand side expression of a `NodeBinaryExpr`.
60/// Prefers the named field "rhs" when present; otherwise uses the last node child.
61/// When the grammar labels RHS, it may wrap it in `NodeExpr`; we unwrap so the returned node is the inner expression.
62#[must_use]
63pub fn binary_expr_rhs(node: &SyntaxNode) -> Option<SyntaxNode> {
64    if node.kind_as::<Kind>() != Some(Kind::NodeBinaryExpr) {
65        return None;
66    }
67    let rhs = node
68        .field_by_id(FIELD_RHS)
69        .or_else(|| node.child_nodes().last())?;
70    if rhs.kind_as::<Kind>() == Some(Kind::NodeExpr) {
71        rhs.first_child_node().or(Some(rhs))
72    } else {
73        Some(rhs)
74    }
75}
76
77/// Returns the member name (identifier after the dot) from a `NodeMemberExpr`.
78#[must_use]
79pub fn member_expr_member_name(node: &SyntaxNode) -> Option<String> {
80    if node.kind_as::<Kind>() != Some(Kind::NodeMemberExpr) {
81        return None;
82    }
83    let mut saw_dot = false;
84    for child in node.children() {
85        if let SyntaxElement::Token(t) = &child {
86            if !t.is_trivia() {
87                if t.text() == "." {
88                    saw_dot = true;
89                } else if saw_dot {
90                    return Some(t.text().to_string());
91                }
92            }
93        }
94    }
95    None
96}
97
98/// Returns the receiver name (identifier or keyword before the dot) from a `NodeMemberExpr`.
99/// For `Cell.getCell` returns `Some("Cell")`, for `this.update` returns `Some("this")`.
100#[must_use]
101pub fn member_expr_receiver_name(node: &SyntaxNode) -> Option<String> {
102    if node.kind_as::<Kind>() != Some(Kind::NodeMemberExpr) {
103        return None;
104    }
105    let receiver = node.first_child_node()?;
106    primary_expr_resolvable_name(&receiver)
107}
108
109/// Returns the declaration kind and name from a `NodeVarDecl`.
110/// For "var x", "global T x": name is the first identifier after the keyword.
111/// For typed form "Array<EffectOverTime> arr" or "integer? y": name is the *last* identifier before "=" (type names come first).
112#[must_use]
113pub fn var_decl_info(node: &SyntaxNode) -> Option<VarDeclInfo> {
114    if node.kind_as::<Kind>() != Some(Kind::NodeVarDecl) {
115        return None;
116    }
117    let mut idents = Vec::new();
118    let mut first_token_text: Option<String> = None;
119    idents_before_assign(node, &mut idents, &mut first_token_text);
120    let first_token_text = first_token_text.as_deref();
121    let last_idx = idents.len().saturating_sub(1);
122    let (kind, name_idx) = match first_token_text {
123        Some("var") => (VarDeclKind::Var, 0),
124        Some("global") => (VarDeclKind::Global, last_idx),
125        Some("const") => (VarDeclKind::Const, 0),
126        Some("let") => (VarDeclKind::Let, 0),
127        _ => (VarDeclKind::Typed, last_idx),
128    };
129    let (name, name_span) = idents.get(name_idx).cloned()?;
130    Some(VarDeclInfo {
131        kind,
132        name,
133        name_span,
134    })
135}
136
137/// Info extracted from a `NodeFunctionDecl` (name and parameter counts).
138pub struct FunctionDeclInfo {
139    pub name: String,
140    pub name_span: Span,
141    /// Minimum number of arguments (params without a default value).
142    pub min_arity: usize,
143    /// Maximum number of arguments (total params; for overloads we store multiple ranges).
144    pub max_arity: usize,
145}
146
147/// Number of argument expressions in a `NodeCallExpr`. Used for type inference so the call
148/// is inferred as the function's return type. The grammar uses lparen, optional(expr, `zero_or_more(comma`, expr)), rparen;
149/// the optional may be one node (with 0, 1, or more children) or optional + `zero_or_more` as siblings.
150#[must_use]
151pub fn call_argument_count(node: &SyntaxNode) -> usize {
152    if node.kind_as::<Kind>() != Some(Kind::NodeCallExpr) {
153        return 0;
154    }
155    let content_nodes: Vec<SyntaxNode> = node.child_nodes().collect();
156    if content_nodes.is_empty() {
157        return 0;
158    }
159    let first_children = content_nodes[0].child_nodes().count();
160    if content_nodes.len() == 1 {
161        // Single node: optional(expr, zero_or_more(...)) with 0, 1, or more expr children.
162        return first_children;
163    }
164    // First node = optional (0 or 1 expr), rest = one zero_or_more node per (comma, expr).
165    first_children.min(1) + content_nodes.len().saturating_sub(1)
166}
167
168/// Returns the syntax node for the `i`-th argument expression of a `NodeCallExpr` (0-based), for error spans.
169#[must_use]
170pub fn call_argument_node(node: &SyntaxNode, i: usize) -> Option<SyntaxNode> {
171    if node.kind_as::<Kind>() != Some(Kind::NodeCallExpr) {
172        return None;
173    }
174    let content_nodes: Vec<SyntaxNode> = node.child_nodes().collect();
175    let args: Vec<SyntaxNode> = if content_nodes.len() == 1 {
176        content_nodes[0].child_nodes().collect()
177    } else {
178        let mut v = Vec::new();
179        if let Some(expr) = content_nodes
180            .first()
181            .and_then(sipha::red::SyntaxNode::first_child_node)
182        {
183            v.push(expr);
184        }
185        for n in &content_nodes[1..] {
186            if let Some(expr) = n.first_child_node() {
187                v.push(expr);
188            }
189        }
190        v
191    };
192    args.into_iter().nth(i)
193}
194
195/// Returns true if this `NodeParam` has a default value (`= expr`).
196pub fn param_has_default(node: &SyntaxNode) -> bool {
197    if node.kind_as::<Kind>() != Some(Kind::NodeParam) {
198        return false;
199    }
200    node.descendant_tokens().iter().any(|t| t.text() == "=")
201}
202
203/// Returns name and parameter counts from a `NodeFunctionDecl`.
204/// For default parameters, `min_arity` is the number of required params; `max_arity` is total.
205#[must_use]
206pub fn function_decl_info(node: &SyntaxNode) -> Option<FunctionDeclInfo> {
207    if node.kind_as::<Kind>() != Some(Kind::NodeFunctionDecl) {
208        return None;
209    }
210    let tokens: Vec<SyntaxToken> = node.non_trivia_tokens().collect();
211    let lparen_idx = tokens.iter().position(|t| t.text() == "(")?;
212    let name_token = tokens.get(lparen_idx.checked_sub(1)?)?;
213    let name = name_token.text().to_string();
214    let name_span = name_token.text_range();
215    let params: Vec<SyntaxNode> = node
216        .child_nodes()
217        .filter(|n| n.kind_as::<Kind>() == Some(Kind::NodeParam))
218        .collect();
219    let min_arity = params.iter().take_while(|p| !param_has_default(p)).count();
220    let max_arity = params.len();
221    Some(FunctionDeclInfo {
222        name,
223        name_span,
224        min_arity,
225        max_arity,
226    })
227}
228
229/// Info extracted from a `NodeClassDecl` (name and optional super class).
230pub struct ClassDeclInfo {
231    pub name: String,
232    pub name_span: Span,
233    /// Name of the class this extends, if any.
234    pub super_class: Option<String>,
235}
236
237/// Returns class name and optional super class from a `NodeClassDecl`.
238#[must_use]
239pub fn class_decl_info(node: &SyntaxNode) -> Option<ClassDeclInfo> {
240    if node.kind_as::<Kind>() != Some(Kind::NodeClassDecl) {
241        return None;
242    }
243    let tokens: Vec<SyntaxToken> = node.non_trivia_tokens().collect();
244    let class_idx = tokens.iter().position(|t| t.text() == "class")?;
245    let name_token = tokens.get(class_idx + 1)?;
246    let name = name_token.text().to_string();
247    let name_span = name_token.text_range();
248    let super_class = tokens
249        .iter()
250        .skip(class_idx + 2)
251        .position(|t| t.text() == "extends")
252        .and_then(|extends_offset| {
253            let idx = class_idx + 2 + extends_offset + 1;
254            tokens
255                .get(idx)
256                .filter(|t| t.text() != "{")
257                .map(|t| t.text().to_string())
258        });
259    Some(ClassDeclInfo {
260        name,
261        name_span,
262        super_class,
263    })
264}
265
266/// If the node is or contains a simple identifier (single identifier token),
267/// returns its text and span. Handles `NodeExpr` and `NodePrimaryExpr` (expr may not wrap in `NodeExpr` in the grammar).
268pub fn expr_identifier(node: &SyntaxNode) -> Option<(String, Span)> {
269    let kind = node.kind_as::<Kind>()?;
270    if kind != Kind::NodeExpr && kind != Kind::NodePrimaryExpr {
271        return None;
272    }
273    let first = node.first_token()?;
274    let last = node.last_token()?;
275    if first.offset() != last.offset() {
276        return None;
277    }
278    if first.kind_as::<Kind>() == Some(Kind::TokIdent) {
279        Some((first.text().to_string(), first.text_range()))
280    } else {
281        None
282    }
283}
284
285/// Name to use for type/scope resolution from a primary expr: identifier, or "this"/"super" when
286/// the single token is the corresponding keyword (class methods don't use the `function` keyword).
287#[must_use]
288pub fn primary_expr_resolvable_name(node: &SyntaxNode) -> Option<String> {
289    if let Some((name, _)) = expr_identifier(node) {
290        return Some(name);
291    }
292    let kind = node.kind_as::<Kind>()?;
293    if kind != Kind::NodePrimaryExpr {
294        return None;
295    }
296    let first = node.first_token()?;
297    let last = node.last_token()?;
298    if first.offset() != last.offset() {
299        return None;
300    }
301    match first.kind_as::<Kind>() {
302        Some(Kind::KwThis) => Some("this".to_string()),
303        Some(Kind::KwSuper) => Some("super".to_string()),
304        _ => None,
305    }
306}
307
308/// If this primary is a `new ClassName(...)` constructor call, returns `Some((class_name, num_args))`.
309#[must_use]
310pub fn primary_expr_new_constructor(node: &SyntaxNode) -> Option<(String, usize)> {
311    if node.kind_as::<Kind>()? != Kind::NodePrimaryExpr {
312        return None;
313    }
314    let elements: Vec<SyntaxElement> = node
315        .children()
316        .filter(|e| match e {
317            SyntaxElement::Token(t) => !t.is_trivia(),
318            SyntaxElement::Node(_) => true,
319        })
320        .collect();
321    let first = elements.first()?;
322    let first_tok = match first {
323        SyntaxElement::Token(t) => t,
324        _ => return None,
325    };
326    if first_tok.kind_as::<Kind>() != Some(Kind::KwNew) {
327        return None;
328    }
329    let second = elements.get(1)?;
330    let class_name = match second {
331        SyntaxElement::Token(t) if t.kind_as::<Kind>() == Some(Kind::TokIdent) => {
332            t.text().to_string()
333        }
334        SyntaxElement::Node(n) => n
335            .first_token()
336            .filter(|t| t.kind_as::<Kind>() == Some(Kind::TokIdent))?
337            .text()
338            .to_string(),
339        _ => return None,
340    };
341    let arg_count = node.find_all_nodes(Kind::NodeExpr.into_syntax_kind()).len();
342    Some((class_name, arg_count))
343}
344
345/// Check if this node is a simple identifier expression (for resolution).
346#[allow(dead_code)]
347pub fn is_identifier_expr(node: &SyntaxNode) -> bool {
348    expr_identifier(node).is_some()
349}
350
351/// Returns the direct child node of `NodeForInStmt` that is the iterable expression (the expr after `in`).
352#[must_use]
353pub fn for_in_iterable_expr(for_in_node: &SyntaxNode) -> Option<SyntaxNode> {
354    if for_in_node.kind_as::<Kind>() != Some(Kind::NodeForInStmt) {
355        return None;
356    }
357    let mut seen_in = false;
358    for child in for_in_node.children() {
359        if let SyntaxElement::Token(t) = &child {
360            if !t.is_trivia() && t.text() == "in" {
361                seen_in = true;
362                continue;
363            }
364        }
365        if seen_in {
366            if let SyntaxElement::Node(n) = child {
367                return Some(n.clone());
368            }
369        }
370    }
371    None
372}
373
374/// Variable name(s) and span(s) from a `NodeForInStmt`: key and optionally value (for key : valueVar in expr).
375/// Skips `type_expr` nodes and for/var/in/paren tokens so we get only the loop variable identifiers.
376pub fn for_in_loop_vars(node: &SyntaxNode) -> Vec<(String, Span)> {
377    if node.kind_as::<Kind>() != Some(Kind::NodeForInStmt) {
378        return Vec::new();
379    }
380    let skip_tokens: &[&str] = &["for", "(", ")", "var", "in", ":"];
381    let mut vars = Vec::new();
382    let mut state = 0u8; // 0 = need key, 1 = need colon or in, 2 = need value
383    for child in node.children() {
384        match child {
385            SyntaxElement::Token(t) if !t.is_trivia() => {
386                let text = t.text();
387                if text == "in" {
388                    break;
389                }
390                if skip_tokens.contains(&text) {
391                    if text == ":" && state == 1 {
392                        state = 2;
393                    }
394                    continue;
395                }
396                if state == 0 {
397                    vars.push((text.to_string(), t.text_range()));
398                    state = 1;
399                } else if state == 2 {
400                    vars.push((text.to_string(), t.text_range()));
401                    break;
402                }
403            }
404            SyntaxElement::Node(n) => {
405                if n.kind_as::<Kind>() == Some(Kind::NodeTypeExpr) {
406                    continue;
407                }
408                if state == 0 {
409                    if let Some(tok) = n.first_token() {
410                        if !tok.is_trivia() && !skip_tokens.contains(&tok.text()) {
411                            vars.push((tok.text().to_string(), tok.text_range()));
412                            state = 1;
413                        }
414                    }
415                } else if state == 2 {
416                    if let Some(tok) = n.first_token() {
417                        if !tok.is_trivia() && !skip_tokens.contains(&tok.text()) {
418                            vars.push((tok.text().to_string(), tok.text_range()));
419                            break;
420                        }
421                    }
422                }
423            }
424            _ => {}
425        }
426    }
427    vars
428}
429
430/// True if this `NodeExpr` is a ternary expression (cond ? then : else).
431#[must_use]
432pub fn is_ternary_expr(node: &SyntaxNode) -> bool {
433    if node.kind_as::<Kind>() != Some(Kind::NodeExpr) {
434        return false;
435    }
436    let mut has_question = false;
437    let mut has_colon = false;
438    for child in node.children() {
439        if let SyntaxElement::Token(t) = child {
440            if !t.is_trivia() {
441                match t.text() {
442                    "?" => has_question = true,
443                    ":" => has_colon = true,
444                    _ => {}
445                }
446            }
447        }
448    }
449    has_question && has_colon
450}
451
452/// Null-check ops: then branch is non-null when condition is true.
453const NULL_CHECK_OPS: &[&str] = &["!=", "!==", "==", "==="];
454
455/// Returns (`var_name`, `then_is_non_null`) if `node` is a null-check condition (e.g. `var != null`, `var === null`).
456/// Searches recursively for a binary with one identifier and one null. `root` is used to get parent/siblings.
457#[must_use]
458pub fn null_check_from_condition(
459    condition_node: &SyntaxNode,
460    root: &SyntaxNode,
461) -> Option<(String, bool)> {
462    find_null_check_binary(condition_node, root)
463}
464
465fn find_null_check_binary(node: &SyntaxNode, root: &SyntaxNode) -> Option<(String, bool)> {
466    if node.kind_as::<Kind>() == Some(Kind::NodeBinaryExpr) {
467        let op = node.children().find_map(|c| {
468            if let SyntaxElement::Token(t) = c {
469                if !t.is_trivia() {
470                    let text = t.text();
471                    if NULL_CHECK_OPS.contains(&text) {
472                        return Some(text.to_string());
473                    }
474                }
475            }
476            None
477        })?;
478        let rhs = binary_expr_rhs(node)?;
479        let lhs = prev_sibling_node(node, root)?;
480        let (var_name, _var_on_left) = if let Some((name, _)) = expr_identifier(&lhs) {
481            if is_null_literal(&rhs) {
482                (name, true)
483            } else {
484                return find_null_check_binary_recurse(node, root);
485            }
486        } else if let Some((name, _)) = expr_identifier(&rhs) {
487            if is_null_literal(&lhs) {
488                (name, false)
489            } else {
490                return find_null_check_binary_recurse(node, root);
491            }
492        } else {
493            return find_null_check_binary_recurse(node, root);
494        };
495        // then_is_non_null: for "var != null" or "null != var", then branch is non-null
496        let then_is_non_null = op == "!=" || op == "!==";
497        return Some((var_name, then_is_non_null));
498    }
499    find_null_check_binary_recurse(node, root)
500}
501
502fn find_null_check_binary_recurse(node: &SyntaxNode, root: &SyntaxNode) -> Option<(String, bool)> {
503    for child in node.child_nodes() {
504        if let Some(r) = find_null_check_binary(&child, root) {
505            return Some(r);
506        }
507    }
508    None
509}
510
511/// Previous sibling that is a node (skips tokens).
512fn prev_sibling_node(node: &SyntaxNode, root: &SyntaxNode) -> Option<SyntaxNode> {
513    let ancestors: Vec<SyntaxNode> = node.ancestors(root);
514    let parent = ancestors.first()?;
515    let mut idx = None;
516    for (i, c) in parent.children().enumerate() {
517        if let SyntaxElement::Node(n) = c {
518            if n.text_range() == node.text_range() {
519                idx = Some(i);
520                break;
521            }
522        }
523    }
524    let i = idx?;
525    parent
526        .children()
527        .take(i)
528        .filter_map(|e| e.as_node().cloned())
529        .last()
530}
531
532fn is_null_literal(node: &SyntaxNode) -> bool {
533    node.first_token().is_some_and(|t| t.text() == "null")
534}
535
536/// Index of `node` among `parent`'s children (including tokens). Returns `None` if not a direct child.
537#[must_use]
538pub fn node_index_in_parent(node: &SyntaxNode, parent: &SyntaxNode) -> Option<usize> {
539    for (i, c) in parent.children().enumerate() {
540        if let SyntaxElement::Node(n) = c {
541            if n.text_range() == node.text_range() {
542                return Some(i);
543            }
544        }
545    }
546    None
547}
548
549/// Parameter name and span from a `NodeParam` (for scope building).
550#[must_use]
551pub fn param_name(node: &SyntaxNode) -> Option<(String, Span)> {
552    if node.kind_as::<Kind>() != Some(Kind::NodeParam) {
553        return None;
554    }
555    let tokens: Vec<SyntaxToken> = node.non_trivia_tokens().collect();
556    let name_token = tokens
557        .iter()
558        .take_while(|t| t.text() != "=")
559        .last()
560        .or_else(|| tokens.first())?;
561    Some((name_token.text().to_string(), name_token.text_range()))
562}
563
564/// Field name, optional declared type, and whether it's static from a `NodeClassField`.
565/// Returns (name, type, `is_static`) where type is None if the field has no type annotation.
566#[must_use]
567pub fn class_field_info(node: &SyntaxNode) -> Option<(String, Option<Type>, bool)> {
568    use super::type_expr::{parse_type_expr, TypeExprResult};
569    use sipha::types::IntoSyntaxKind;
570    if node.kind_as::<Kind>() != Some(Kind::NodeClassField) {
571        return None;
572    }
573    let tokens: Vec<SyntaxToken> = node.non_trivia_tokens().collect();
574    let is_static = tokens.first().is_some_and(|t| t.text() == "static");
575    let name_token = tokens
576        .iter()
577        .take_while(|t| t.text() != "=" && t.text() != ";")
578        .last()?;
579    let name = name_token.text().to_string();
580    // Type may be nested (e.g. inside grammar choice/sequence); search descendants.
581    let type_expr_node = node.find_node(Kind::NodeTypeExpr.into_syntax_kind());
582    let ty = type_expr_node.and_then(|te| match parse_type_expr(&te) {
583        TypeExprResult::Ok(t) => Some(t),
584        TypeExprResult::Err(_) => None,
585    });
586    Some((name, ty, is_static))
587}
588
589/// Visibility of a class member from the preceding modifier. **Public by default** when no modifier.
590/// The modifier that applies is the last visibility keyword before this member that is not "consumed" by another member (i.e. no other member's range contains that keyword).
591#[must_use]
592pub fn class_member_visibility(node: &SyntaxNode, root: &SyntaxNode) -> MemberVisibility {
593    let class_decl = node
594        .ancestors(root)
595        .into_iter()
596        .find(|a| a.kind_as::<Kind>() == Some(Kind::NodeClassDecl));
597    let class_decl = match class_decl {
598        Some(c) => c,
599        None => return MemberVisibility::Public,
600    };
601    let node_start = node.text_range().start;
602    let kind_field = Kind::NodeClassField.into_syntax_kind();
603    let kind_func = Kind::NodeFunctionDecl.into_syntax_kind();
604    // All member nodes in this class (for "who consumes this keyword" check).
605    let members: Vec<SyntaxNode> = class_decl
606        .find_all_nodes(kind_field)
607        .into_iter()
608        .chain(class_decl.find_all_nodes(kind_func))
609        .collect();
610    // Collect (token_end, vis) for each visibility keyword before this node.
611    let mut vis_keywords: Vec<(u32, MemberVisibility)> = Vec::new();
612    for token in class_decl.descendant_tokens() {
613        if token.is_trivia() {
614            continue;
615        }
616        let range = token.text_range();
617        // Include visibility keywords that are before or at the start of this member (so we see the modifier that applies to this member).
618        if range.start <= node_start {
619            match token.text() {
620                "public" => vis_keywords.push((range.end, MemberVisibility::Public)),
621                "private" => vis_keywords.push((range.end, MemberVisibility::Private)),
622                "protected" => vis_keywords.push((range.end, MemberVisibility::Protected)),
623                _ => {}
624            }
625        }
626        if range.start > node_start {
627            break; // past the start of this member
628        }
629    }
630    for (end, vis) in vis_keywords.into_iter().rev() {
631        let sibling_consumes_keyword = members.iter().any(|sib| {
632            let r = sib.text_range();
633            sib.text_range() != node.text_range() && r.start <= end && r.end > end
634        });
635        if !sibling_consumes_keyword {
636            return vis;
637        }
638    }
639    MemberVisibility::Public
640}
641
642/// True if this `NodeFunctionDecl` is a static class method (has "static" as preceding sibling in class body).
643#[must_use]
644pub fn class_method_is_static(node: &SyntaxNode, root: &SyntaxNode) -> bool {
645    if node.kind_as::<Kind>() != Some(Kind::NodeFunctionDecl) {
646        return false;
647    }
648    let ancestors: Vec<SyntaxNode> = node.ancestors(root);
649    let parent = match ancestors.first() {
650        Some(p) => p,
651        None => return false,
652    };
653    let node_start = node.text_range().start;
654    // Last non-trivia token in parent that ends before this node (source order) - if "static", method is static.
655    let mut last_before: Option<String> = None;
656    for token in parent.descendant_tokens() {
657        if token.is_trivia() {
658            continue;
659        }
660        let range = token.text_range();
661        if range.end <= node_start {
662            last_before = Some(token.text().to_string());
663        } else {
664            break;
665        }
666    }
667    last_before.as_deref() == Some("static")
668}