ast_grep_core/matcher/
kind.rs1use 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
13const 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 let s = Self::new(node_kind, lang);
39 if s.is_invalid() {
40 Err(KindMatcherError::InvalidKindName(node_kind.into()))
41 } else {
42 Ok(s)
43 }
44 }
45
46 pub fn from_id(kind: KindId) -> Self {
47 Self { kind }
48 }
49
50 pub fn is_invalid(&self) -> bool {
52 self.kind == TS_BUILTIN_SYM_END
53 }
54
55 pub fn error_matcher() -> Self {
57 Self::from_id(TS_BUILTIN_SYM_ERROR)
58 }
59}
60
61pub mod kind_utils {
62 use super::*;
63
64 pub fn is_error_kind(kind: KindId) -> bool {
69 kind == TS_BUILTIN_SYM_ERROR
70 }
71
72 pub fn are_kinds_matching(goal: KindId, candidate: KindId) -> bool {
73 goal == candidate || is_error_kind(goal)
74 }
75}
76
77impl Matcher for KindMatcher {
78 fn match_node_with_env<'tree, D: Doc>(
79 &self,
80 node: Node<'tree, D>,
81 _env: &mut Cow<MetaVarEnv<'tree, D>>,
82 ) -> Option<Node<'tree, D>> {
83 if node.kind_id() == self.kind {
84 Some(node)
85 } else {
86 None
87 }
88 }
89
90 fn potential_kinds(&self) -> Option<BitSet> {
91 let mut set = BitSet::new();
92 set.insert(self.kind.into());
93 Some(set)
94 }
95}
96
97#[cfg(test)]
98mod test {
99 use super::*;
100 use crate::language::Tsx;
101 use crate::matcher::MatcherExt;
102 use crate::{tree_sitter::StrDoc, Root};
103
104 fn pattern_node(s: &str) -> Root<StrDoc<Tsx>> {
105 Root::str(s, Tsx)
106 }
107 #[test]
108 fn test_kind_match() {
109 let kind = "public_field_definition";
110 let cand = pattern_node("class A { a = 123 }");
111 let cand = cand.root();
112 let pattern = KindMatcher::new(kind, Tsx);
113 assert!(
114 pattern.find_node(cand.clone()).is_some(),
115 "goal: {}, candidate: {}",
116 kind,
117 cand.get_inner_node().to_sexp(),
118 );
119 }
120
121 #[test]
122 fn test_kind_non_match() {
123 let kind = "field_definition";
124 let cand = pattern_node("const a = 123");
125 let cand = cand.root();
126 let pattern = KindMatcher::new(kind, Tsx);
127 assert!(
128 pattern.find_node(cand.clone()).is_none(),
129 "goal: {}, candidate: {}",
130 kind,
131 cand.get_inner_node().to_sexp(),
132 );
133 }
134
135 #[test]
136 fn test_kind_potential_kinds() {
137 let kind = "field_definition";
138 let matcher = KindMatcher::new(kind, Tsx);
139 let potential_kinds = matcher
140 .potential_kinds()
141 .expect("should have potential kinds");
142 assert_eq!(potential_kinds.len(), 1);
144 }
145}