ide_ssr/
fragments.rs

1//! When specifying SSR rule, you generally want to map one *kind* of thing to
2//! the same kind of thing: path to path, expression to expression, type to
3//! type.
4//!
5//! The problem is, while this *kind* is generally obvious to the human, the ide
6//! needs to determine it somehow. We do this in a stupid way -- by pasting SSR
7//! rule into different contexts and checking what works.
8
9use syntax::{AstNode, SyntaxNode, ast};
10
11pub(crate) fn ty(s: &str) -> Result<SyntaxNode, ()> {
12    fragment::<ast::Type>("type T = {};", s)
13}
14
15pub(crate) fn item(s: &str) -> Result<SyntaxNode, ()> {
16    fragment::<ast::Item>("{}", s)
17}
18
19pub(crate) fn pat(s: &str) -> Result<SyntaxNode, ()> {
20    fragment::<ast::Pat>("const _: () = {let {} = ();};", s)
21}
22
23pub(crate) fn expr(s: &str) -> Result<SyntaxNode, ()> {
24    fragment::<ast::Expr>("const _: () = {};", s)
25}
26
27pub(crate) fn stmt(s: &str) -> Result<SyntaxNode, ()> {
28    let template = "const _: () = { {}; };";
29    let input = template.replace("{}", s);
30    let parse = syntax::SourceFile::parse(&input, syntax::Edition::CURRENT);
31    if !parse.errors().is_empty() {
32        return Err(());
33    }
34    let mut node =
35        parse.tree().syntax().descendants().skip(2).find_map(ast::Stmt::cast).ok_or(())?;
36    if !s.ends_with(';') && node.to_string().ends_with(';') {
37        node = node.clone_for_update();
38        if let Some(it) = node.syntax().last_token() {
39            it.detach()
40        }
41    }
42    if node.to_string() != s {
43        return Err(());
44    }
45    Ok(node.syntax().clone_subtree())
46}
47
48fn fragment<T: AstNode>(template: &str, s: &str) -> Result<SyntaxNode, ()> {
49    let s = s.trim();
50    let input = template.replace("{}", s);
51    let parse = syntax::SourceFile::parse(&input, syntax::Edition::CURRENT);
52    if !parse.errors().is_empty() {
53        return Err(());
54    }
55    let node = parse.tree().syntax().descendants().find_map(T::cast).ok_or(())?;
56    if node.syntax().text() != s {
57        return Err(());
58    }
59    Ok(node.syntax().clone_subtree())
60}