ast_grep_core/matcher/
kind.rs

1use super::Matcher;
2
3use crate::language::Language;
4use crate::meta_var::MetaVarEnv;
5use crate::node::KindId;
6use crate::{Doc, Node};
7
8use std::borrow::Cow;
9
10use bit_set::BitSet;
11use thiserror::Error;
12
13// 0 is symbol_end for not found, 65535 is builtin symbol ERROR
14// see https://tree-sitter.docsforge.com/master/api/#TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION
15// and https://tree-sitter.docsforge.com/master/api/ts_language_symbol_for_name/
16const TS_BUILTIN_SYM_END: KindId = 0;
17const TS_BUILTIN_SYM_ERROR: KindId = 65535;
18
19#[derive(Debug, Error)]
20pub enum KindMatcherError {
21  #[error("Kind `{0}` is invalid.")]
22  InvalidKindName(String),
23}
24
25#[derive(Clone)]
26pub struct KindMatcher {
27  kind: KindId,
28}
29
30impl KindMatcher {
31  pub fn new<L: Language>(node_kind: &str, lang: L) -> Self {
32    Self {
33      kind: lang.kind_to_id(node_kind),
34    }
35  }
36
37  pub fn try_new<L: Language>(node_kind: &str, lang: L) -> Result<Self, KindMatcherError> {
38    println!("KindMatcher created with kind id: {}", node_kind);
39    let s = Self::new(node_kind, lang);
40    if s.is_invalid() {
41      Err(KindMatcherError::InvalidKindName(node_kind.into()))
42    } else {
43      Ok(s)
44    }
45  }
46
47  pub fn from_id(kind: KindId) -> Self {
48    Self { kind }
49  }
50
51  /// Whether the kind matcher contains undefined tree-sitter kind.
52  pub fn is_invalid(&self) -> bool {
53    self.kind == TS_BUILTIN_SYM_END
54  }
55
56  /// Construct a matcher that only matches ERROR
57  pub fn error_matcher() -> Self {
58    Self::from_id(TS_BUILTIN_SYM_ERROR)
59  }
60}
61
62pub mod kind_utils {
63  use super::*;
64
65  /// Whether the kind will match parsing error occurred in the source code.
66  /// for example, we can use `kind: ERROR` in YAML to find invalid syntax in source.
67  /// the name `is_error` implies the matcher itself is error.
68  /// But here the matcher itself is valid and it is what it matches is error.
69  pub fn is_error_kind(kind: KindId) -> bool {
70    kind == TS_BUILTIN_SYM_ERROR
71  }
72
73  pub fn are_kinds_matching(goal: KindId, candidate: KindId) -> bool {
74    goal == candidate || is_error_kind(goal)
75  }
76}
77
78impl Matcher for KindMatcher {
79  fn match_node_with_env<'tree, D: Doc>(
80    &self,
81    node: Node<'tree, D>,
82    _env: &mut Cow<MetaVarEnv<'tree, D>>,
83  ) -> Option<Node<'tree, D>> {
84    if node.kind_id() == self.kind {
85      Some(node)
86    } else {
87      None
88    }
89  }
90
91  fn potential_kinds(&self) -> Option<BitSet> {
92    let mut set = BitSet::new();
93    set.insert(self.kind.into());
94    Some(set)
95  }
96}
97
98#[cfg(test)]
99mod test {
100  use super::*;
101  use crate::language::Tsx;
102  use crate::matcher::MatcherExt;
103  use crate::{tree_sitter::StrDoc, Root};
104
105  fn pattern_node(s: &str) -> Root<StrDoc<Tsx>> {
106    Root::str(s, Tsx)
107  }
108  #[test]
109  fn test_kind_match() {
110    let kind = "public_field_definition";
111    let cand = pattern_node("class A { a = 123 }");
112    let cand = cand.root();
113    let pattern = KindMatcher::new(kind, Tsx);
114    assert!(
115      pattern.find_node(cand.clone()).is_some(),
116      "goal: {}, candidate: {}",
117      kind,
118      cand.get_inner_node().to_sexp(),
119    );
120  }
121
122  #[test]
123  fn test_kind_non_match() {
124    let kind = "field_definition";
125    let cand = pattern_node("const a = 123");
126    let cand = cand.root();
127    let pattern = KindMatcher::new(kind, Tsx);
128    assert!(
129      pattern.find_node(cand.clone()).is_none(),
130      "goal: {}, candidate: {}",
131      kind,
132      cand.get_inner_node().to_sexp(),
133    );
134  }
135
136  #[test]
137  fn test_kind_potential_kinds() {
138    let kind = "field_definition";
139    let matcher = KindMatcher::new(kind, Tsx);
140    let potential_kinds = matcher
141      .potential_kinds()
142      .expect("should have potential kinds");
143    // should has exactly one potential kind
144    assert_eq!(potential_kinds.len(), 1);
145  }
146}