Skip to main content

tree_sitter_cpp/
lib.rs

1//! This crate provides C++ language support for the [tree-sitter] parsing library.
2//!
3//! Use [willbooster-tree-sitter] 7.0.10 or later within major version 7. The parser carries the ABI
4//! number of the WillBooster/tree-sitter fork; upstream's runtime rejects it.
5//!
6//! Typically, you will use the [`LANGUAGE`] constant to add this language to a
7//! tree-sitter [`Parser`], and then use the parser to parse some code:
8//!
9//! ```
10//! let code = "#include <iostream>\n\nint main() {\n    std::cout << \"Hello\\n\";\n}\n";
11//! let mut parser = tree_sitter::Parser::new();
12//! let language = tree_sitter_cpp::LANGUAGE;
13//! parser
14//!     .set_language(&language.into())
15//!     .expect("Error loading C++ parser");
16//! let tree = parser.parse(code, None).unwrap();
17//! assert!(!tree.root_node().has_error());
18//! ```
19//!
20//! [`Parser`]: https://docs.rs/willbooster-tree-sitter/7/tree_sitter/struct.Parser.html
21//! [willbooster-tree-sitter]: https://crates.io/crates/willbooster-tree-sitter
22//! [tree-sitter]: https://tree-sitter.github.io/
23
24use tree_sitter_language::LanguageFn;
25
26unsafe extern "C" {
27    fn tree_sitter_cpp() -> *const ();
28}
29
30/// The tree-sitter [`LanguageFn`] for this grammar.
31pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_cpp) };
32
33/// The content of the [`node-types.json`] file for this grammar.
34///
35/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers/6-static-node-types
36pub const NODE_TYPES: &str = include_str!("../../src/node-types.json");
37
38/// The query for extracting symbol definitions from C++ source.
39pub const TAGS_QUERY: &str = include_str!("../../queries/tags.scm");
40
41/// The query for identifying languages and contents embedded in C++ raw strings.
42pub const INJECTIONS_QUERY: &str = include_str!("../../queries/injections.scm");
43
44#[cfg(test)]
45mod tests {
46    use tree_sitter::StreamingIterator;
47
48    #[test]
49    fn test_can_load_grammar() {
50        let mut parser = tree_sitter::Parser::new();
51        parser
52            .set_language(&super::LANGUAGE.into())
53            .expect("Error loading C++ parser");
54    }
55
56    #[test]
57    fn extracts_symbol_definitions() {
58        let source = "struct Example { void method() {} };\nvoid function() {}\n";
59        assert_eq!(
60            captured_texts(source, super::TAGS_QUERY, "name"),
61            ["Example", "method", "function"]
62        );
63    }
64
65    #[test]
66    fn extracts_raw_string_injections() {
67        let source =
68            "const char* embedded = R\"sql(SELECT 1)sql\";\nconst char* plain = \"SELECT 2\";\n";
69        assert_eq!(
70            captured_texts(source, super::INJECTIONS_QUERY, "injection.language"),
71            ["sql"]
72        );
73        assert_eq!(
74            captured_texts(source, super::INJECTIONS_QUERY, "injection.content"),
75            ["SELECT 1"]
76        );
77    }
78
79    fn captured_texts(source: &str, query_source: &str, capture_name: &str) -> Vec<String> {
80        let language = super::LANGUAGE.into();
81        let mut parser = tree_sitter::Parser::new();
82        parser.set_language(&language).unwrap();
83        let tree = parser.parse(source, None).unwrap();
84        assert!(!tree.root_node().has_error());
85
86        let query = tree_sitter::Query::new(&language, query_source).unwrap();
87        let name_index = query.capture_index_for_name(capture_name).unwrap();
88        let mut cursor = tree_sitter::QueryCursor::new();
89        let mut captures = cursor.captures(&query, tree.root_node(), source.as_bytes());
90        let mut texts = Vec::new();
91        while let Some((query_match, capture_index)) = captures.next() {
92            let capture = query_match.captures()[*capture_index];
93            if capture.index == name_index {
94                texts.push(
95                    capture
96                        .node
97                        .utf8_text(source.as_bytes())
98                        .unwrap()
99                        .to_owned(),
100                );
101            }
102        }
103        texts
104    }
105}