1#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
23
24#[cfg(not(feature = "in-rust-tree"))]
25extern crate ra_ap_rustc_lexer as rustc_lexer;
26#[cfg(feature = "in-rust-tree")]
27extern crate rustc_driver as _;
28#[cfg(feature = "in-rust-tree")]
29extern crate rustc_lexer;
30
31mod parsing;
32mod ptr;
33mod syntax_error;
34mod syntax_node;
35#[cfg(test)]
36mod tests;
37mod validation;
38
39pub mod algo;
40pub mod ast;
41#[doc(hidden)]
42pub mod fuzz;
43pub mod hacks;
44pub mod syntax_editor;
45pub mod utils;
46
47use std::{marker::PhantomData, ops::Range};
48
49use stdx::format_to;
50use triomphe::Arc;
51
52pub use crate::{
53 ast::{AstNode, AstToken},
54 ptr::{AstPtr, SyntaxNodePtr},
55 syntax_error::SyntaxError,
56 syntax_node::{
57 PreorderWithTokens, RustLanguage, SyntaxElement, SyntaxElementChildren, SyntaxNode,
58 SyntaxNodeChildren, SyntaxToken, SyntaxTreeBuilder,
59 },
60};
61pub use parser::{Edition, SyntaxKind, T};
62pub use rowan::{
63 Direction, GreenNode, NodeOrToken, SyntaxText, TextRange, TextSize, TokenAtOffset, WalkEvent,
64 api::Preorder,
65};
66pub use rustc_literal_escaper as unescape;
67pub use smol_str::{SmolStr, SmolStrBuilder, ToSmolStr, format_smolstr};
68
69#[derive(Debug, PartialEq, Eq)]
75pub struct Parse<T> {
76 green: Option<GreenNode>,
77 errors: Option<Arc<[SyntaxError]>>,
78 _ty: PhantomData<fn() -> T>,
79}
80
81impl<T> Clone for Parse<T> {
82 fn clone(&self) -> Parse<T> {
83 Parse { green: self.green.clone(), errors: self.errors.clone(), _ty: PhantomData }
84 }
85}
86
87impl<T> Parse<T> {
88 fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> {
89 Parse {
90 green: Some(green),
91 errors: if errors.is_empty() { None } else { Some(errors.into()) },
92 _ty: PhantomData,
93 }
94 }
95
96 pub fn syntax_node(&self) -> SyntaxNode {
97 SyntaxNode::new_root(self.green.as_ref().unwrap().clone())
98 }
99
100 pub fn errors(&self) -> Vec<SyntaxError> {
101 let mut errors = if let Some(e) = self.errors.as_deref() { e.to_vec() } else { vec![] };
102 validation::validate(&self.syntax_node(), &mut errors);
103 errors
104 }
105}
106
107impl<T: AstNode> Parse<T> {
108 pub fn to_syntax(mut self) -> Parse<SyntaxNode> {
110 let green = self.green.take();
111 let errors = self.errors.take();
112 Parse { green, errors, _ty: PhantomData }
113 }
114
115 pub fn tree(&self) -> T {
122 T::cast(self.syntax_node()).unwrap()
123 }
124
125 pub fn ok(self) -> Result<T, Vec<SyntaxError>> {
127 match self.errors() {
128 errors if !errors.is_empty() => Err(errors),
129 _ => Ok(self.tree()),
130 }
131 }
132}
133
134impl Parse<SyntaxNode> {
135 pub fn cast<N: AstNode>(mut self) -> Option<Parse<N>> {
136 if N::cast(self.syntax_node()).is_some() {
137 Some(Parse { green: self.green.take(), errors: self.errors.take(), _ty: PhantomData })
138 } else {
139 None
140 }
141 }
142}
143
144impl Parse<SourceFile> {
145 pub fn debug_dump(&self) -> String {
146 let mut buf = format!("{:#?}", self.tree().syntax());
147 for err in self.errors() {
148 format_to!(buf, "error {:?}: {}\n", err.range(), err);
149 }
150 buf
151 }
152
153 pub fn reparse(&self, delete: TextRange, insert: &str, edition: Edition) -> Parse<SourceFile> {
154 self.incremental_reparse(delete, insert, edition)
155 .unwrap_or_else(|| self.full_reparse(delete, insert, edition))
156 }
157
158 fn incremental_reparse(
159 &self,
160 delete: TextRange,
161 insert: &str,
162 edition: Edition,
163 ) -> Option<Parse<SourceFile>> {
164 parsing::incremental_reparse(
166 self.tree().syntax(),
167 delete,
168 insert,
169 self.errors.as_deref().unwrap_or_default().iter().cloned(),
170 edition,
171 )
172 .map(|(green_node, errors, _reparsed_range)| Parse {
173 green: Some(green_node),
174 errors: if errors.is_empty() { None } else { Some(errors.into()) },
175 _ty: PhantomData,
176 })
177 }
178
179 fn full_reparse(&self, delete: TextRange, insert: &str, edition: Edition) -> Parse<SourceFile> {
180 let mut text = self.tree().syntax().text().to_string();
181 text.replace_range(Range::<usize>::from(delete), insert);
182 SourceFile::parse(&text, edition)
183 }
184}
185
186impl ast::Expr {
187 pub fn parse(text: &str, edition: Edition) -> Parse<ast::Expr> {
196 let _p = tracing::info_span!("Expr::parse").entered();
197 let (green, errors) = parsing::parse_text_at(text, parser::TopEntryPoint::Expr, edition);
198 let root = SyntaxNode::new_root(green.clone());
199
200 assert!(
201 ast::Expr::can_cast(root.kind()) || root.kind() == SyntaxKind::ERROR,
202 "{:?} isn't an expression",
203 root.kind()
204 );
205 Parse::new(green, errors)
206 }
207}
208
209#[cfg(not(no_salsa_async_drops))]
210impl<T> Drop for Parse<T> {
211 fn drop(&mut self) {
212 let Some(green) = self.green.take() else {
213 return;
214 };
215 static PARSE_DROP_THREAD: std::sync::OnceLock<std::sync::mpsc::Sender<GreenNode>> =
216 std::sync::OnceLock::new();
217 PARSE_DROP_THREAD
218 .get_or_init(|| {
219 let (sender, receiver) = std::sync::mpsc::channel::<GreenNode>();
220 std::thread::Builder::new()
221 .name("ParseNodeDropper".to_owned())
222 .spawn(move || {
223 loop {
224 _ = receiver.recv();
226 while receiver.try_recv().is_ok() {}
228 std::thread::sleep(std::time::Duration::from_millis(100));
230 }
231 })
234 .unwrap();
235 sender
236 })
237 .send(green)
238 .unwrap();
239 }
240}
241
242pub use crate::ast::SourceFile;
244
245impl SourceFile {
246 pub fn parse(text: &str, edition: Edition) -> Parse<SourceFile> {
247 let _p = tracing::info_span!("SourceFile::parse").entered();
248 let (green, errors) = parsing::parse_text(text, edition);
249 let root = SyntaxNode::new_root(green.clone());
250
251 assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
252 Parse::new(green, errors)
253 }
254}
255
256#[macro_export]
271macro_rules! match_ast {
272 (match $node:ident { $($tt:tt)* }) => { $crate::match_ast!(match ($node) { $($tt)* }) };
273
274 (match ($node:expr) {
275 $( $( $path:ident )::+ ($it:pat) $(if $guard:expr)? => $res:expr, )*
276 _ => $catch_all:expr $(,)?
277 }) => {{
278 #[allow(clippy::question_mark, reason = "if `$catch_all` is `return None` Clippy can mark this")]
279 {
280 $( if let Some($it) = $($path::)+cast($node.clone()) $(&& $guard)? { $res } else )*
281 { $catch_all }
282 }
283 }};
284}
285
286#[test]
289fn api_walkthrough() {
290 use ast::{HasModuleItem, HasName};
291
292 let source_code = "
293 fn foo() {
294 1 + 1
295 }
296 ";
297 let parse = SourceFile::parse(source_code, parser::Edition::CURRENT);
302 assert!(parse.errors().is_empty());
303
304 let file: SourceFile = parse.tree();
307
308 let mut func = None;
311 for item in file.items() {
312 match item {
313 ast::Item::Fn(f) => func = Some(f),
314 _ => unreachable!(),
315 }
316 }
317 let func: ast::Fn = func.unwrap();
318
319 let name: Option<ast::Name> = func.name();
325 let name = name.unwrap();
326 assert_eq!(name.text(), "foo");
327
328 let body: ast::BlockExpr = func.body().unwrap();
330 let stmt_list: ast::StmtList = body.stmt_list().unwrap();
331 let expr: ast::Expr = stmt_list.tail_expr().unwrap();
332
333 let bin_expr: &ast::BinExpr = match &expr {
338 ast::Expr::BinExpr(e) => e,
339 _ => unreachable!(),
340 };
341
342 let expr_syntax: &SyntaxNode = expr.syntax();
345
346 assert!(expr_syntax == bin_expr.syntax());
348
349 let _expr: ast::Expr = match ast::Expr::cast(expr_syntax.clone()) {
351 Some(e) => e,
352 None => unreachable!(),
353 };
354
355 assert_eq!(expr_syntax.kind(), SyntaxKind::BIN_EXPR);
357
358 assert_eq!(expr_syntax.text_range(), TextRange::new(32.into(), 37.into()));
360
361 let text: SyntaxText = expr_syntax.text();
364 assert_eq!(text.to_string(), "1 + 1");
365
366 assert_eq!(expr_syntax.parent().as_ref(), Some(stmt_list.syntax()));
368 assert_eq!(stmt_list.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
369 assert_eq!(
370 expr_syntax.next_sibling_or_token().map(|it| it.kind()),
371 Some(SyntaxKind::WHITESPACE)
372 );
373
374 let f = expr_syntax.ancestors().find_map(ast::Fn::cast);
376 assert_eq!(f, Some(func));
377 assert!(expr_syntax.siblings_with_tokens(Direction::Next).any(|it| it.kind() == T!['}']));
378 assert_eq!(
379 expr_syntax.descendants_with_tokens().count(),
380 8, );
384
385 let mut buf = String::new();
387 let mut indent = 0;
388 for event in expr_syntax.preorder_with_tokens() {
389 match event {
390 WalkEvent::Enter(node) => {
391 let text = match &node {
392 NodeOrToken::Node(it) => it.text().to_string(),
393 NodeOrToken::Token(it) => it.text().to_owned(),
394 };
395 format_to!(buf, "{:indent$}{:?} {:?}\n", " ", text, node.kind(), indent = indent);
396 indent += 2;
397 }
398 WalkEvent::Leave(_) => indent -= 2,
399 }
400 }
401 assert_eq!(indent, 0);
402 assert_eq!(
403 buf.trim(),
404 r#"
405"1 + 1" BIN_EXPR
406 "1" LITERAL
407 "1" INT_NUMBER
408 " " WHITESPACE
409 "+" PLUS
410 " " WHITESPACE
411 "1" LITERAL
412 "1" INT_NUMBER
413"#
414 .trim()
415 );
416
417 let exprs_cast: Vec<String> = file
424 .syntax()
425 .descendants()
426 .filter_map(ast::Expr::cast)
427 .map(|expr| expr.syntax().text().to_string())
428 .collect();
429
430 let mut exprs_visit = Vec::new();
432 for node in file.syntax().descendants() {
433 match_ast! {
434 match node {
435 ast::Expr(it) => {
436 let res = it.syntax().text().to_string();
437 exprs_visit.push(res);
438 },
439 _ => (),
440 }
441 }
442 }
443 assert_eq!(exprs_cast, exprs_visit);
444}