about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-12-28 16:22:20 +0000
committerGitHub <noreply@github.com>2021-12-28 16:22:20 +0000
commit355a4bdb883ee9edc1c32b553fcf91c302b3df19 (patch)
treee0fc3236e0472c44f5a9f02bcc9f8418de374708
parent4d3ad04a9c84ac70c15465c1124c4d4dbef3b772 (diff)
parentea96c376c85e02ec86df5ff6522754395ad819e9 (diff)
downloadrust-355a4bdb883ee9edc1c32b553fcf91c302b3df19.tar.gz
rust-355a4bdb883ee9edc1c32b553fcf91c302b3df19.zip
Merge #11134
11134: internal: tighten up parser API r=matklad a=matklad

It's tempting to expose things like `Expr::parse`, 
but they'll necessary have somewhat ad-hoc semantics. 

Instead, we narrow down the parser's interface strictly 
to what's needed for MBE. For everything else (eg, parsing
imports), the proper way is enclose the input string into 
some context, parse the whole as a file, and then verify 
that the input was parsed as intended.

bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
-rw-r--r--crates/hir/src/attrs.rs16
-rw-r--r--crates/hir_def/src/attr.rs3
-rw-r--r--crates/hir_expand/src/builtin_derive_macro.rs2
-rw-r--r--crates/hir_expand/src/db.rs10
-rw-r--r--crates/hir_expand/src/eager.rs2
-rw-r--r--crates/ide_assists/src/handlers/remove_dbg.rs5
-rw-r--r--crates/ide_completion/src/completions/attribute.rs2
-rw-r--r--crates/ide_completion/src/snippet.rs13
-rw-r--r--crates/ide_db/src/helpers.rs13
-rw-r--r--crates/ide_ssr/src/fragments.rs58
-rw-r--r--crates/ide_ssr/src/lib.rs1
-rw-r--r--crates/ide_ssr/src/parsing.rs37
-rw-r--r--crates/ide_ssr/src/replacing.rs12
-rw-r--r--crates/ide_ssr/src/tests.rs22
-rw-r--r--crates/mbe/src/expander/matcher.rs52
-rw-r--r--crates/mbe/src/lib.rs2
-rw-r--r--crates/mbe/src/syntax_bridge.rs10
-rw-r--r--crates/mbe/src/tt_iter.rs6
-rw-r--r--crates/parser/src/grammar.rs102
-rw-r--r--crates/parser/src/lib.rs126
-rw-r--r--crates/parser/src/shortcuts.rs7
-rw-r--r--crates/parser/src/tests.rs4
-rw-r--r--crates/syntax/src/hacks.rs15
-rw-r--r--crates/syntax/src/lib.rs56
-rw-r--r--crates/syntax/src/parsing.rs29
-rw-r--r--crates/syntax/src/parsing/reparsing.rs2
-rw-r--r--crates/syntax/src/tests.rs72
-rw-r--r--crates/syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/expr/ok/0000_add.rast8
-rw-r--r--crates/syntax/test_data/parser/fragments/expr/ok/0000_add.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/item/ok/0000_fn.rast13
-rw-r--r--crates/syntax/test_data/parser/fragments/item/ok/0000_fn.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/path/err/0000_reserved_word.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/path/err/0000_reserved_word.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/path/err/0001_expression.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/path/err/0001_expression.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/path/ok/0000_single_ident.rast4
-rw-r--r--crates/syntax/test_data/parser/fragments/path/ok/0000_single_ident.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/path/ok/0001_multipart.rast14
-rw-r--r--crates/syntax/test_data/parser/fragments/path/ok/0001_multipart.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/pattern/ok/0000_enum.rast10
-rw-r--r--crates/syntax/test_data/parser/fragments/pattern/ok/0000_enum.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast9
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast70
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs5
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast11
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast12
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast21
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast21
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast22
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs3
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast10
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast11
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/type/err/0000_missing_close.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/type/err/0000_missing_close.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/type/ok/0000_result.rast22
-rw-r--r--crates/syntax/test_data/parser/fragments/type/ok/0000_result.rs1
81 files changed, 331 insertions, 651 deletions
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 54e95cf7c3d..46e9c54dad6 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -11,7 +11,7 @@ use hir_def::{
 };
 use hir_expand::{hygiene::Hygiene, MacroDefId};
 use hir_ty::db::HirDatabase;
-use syntax::ast;
+use syntax::{ast, AstNode};
 
 use crate::{
     Adt, AssocItem, Const, ConstParam, Enum, Field, Function, GenericParam, Impl, LifetimeParam,
@@ -147,8 +147,18 @@ fn resolve_doc_path(
         // FIXME
         AttrDefId::MacroDefId(_) => return None,
     };
-    let path = ast::Path::parse(link).ok()?;
-    let modpath = ModPath::from_src(db.upcast(), path, &Hygiene::new_unhygienic())?;
+
+    let modpath = {
+        let ast_path = ast::SourceFile::parse(&format!("type T = {};", link))
+            .syntax_node()
+            .descendants()
+            .find_map(ast::Path::cast)?;
+        if ast_path.to_string() != link {
+            return None;
+        }
+        ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())?
+    };
+
     let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
     let resolved = if resolved == PerNs::none() {
         resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)?
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 36e46a103c1..383ad7f0c83 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -714,8 +714,7 @@ impl Attr {
         hygiene: &Hygiene,
         id: AttrId,
     ) -> Option<Attr> {
-        let (parse, _) =
-            mbe::token_tree_to_syntax_node(tt, mbe::ParserEntryPoint::MetaItem).ok()?;
+        let (parse, _) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MetaItem).ok()?;
         let ast = ast::Meta::cast(parse.syntax_node())?;
 
         Self::from_src(db, ast, hygiene, id)
diff --git a/crates/hir_expand/src/builtin_derive_macro.rs b/crates/hir_expand/src/builtin_derive_macro.rs
index c20dae8e6f7..c1542f48f0e 100644
--- a/crates/hir_expand/src/builtin_derive_macro.rs
+++ b/crates/hir_expand/src/builtin_derive_macro.rs
@@ -72,7 +72,7 @@ struct BasicAdtInfo {
 }
 
 fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
-    let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::ParserEntryPoint::Items)?; // FragmentKind::Items doesn't parse attrs?
+    let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems)?; // FragmentKind::Items doesn't parse attrs?
     let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
         debug!("derive node didn't parse");
         mbe::ExpandError::UnexpectedToken
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 747a19a5092..3369e3e5fed 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -497,11 +497,11 @@ fn token_tree_to_syntax_node(
     expand_to: ExpandTo,
 ) -> Result<(Parse<SyntaxNode>, mbe::TokenMap), ExpandError> {
     let entry_point = match expand_to {
-        ExpandTo::Statements => mbe::ParserEntryPoint::Statements,
-        ExpandTo::Items => mbe::ParserEntryPoint::Items,
-        ExpandTo::Pattern => mbe::ParserEntryPoint::Pattern,
-        ExpandTo::Type => mbe::ParserEntryPoint::Type,
-        ExpandTo::Expr => mbe::ParserEntryPoint::Expr,
+        ExpandTo::Statements => mbe::TopEntryPoint::MacroStmts,
+        ExpandTo::Items => mbe::TopEntryPoint::MacroItems,
+        ExpandTo::Pattern => mbe::TopEntryPoint::Pattern,
+        ExpandTo::Type => mbe::TopEntryPoint::Type,
+        ExpandTo::Expr => mbe::TopEntryPoint::Expr,
     };
     mbe::token_tree_to_syntax_node(tt, entry_point)
 }
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index ea57c2c4101..1d29ad26307 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -131,7 +131,7 @@ pub fn expand_eager_macro(
     let arg_file_id = arg_id;
 
     let parsed_args = diagnostic_sink
-        .result(mbe::token_tree_to_syntax_node(&parsed_args, mbe::ParserEntryPoint::Expr))?
+        .result(mbe::token_tree_to_syntax_node(&parsed_args, mbe::TopEntryPoint::Expr))?
         .0;
     let result = eager_macro_recur(
         db,
diff --git a/crates/ide_assists/src/handlers/remove_dbg.rs b/crates/ide_assists/src/handlers/remove_dbg.rs
index b860a3b6da8..07dcfd96717 100644
--- a/crates/ide_assists/src/handlers/remove_dbg.rs
+++ b/crates/ide_assists/src/handlers/remove_dbg.rs
@@ -36,9 +36,8 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let input_expressions = input_expressions
         .into_iter()
         .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
-        .map(|mut tokens| ast::Expr::parse(&tokens.join("")))
-        .collect::<Result<Vec<ast::Expr>, _>>()
-        .ok()?;
+        .map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join("")))
+        .collect::<Option<Vec<ast::Expr>>>()?;
 
     let parent = macro_call.syntax().parent()?;
     let (range, text) = match &*input_expressions {
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index d763878834f..f67d7d56d81 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -309,7 +309,7 @@ fn parse_comma_sep_expr(input: ast::TokenTree) -> Option<Vec<ast::Expr>> {
         input_expressions
             .into_iter()
             .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
-            .filter_map(|mut tokens| ast::Expr::parse(&tokens.join("")).ok())
+            .filter_map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join("")))
             .collect::<Vec<ast::Expr>>(),
     )
 }
diff --git a/crates/ide_completion/src/snippet.rs b/crates/ide_completion/src/snippet.rs
index c5e2b009c7c..98cd3f8f337 100644
--- a/crates/ide_completion/src/snippet.rs
+++ b/crates/ide_completion/src/snippet.rs
@@ -212,15 +212,14 @@ fn validate_snippet(
 ) -> Option<(Box<[GreenNode]>, String, Option<Box<str>>)> {
     let mut imports = Vec::with_capacity(requires.len());
     for path in requires.iter() {
-        let path = ast::Path::parse(path).ok()?;
-        let valid_use_path = path.segments().all(|seg| {
-            matches!(seg.kind(), Some(ast::PathSegmentKind::Name(_)))
-                || seg.generic_arg_list().is_none()
-        });
-        if !valid_use_path {
+        let use_path = ast::SourceFile::parse(&format!("use {};", path))
+            .syntax_node()
+            .descendants()
+            .find_map(ast::Path::cast)?;
+        if use_path.syntax().text() != path.as_str() {
             return None;
         }
-        let green = path.syntax().green().into_owned();
+        let green = use_path.syntax().green().into_owned();
         imports.push(green);
     }
     let snippet = snippet.iter().join("\n");
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index e4199898bfc..e589940dae2 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -67,7 +67,11 @@ pub fn get_path_at_cursor_in_tt(cursor: &ast::Ident) -> Option<ast::Path> {
         .filter_map(SyntaxElement::into_token)
         .take_while(|tok| tok != cursor);
 
-    ast::Path::parse(&path_tokens.chain(iter::once(cursor.clone())).join("")).ok()
+    syntax::hacks::parse_expr_from_str(&path_tokens.chain(iter::once(cursor.clone())).join(""))
+        .and_then(|expr| match expr {
+            ast::Expr::PathExpr(it) => it.path(),
+            _ => None,
+        })
 }
 
 /// Parses and resolves the path at the cursor position in the given attribute, if it is a derive.
@@ -323,7 +327,12 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Pat
     let paths = input_expressions
         .into_iter()
         .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
-        .filter_map(|mut tokens| ast::Path::parse(&tokens.join("")).ok())
+        .filter_map(|mut tokens| {
+            syntax::hacks::parse_expr_from_str(&tokens.join("")).and_then(|expr| match expr {
+                ast::Expr::PathExpr(it) => it.path(),
+                _ => None,
+            })
+        })
         .collect();
     Some(paths)
 }
diff --git a/crates/ide_ssr/src/fragments.rs b/crates/ide_ssr/src/fragments.rs
new file mode 100644
index 00000000000..503754afe7c
--- /dev/null
+++ b/crates/ide_ssr/src/fragments.rs
@@ -0,0 +1,58 @@
+//! When specifying SSR rule, you generally want to map one *kind* of thing to
+//! the same kind of thing: path to path, expression to expression, type to
+//! type.
+//!
+//! The problem is, while this *kind* is generally obvious to the human, the ide
+//! needs to determine it somehow. We do this in a stupid way -- by pasting SSR
+//! rule into different contexts and checking what works.
+
+use syntax::{ast, AstNode, SyntaxNode};
+
+pub(crate) fn ty(s: &str) -> Result<SyntaxNode, ()> {
+    fragment::<ast::Type>("type T = {};", s)
+}
+
+pub(crate) fn item(s: &str) -> Result<SyntaxNode, ()> {
+    fragment::<ast::Item>("{}", s)
+}
+
+pub(crate) fn pat(s: &str) -> Result<SyntaxNode, ()> {
+    fragment::<ast::Pat>("const _: () = {let {} = ();};", s)
+}
+
+pub(crate) fn expr(s: &str) -> Result<SyntaxNode, ()> {
+    fragment::<ast::Expr>("const _: () = {};", s)
+}
+
+pub(crate) fn stmt(s: &str) -> Result<SyntaxNode, ()> {
+    let template = "const _: () = { {}; };";
+    let input = template.replace("{}", s);
+    let parse = syntax::SourceFile::parse(&input);
+    if !parse.errors().is_empty() {
+        return Err(());
+    }
+    let mut node =
+        parse.tree().syntax().descendants().skip(2).find_map(ast::Stmt::cast).ok_or(())?;
+    if !s.ends_with(';') && node.to_string().ends_with(';') {
+        node = node.clone_for_update();
+        node.syntax().last_token().map(|it| it.detach());
+    }
+    if node.to_string() != s {
+        return Err(());
+    }
+    Ok(node.syntax().clone_subtree())
+}
+
+fn fragment<T: AstNode>(template: &str, s: &str) -> Result<SyntaxNode, ()> {
+    let s = s.trim();
+    let input = template.replace("{}", s);
+    let parse = syntax::SourceFile::parse(&input);
+    if !parse.errors().is_empty() {
+        return Err(());
+    }
+    let node = parse.tree().syntax().descendants().find_map(T::cast).ok_or(())?;
+    if node.syntax().text() != s {
+        return Err(());
+    }
+    Ok(node.syntax().clone_subtree())
+}
diff --git a/crates/ide_ssr/src/lib.rs b/crates/ide_ssr/src/lib.rs
index 2fe1f5b616e..d56bc12b680 100644
--- a/crates/ide_ssr/src/lib.rs
+++ b/crates/ide_ssr/src/lib.rs
@@ -71,6 +71,7 @@ mod from_comment;
 mod matching;
 mod nester;
 mod parsing;
+mod fragments;
 mod replacing;
 mod resolving;
 mod search;
diff --git a/crates/ide_ssr/src/parsing.rs b/crates/ide_ssr/src/parsing.rs
index ae7d5b4bf15..aaaee576b52 100644
--- a/crates/ide_ssr/src/parsing.rs
+++ b/crates/ide_ssr/src/parsing.rs
@@ -4,12 +4,12 @@
 //! placeholders, which start with `$`. For replacement templates, this is the final form. For
 //! search patterns, we go further and parse the pattern as each kind of thing that we can match.
 //! e.g. expressions, type references etc.
-
-use crate::errors::bail;
-use crate::{SsrError, SsrPattern, SsrRule};
 use rustc_hash::{FxHashMap, FxHashSet};
 use std::{fmt::Display, str::FromStr};
-use syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T};
+use syntax::{SmolStr, SyntaxKind, SyntaxNode, T};
+
+use crate::errors::bail;
+use crate::{fragments, SsrError, SsrPattern, SsrRule};
 
 #[derive(Debug)]
 pub(crate) struct ParsedRule {
@@ -73,17 +73,16 @@ impl ParsedRule {
             rules: Vec::new(),
         };
 
-        let raw_template_stmt = raw_template.map(ast::Stmt::parse);
-        if let raw_template_expr @ Some(Ok(_)) = raw_template.map(ast::Expr::parse) {
-            builder.try_add(ast::Expr::parse(&raw_pattern), raw_template_expr);
+        let raw_template_stmt = raw_template.map(fragments::stmt);
+        if let raw_template_expr @ Some(Ok(_)) = raw_template.map(fragments::expr) {
+            builder.try_add(fragments::expr(&raw_pattern), raw_template_expr);
         } else {
-            builder.try_add(ast::Expr::parse(&raw_pattern), raw_template_stmt.clone());
+            builder.try_add(fragments::expr(&raw_pattern), raw_template_stmt.clone());
         }
-        builder.try_add(ast::Type::parse(&raw_pattern), raw_template.map(ast::Type::parse));
-        builder.try_add(ast::Item::parse(&raw_pattern), raw_template.map(ast::Item::parse));
-        builder.try_add(ast::Path::parse(&raw_pattern), raw_template.map(ast::Path::parse));
-        builder.try_add(ast::Pat::parse(&raw_pattern), raw_template.map(ast::Pat::parse));
-        builder.try_add(ast::Stmt::parse(&raw_pattern), raw_template_stmt);
+        builder.try_add(fragments::ty(&raw_pattern), raw_template.map(fragments::ty));
+        builder.try_add(fragments::item(&raw_pattern), raw_template.map(fragments::item));
+        builder.try_add(fragments::pat(&raw_pattern), raw_template.map(fragments::pat));
+        builder.try_add(fragments::stmt(&raw_pattern), raw_template_stmt);
         builder.build()
     }
 }
@@ -94,20 +93,20 @@ struct RuleBuilder {
 }
 
 impl RuleBuilder {
-    fn try_add<T: AstNode, T2: AstNode>(
+    fn try_add(
         &mut self,
-        pattern: Result<T, ()>,
-        template: Option<Result<T2, ()>>,
+        pattern: Result<SyntaxNode, ()>,
+        template: Option<Result<SyntaxNode, ()>>,
     ) {
         match (pattern, template) {
             (Ok(pattern), Some(Ok(template))) => self.rules.push(ParsedRule {
                 placeholders_by_stand_in: self.placeholders_by_stand_in.clone(),
-                pattern: pattern.syntax().clone(),
-                template: Some(template.syntax().clone()),
+                pattern,
+                template: Some(template),
             }),
             (Ok(pattern), None) => self.rules.push(ParsedRule {
                 placeholders_by_stand_in: self.placeholders_by_stand_in.clone(),
-                pattern: pattern.syntax().clone(),
+                pattern,
                 template: None,
             }),
             _ => {}
diff --git a/crates/ide_ssr/src/replacing.rs b/crates/ide_ssr/src/replacing.rs
index 9265af7c13a..6d21bad1eb3 100644
--- a/crates/ide_ssr/src/replacing.rs
+++ b/crates/ide_ssr/src/replacing.rs
@@ -1,5 +1,6 @@
 //! Code for applying replacement templates for matches that have previously been found.
 
+use crate::fragments;
 use crate::{resolving::ResolvedRule, Match, SsrMatches};
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -225,12 +226,13 @@ fn token_is_method_call_receiver(token: &SyntaxToken) -> bool {
 
 fn parse_as_kind(code: &str, kind: SyntaxKind) -> Option<SyntaxNode> {
     if ast::Expr::can_cast(kind) {
-        if let Ok(expr) = ast::Expr::parse(code) {
-            return Some(expr.syntax().clone());
+        if let Ok(expr) = fragments::expr(code) {
+            return Some(expr);
         }
-    } else if ast::Item::can_cast(kind) {
-        if let Ok(item) = ast::Item::parse(code) {
-            return Some(item.syntax().clone());
+    }
+    if ast::Item::can_cast(kind) {
+        if let Ok(item) = fragments::item(code) {
+            return Some(item);
         }
     }
     None
diff --git a/crates/ide_ssr/src/tests.rs b/crates/ide_ssr/src/tests.rs
index 0b0c1111c46..30eda9d56cb 100644
--- a/crates/ide_ssr/src/tests.rs
+++ b/crates/ide_ssr/src/tests.rs
@@ -332,6 +332,15 @@ fn ssr_struct_lit() {
 }
 
 #[test]
+fn ssr_struct_def() {
+    assert_ssr_transform(
+        "struct Foo { $f: $t } ==>> struct Foo($t);",
+        r#"struct Foo { field: i32 }"#,
+        expect![[r#"struct Foo(i32);"#]],
+    )
+}
+
+#[test]
 fn ignores_whitespace() {
     assert_matches("1+2", "fn f() -> i32 {1  +  2}", &["1  +  2"]);
     assert_matches("1 + 2", "fn f() -> i32 {1+2}", &["1+2"]);
@@ -792,6 +801,19 @@ fn replace_type() {
             "struct Result<T, E> {} struct Option<T> {} fn f1() -> Option<Vec<Error>> {foo()}"
         ]],
     );
+    assert_ssr_transform(
+        "dyn Trait<$a> ==>> DynTrait<$a>",
+        r#"
+trait Trait<T> {}
+struct DynTrait<T> {}
+fn f1() -> dyn Trait<Vec<Error>> {foo()}
+"#,
+        expect![[r#"
+trait Trait<T> {}
+struct DynTrait<T> {}
+fn f1() -> DynTrait<Vec<Error>> {foo()}
+"#]],
+    );
 }
 
 #[test]
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index b2d3f038f54..bcda2381a48 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -61,18 +61,16 @@
 
 use std::rc::Rc;
 
+use smallvec::{smallvec, SmallVec};
+use syntax::SmolStr;
+
 use crate::{
-    expander::{Binding, Bindings, Fragment},
+    expander::{Binding, Bindings, ExpandResult, Fragment},
     parser::{Op, RepeatKind, Separator},
     tt_iter::TtIter,
     ExpandError, MetaTemplate,
 };
 
-use super::ExpandResult;
-use parser::ParserEntryPoint::*;
-use smallvec::{smallvec, SmallVec};
-use syntax::SmolStr;
-
 impl Bindings {
     fn push_optional(&mut self, name: &SmolStr) {
         // FIXME: Do we have a better way to represent an empty token ?
@@ -691,14 +689,21 @@ fn match_leaf(lhs: &tt::Leaf, src: &mut TtIter) -> Result<(), ExpandError> {
 
 fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragment>> {
     let fragment = match kind {
-        "path" => Path,
-        "expr" => Expr,
-        "ty" => Type,
-        "pat" | "pat_param" => Pattern, // FIXME: edition2021
-        "stmt" => Statement,
-        "block" => Block,
-        "meta" => MetaItem,
-        "item" => Item,
+        "path" => parser::PrefixEntryPoint::Path,
+        "ty" => parser::PrefixEntryPoint::Ty,
+        // FIXME: These two should actually behave differently depending on the edition.
+        //
+        // https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html
+        "pat" | "pat_param" => parser::PrefixEntryPoint::Pat,
+        "stmt" => parser::PrefixEntryPoint::Stmt,
+        "block" => parser::PrefixEntryPoint::Block,
+        "meta" => parser::PrefixEntryPoint::MetaItem,
+        "item" => parser::PrefixEntryPoint::Item,
+        "expr" => {
+            return input
+                .expect_fragment(parser::PrefixEntryPoint::Expr)
+                .map(|tt| tt.map(Fragment::Expr))
+        }
         _ => {
             let tt_result = match kind {
                 "ident" => input
@@ -726,17 +731,13 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragmen
                         .map_err(|()| err!())
                 }
                 // `vis` is optional
-                "vis" => match input.eat_vis() {
-                    Some(vis) => Ok(Some(vis)),
-                    None => Ok(None),
-                },
+                "vis" => Ok(input.expect_fragment(parser::PrefixEntryPoint::Vis).value),
                 _ => Err(ExpandError::UnexpectedToken),
             };
             return tt_result.map(|it| it.map(Fragment::Tokens)).into();
         }
     };
-    let result = input.expect_fragment(fragment);
-    result.map(|tt| if kind == "expr" { tt.map(Fragment::Expr) } else { tt.map(Fragment::Tokens) })
+    input.expect_fragment(fragment).map(|it| it.map(Fragment::Tokens))
 }
 
 fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &MetaTemplate) {
@@ -898,17 +899,6 @@ impl<'a> TtIter<'a> {
         .into())
     }
 
-    fn eat_vis(&mut self) -> Option<tt::TokenTree> {
-        let mut fork = self.clone();
-        match fork.expect_fragment(Visibility) {
-            ExpandResult { value: tt, err: None } => {
-                *self = fork;
-                tt
-            }
-            ExpandResult { value: _, err: Some(_) } => None,
-        }
-    }
-
     fn eat_char(&mut self, c: char) -> Option<tt::TokenTree> {
         let mut fork = self.clone();
         match fork.expect_char(c) {
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 5e14a3fb590..62e7509eb37 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -24,7 +24,7 @@ use crate::{
 };
 
 // FIXME: we probably should re-think  `token_tree_to_syntax_node` interfaces
-pub use ::parser::ParserEntryPoint;
+pub use ::parser::TopEntryPoint;
 pub use tt::{Delimiter, DelimiterKind, Punct};
 
 #[derive(Debug, PartialEq, Eq, Clone)]
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index f0c1f806ffa..8bdc5e6e946 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -9,9 +9,7 @@ use syntax::{
 };
 use tt::buffer::{Cursor, TokenBuffer};
 
-use crate::{
-    to_parser_input::to_parser_input, tt_iter::TtIter, ExpandError, ParserEntryPoint, TokenMap,
-};
+use crate::{to_parser_input::to_parser_input, tt_iter::TtIter, ExpandError, TokenMap};
 
 /// Convert the syntax node to a `TokenTree` (what macro
 /// will consume).
@@ -46,7 +44,7 @@ pub fn syntax_node_to_token_tree_censored(
 
 pub fn token_tree_to_syntax_node(
     tt: &tt::Subtree,
-    entry_point: ParserEntryPoint,
+    entry_point: parser::TopEntryPoint,
 ) -> Result<(Parse<SyntaxNode>, TokenMap), ExpandError> {
     let buffer = match tt {
         tt::Subtree { delimiter: None, token_trees } => {
@@ -55,7 +53,7 @@ pub fn token_tree_to_syntax_node(
         _ => TokenBuffer::from_subtree(tt),
     };
     let parser_input = to_parser_input(&buffer);
-    let parser_output = parser::parse(&parser_input, entry_point);
+    let parser_output = entry_point.parse(&parser_input);
     let mut tree_sink = TtTreeSink::new(buffer.begin());
     for event in parser_output.iter() {
         match event {
@@ -106,7 +104,7 @@ pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char) -> Vec<tt::Subtree> {
     let mut res = Vec::new();
 
     while iter.peek_n(0).is_some() {
-        let expanded = iter.expect_fragment(ParserEntryPoint::Expr);
+        let expanded = iter.expect_fragment(parser::PrefixEntryPoint::Expr);
 
         res.push(match expanded.value {
             None => break,
diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs
index 2d2dbd8994f..6c9f615c7a9 100644
--- a/crates/mbe/src/tt_iter.rs
+++ b/crates/mbe/src/tt_iter.rs
@@ -1,7 +1,7 @@
 //! A "Parser" structure for token trees. We use this when parsing a declarative
 //! macro definition into a list of patterns and templates.
 
-use crate::{to_parser_input::to_parser_input, ExpandError, ExpandResult, ParserEntryPoint};
+use crate::{to_parser_input::to_parser_input, ExpandError, ExpandResult};
 
 use syntax::SyntaxKind;
 use tt::buffer::TokenBuffer;
@@ -91,11 +91,11 @@ impl<'a> TtIter<'a> {
 
     pub(crate) fn expect_fragment(
         &mut self,
-        entry_point: ParserEntryPoint,
+        entry_point: parser::PrefixEntryPoint,
     ) -> ExpandResult<Option<tt::TokenTree>> {
         let buffer = TokenBuffer::from_tokens(self.inner.as_slice());
         let parser_input = to_parser_input(&buffer);
-        let tree_traversal = parser::parse(&parser_input, entry_point);
+        let tree_traversal = entry_point.parse(&parser_input);
 
         let mut cursor = buffer.begin();
         let mut error = false;
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 25178ddd775..42426a1df28 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -44,72 +44,76 @@ use crate::{
     TokenSet, T,
 };
 
-pub(crate) mod entry_points {
+pub(crate) mod entry {
     use super::*;
 
-    pub(crate) fn source_file(p: &mut Parser) {
-        let m = p.start();
-        p.eat(SHEBANG);
-        items::mod_contents(p, false);
-        m.complete(p, SOURCE_FILE);
-    }
-
-    pub(crate) use expressions::block_expr;
-
-    pub(crate) use paths::type_path as path;
+    pub(crate) mod prefix {
+        use super::*;
 
-    pub(crate) use patterns::pattern_single as pattern;
-
-    pub(crate) use types::type_;
+        pub(crate) fn vis(p: &mut Parser) {
+            let _ = opt_visibility(p, false);
+        }
 
-    pub(crate) fn expr(p: &mut Parser) {
-        let _ = expressions::expr(p);
-    }
+        pub(crate) fn block(p: &mut Parser) {
+            expressions::block_expr(p);
+        }
 
-    pub(crate) fn stmt(p: &mut Parser) {
-        expressions::stmt(p, expressions::StmtWithSemi::No, true);
-    }
+        pub(crate) fn stmt(p: &mut Parser) {
+            expressions::stmt(p, expressions::StmtWithSemi::No, true);
+        }
 
-    pub(crate) fn stmt_optional_semi(p: &mut Parser) {
-        expressions::stmt(p, expressions::StmtWithSemi::Optional, false);
-    }
+        pub(crate) fn pat(p: &mut Parser) {
+            patterns::pattern_single(p);
+        }
 
-    pub(crate) fn visibility(p: &mut Parser) {
-        let _ = opt_visibility(p, false);
+        pub(crate) fn ty(p: &mut Parser) {
+            types::type_(p);
+        }
+        pub(crate) fn expr(p: &mut Parser) {
+            let _ = expressions::expr(p);
+        }
+        pub(crate) fn path(p: &mut Parser) {
+            let _ = paths::type_path(p);
+        }
+        pub(crate) fn item(p: &mut Parser) {
+            items::item_or_macro(p, true);
+        }
+        // Parse a meta item , which excluded [], e.g : #[ MetaItem ]
+        pub(crate) fn meta_item(p: &mut Parser) {
+            attributes::meta(p);
+        }
     }
 
-    // Parse a meta item , which excluded [], e.g : #[ MetaItem ]
-    pub(crate) fn meta_item(p: &mut Parser) {
-        attributes::meta(p);
-    }
+    pub(crate) mod top {
+        use super::*;
 
-    pub(crate) fn item(p: &mut Parser) {
-        items::item_or_macro(p, true);
-    }
+        pub(crate) fn source_file(p: &mut Parser) {
+            let m = p.start();
+            p.eat(SHEBANG);
+            items::mod_contents(p, false);
+            m.complete(p, SOURCE_FILE);
+        }
 
-    pub(crate) fn macro_items(p: &mut Parser) {
-        let m = p.start();
-        items::mod_contents(p, false);
-        m.complete(p, MACRO_ITEMS);
-    }
+        pub(crate) fn macro_stmts(p: &mut Parser) {
+            let m = p.start();
 
-    pub(crate) fn macro_stmts(p: &mut Parser) {
-        let m = p.start();
+            while !p.at(EOF) {
+                if p.at(T![;]) {
+                    p.bump(T![;]);
+                    continue;
+                }
 
-        while !p.at(EOF) {
-            if p.at(T![;]) {
-                p.bump(T![;]);
-                continue;
+                expressions::stmt(p, expressions::StmtWithSemi::Optional, true);
             }
 
-            expressions::stmt(p, expressions::StmtWithSemi::Optional, true);
+            m.complete(p, MACRO_STMTS);
         }
 
-        m.complete(p, MACRO_STMTS);
-    }
-
-    pub(crate) fn attr(p: &mut Parser) {
-        attributes::outer_attrs(p);
+        pub(crate) fn macro_items(p: &mut Parser) {
+            let m = p.start();
+            items::mod_contents(p, false);
+            m.complete(p, MACRO_ITEMS);
+        }
     }
 }
 
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 841d2aa4e94..c5014be6c33 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -41,63 +41,95 @@ pub use crate::{
     syntax_kind::SyntaxKind,
 };
 
-/// rust-analyzer parser allows you to choose one of the possible entry points.
+/// Parse a prefix of the input as a given syntactic construct.
 ///
-/// The primary consumer of this API are declarative macros, `$x:expr` matchers
-/// are implemented by calling into the parser with non-standard entry point.
-#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
-pub enum ParserEntryPoint {
-    SourceFile,
-    Path,
+/// This is used by macro-by-example parser to implement things like `$i:item`
+/// and the naming of variants follows the naming of macro fragments.
+///
+/// Note that this is generally non-optional -- the result is intentionally not
+/// `Option<Output>`. The way MBE work, by the time we *try* to parse `$e:expr`
+/// we already commit to expression. In other words, this API by design can't be
+/// used to implement "rollback and try another alternative" logic.
+#[derive(Debug)]
+pub enum PrefixEntryPoint {
+    Vis,
+    Block,
+    Stmt,
+    Pat,
+    Ty,
     Expr,
-    Statement,
-    StatementOptionalSemi,
-    Type,
-    Pattern,
+    Path,
     Item,
-    Block,
-    Visibility,
     MetaItem,
-    Items,
-    Statements,
-    Attr,
 }
 
-/// Parse given tokens into the given sink as a rust file.
-pub fn parse_source_file(inp: &Input) -> Output {
-    parse(inp, ParserEntryPoint::SourceFile)
+impl PrefixEntryPoint {
+    pub fn parse(&self, input: &Input) -> Output {
+        let entry_point: fn(&'_ mut parser::Parser) = match self {
+            PrefixEntryPoint::Vis => grammar::entry::prefix::vis,
+            PrefixEntryPoint::Block => grammar::entry::prefix::block,
+            PrefixEntryPoint::Stmt => grammar::entry::prefix::stmt,
+            PrefixEntryPoint::Pat => grammar::entry::prefix::pat,
+            PrefixEntryPoint::Ty => grammar::entry::prefix::ty,
+            PrefixEntryPoint::Expr => grammar::entry::prefix::expr,
+            PrefixEntryPoint::Path => grammar::entry::prefix::path,
+            PrefixEntryPoint::Item => grammar::entry::prefix::item,
+            PrefixEntryPoint::MetaItem => grammar::entry::prefix::meta_item,
+        };
+        let mut p = parser::Parser::new(input);
+        entry_point(&mut p);
+        let events = p.finish();
+        event::process(events)
+    }
 }
 
-/// Parses the given [`Input`] into [`Output`] assuming that the top-level
-/// syntactic construct is the given [`ParserEntryPoint`].
+/// Parse the whole of the input as a given syntactic construct.
+///
+/// This covers two main use-cases:
+///
+///   * Parsing a Rust file.
+///   * Parsing a result of macro expansion.
 ///
-/// Both input and output here are fairly abstract. The overall flow is that the
-/// caller has some "real" tokens, converts them to [`Input`], parses them to
-/// [`Output`], and then converts that into a "real" tree. The "real" tree is
-/// made of "real" tokens, so this all hinges on rather tight coordination of
-/// indices between the four stages.
-pub fn parse(inp: &Input, entry_point: ParserEntryPoint) -> Output {
-    let entry_point: fn(&'_ mut parser::Parser) = match entry_point {
-        ParserEntryPoint::SourceFile => grammar::entry_points::source_file,
-        ParserEntryPoint::Path => grammar::entry_points::path,
-        ParserEntryPoint::Expr => grammar::entry_points::expr,
-        ParserEntryPoint::Type => grammar::entry_points::type_,
-        ParserEntryPoint::Pattern => grammar::entry_points::pattern,
-        ParserEntryPoint::Item => grammar::entry_points::item,
-        ParserEntryPoint::Block => grammar::entry_points::block_expr,
-        ParserEntryPoint::Visibility => grammar::entry_points::visibility,
-        ParserEntryPoint::MetaItem => grammar::entry_points::meta_item,
-        ParserEntryPoint::Statement => grammar::entry_points::stmt,
-        ParserEntryPoint::StatementOptionalSemi => grammar::entry_points::stmt_optional_semi,
-        ParserEntryPoint::Items => grammar::entry_points::macro_items,
-        ParserEntryPoint::Statements => grammar::entry_points::macro_stmts,
-        ParserEntryPoint::Attr => grammar::entry_points::attr,
-    };
+/// That is, for something like
+///
+/// ```
+/// quick_check! {
+///    fn prop() {}
+/// }
+/// ```
+///
+/// the input to the macro will be parsed with [`PrefixEntryPoint::Item`], and
+/// the result will be [`TopEntryPoint::Items`].
+///
+/// This *should* (but currently doesn't) guarantee that all input is consumed.
+#[derive(Debug)]
+pub enum TopEntryPoint {
+    SourceFile,
+    MacroStmts,
+    MacroItems,
+    Pattern,
+    Type,
+    Expr,
+    MetaItem,
+}
 
-    let mut p = parser::Parser::new(inp);
-    entry_point(&mut p);
-    let events = p.finish();
-    event::process(events)
+impl TopEntryPoint {
+    pub fn parse(&self, input: &Input) -> Output {
+        let entry_point: fn(&'_ mut parser::Parser) = match self {
+            TopEntryPoint::SourceFile => grammar::entry::top::source_file,
+            TopEntryPoint::MacroStmts => grammar::entry::top::macro_stmts,
+            TopEntryPoint::MacroItems => grammar::entry::top::macro_items,
+            // FIXME
+            TopEntryPoint::Pattern => grammar::entry::prefix::pat,
+            TopEntryPoint::Type => grammar::entry::prefix::ty,
+            TopEntryPoint::Expr => grammar::entry::prefix::expr,
+            TopEntryPoint::MetaItem => grammar::entry::prefix::meta_item,
+        };
+        let mut p = parser::Parser::new(input);
+        entry_point(&mut p);
+        let events = p.finish();
+        event::process(events)
+    }
 }
 
 /// A parsing function for a specific braced-block.
diff --git a/crates/parser/src/shortcuts.rs b/crates/parser/src/shortcuts.rs
index 15387a85cc1..3d28f814c9f 100644
--- a/crates/parser/src/shortcuts.rs
+++ b/crates/parser/src/shortcuts.rs
@@ -52,14 +52,10 @@ impl<'a> LexedStr<'a> {
     pub fn intersperse_trivia(
         &self,
         output: &crate::Output,
-        synthetic_root: bool,
         sink: &mut dyn FnMut(StrStep),
     ) -> bool {
         let mut builder = Builder { lexed: self, pos: 0, state: State::PendingEnter, sink };
 
-        if synthetic_root {
-            builder.enter(SyntaxKind::SOURCE_FILE);
-        }
         for event in output.iter() {
             match event {
                 Step::Token { kind, n_input_tokens: n_raw_tokens } => {
@@ -73,9 +69,6 @@ impl<'a> LexedStr<'a> {
                 }
             }
         }
-        if synthetic_root {
-            builder.exit();
-        }
 
         match mem::replace(&mut builder.state, State::Normal) {
             State::PendingExit => {
diff --git a/crates/parser/src/tests.rs b/crates/parser/src/tests.rs
index 7a6230eaf8b..512f7ddb95b 100644
--- a/crates/parser/src/tests.rs
+++ b/crates/parser/src/tests.rs
@@ -80,12 +80,12 @@ fn parse_inline_err() {
 fn parse(text: &str) -> (String, bool) {
     let lexed = LexedStr::new(text);
     let input = lexed.to_input();
-    let output = crate::parse_source_file(&input);
+    let output = crate::TopEntryPoint::SourceFile.parse(&input);
 
     let mut buf = String::new();
     let mut errors = Vec::new();
     let mut indent = String::new();
-    lexed.intersperse_trivia(&output, false, &mut |step| match step {
+    lexed.intersperse_trivia(&output, &mut |step| match step {
         crate::StrStep::Token { kind, text } => {
             write!(buf, "{}", indent).unwrap();
             write!(buf, "{:?} {:?}\n", kind, text).unwrap();
diff --git a/crates/syntax/src/hacks.rs b/crates/syntax/src/hacks.rs
new file mode 100644
index 00000000000..a047f61fa03
--- /dev/null
+++ b/crates/syntax/src/hacks.rs
@@ -0,0 +1,15 @@
+//! Things which exist to solve practial issues, but which shouldn't exist.
+//!
+//! Please avoid adding new usages of the functions in this module
+
+use crate::{ast, AstNode};
+
+pub fn parse_expr_from_str(s: &str) -> Option<ast::Expr> {
+    let s = s.trim();
+    let file = ast::SourceFile::parse(&format!("const _: () = {};", s));
+    let expr = file.syntax_node().descendants().find_map(ast::Expr::cast)?;
+    if expr.syntax().text() != s {
+        return None;
+    }
+    Some(expr)
+}
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index 65a6b7ac4e4..d6b1cce45ff 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -40,6 +40,7 @@ pub mod ast;
 pub mod fuzz;
 pub mod utils;
 pub mod ted;
+pub mod hacks;
 
 use std::{marker::PhantomData, sync::Arc};
 
@@ -167,61 +168,6 @@ impl SourceFile {
     }
 }
 
-// FIXME: `parse` functions shouldn't hang directly from AST nodes, and they
-// shouldn't return `Result`.
-//
-// We need a dedicated module for parser entry points, and they should always
-// return `Parse`.
-
-impl ast::Path {
-    /// Returns `text`, parsed as a path, but only if it has no errors.
-    pub fn parse(text: &str) -> Result<Self, ()> {
-        parsing::parse_text_as(text, parser::ParserEntryPoint::Path)
-    }
-}
-
-impl ast::Pat {
-    /// Returns `text`, parsed as a pattern, but only if it has no errors.
-    pub fn parse(text: &str) -> Result<Self, ()> {
-        parsing::parse_text_as(text, parser::ParserEntryPoint::Pattern)
-    }
-}
-
-impl ast::Expr {
-    /// Returns `text`, parsed as an expression, but only if it has no errors.
-    pub fn parse(text: &str) -> Result<Self, ()> {
-        parsing::parse_text_as(text, parser::ParserEntryPoint::Expr)
-    }
-}
-
-impl ast::Item {
-    /// Returns `text`, parsed as an item, but only if it has no errors.
-    pub fn parse(text: &str) -> Result<Self, ()> {
-        parsing::parse_text_as(text, parser::ParserEntryPoint::Item)
-    }
-}
-
-impl ast::Type {
-    /// Returns `text`, parsed as an type reference, but only if it has no errors.
-    pub fn parse(text: &str) -> Result<Self, ()> {
-        parsing::parse_text_as(text, parser::ParserEntryPoint::Type)
-    }
-}
-
-impl ast::Attr {
-    /// Returns `text`, parsed as an attribute, but only if it has no errors.
-    pub fn parse(text: &str) -> Result<Self, ()> {
-        parsing::parse_text_as(text, parser::ParserEntryPoint::Attr)
-    }
-}
-
-impl ast::Stmt {
-    /// Returns `text`, parsed as statement, but only if it has no errors.
-    pub fn parse(text: &str) -> Result<Self, ()> {
-        parsing::parse_text_as(text, parser::ParserEntryPoint::StatementOptionalSemi)
-    }
-}
-
 /// Matches a `SyntaxNode` against an `ast` type.
 ///
 /// # Example:
diff --git a/crates/syntax/src/parsing.rs b/crates/syntax/src/parsing.rs
index 971fa2700d2..047e670c9f4 100644
--- a/crates/syntax/src/parsing.rs
+++ b/crates/syntax/src/parsing.rs
@@ -5,46 +5,25 @@ mod reparsing;
 
 use rowan::TextRange;
 
-use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode, SyntaxTreeBuilder};
+use crate::{syntax_node::GreenNode, SyntaxError, SyntaxTreeBuilder};
 
 pub(crate) use crate::parsing::reparsing::incremental_reparse;
 
 pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
     let lexed = parser::LexedStr::new(text);
     let parser_input = lexed.to_input();
-    let parser_output = parser::parse_source_file(&parser_input);
-    let (node, errors, _eof) = build_tree(lexed, parser_output, false);
+    let parser_output = parser::TopEntryPoint::SourceFile.parse(&parser_input);
+    let (node, errors, _eof) = build_tree(lexed, parser_output);
     (node, errors)
 }
 
-/// Returns `text` parsed as a `T` provided there are no parse errors.
-pub(crate) fn parse_text_as<T: AstNode>(
-    text: &str,
-    entry_point: parser::ParserEntryPoint,
-) -> Result<T, ()> {
-    let lexed = parser::LexedStr::new(text);
-    if lexed.errors().next().is_some() {
-        return Err(());
-    }
-    let parser_input = lexed.to_input();
-    let parser_output = parser::parse(&parser_input, entry_point);
-    let (node, errors, eof) = build_tree(lexed, parser_output, true);
-
-    if !errors.is_empty() || !eof {
-        return Err(());
-    }
-
-    SyntaxNode::new_root(node).first_child().and_then(T::cast).ok_or(())
-}
-
 pub(crate) fn build_tree(
     lexed: parser::LexedStr<'_>,
     parser_output: parser::Output,
-    synthetic_root: bool,
 ) -> (GreenNode, Vec<SyntaxError>, bool) {
     let mut builder = SyntaxTreeBuilder::default();
 
-    let is_eof = lexed.intersperse_trivia(&parser_output, synthetic_root, &mut |step| match step {
+    let is_eof = lexed.intersperse_trivia(&parser_output, &mut |step| match step {
         parser::StrStep::Token { kind, text } => builder.token(kind, text),
         parser::StrStep::Enter { kind } => builder.start_node(kind),
         parser::StrStep::Exit => builder.finish_node(),
diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs
index dd2e01bfc4c..701e6232d58 100644
--- a/crates/syntax/src/parsing/reparsing.rs
+++ b/crates/syntax/src/parsing/reparsing.rs
@@ -96,7 +96,7 @@ fn reparse_block(
 
     let tree_traversal = reparser.parse(&parser_input);
 
-    let (green, new_parser_errors, _eof) = build_tree(lexed, tree_traversal, false);
+    let (green, new_parser_errors, _eof) = build_tree(lexed, tree_traversal);
 
     Some((node.replace_with(green), new_parser_errors, node.text_range()))
 }
diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs
index 04105dedc9e..0611143e2af 100644
--- a/crates/syntax/src/tests.rs
+++ b/crates/syntax/src/tests.rs
@@ -60,60 +60,6 @@ fn validation_tests() {
 }
 
 #[test]
-fn expr_parser_tests() {
-    fragment_parser_dir_test(
-        &["parser/fragments/expr/ok"],
-        &["parser/fragments/expr/err"],
-        crate::ast::Expr::parse,
-    );
-}
-
-#[test]
-fn path_parser_tests() {
-    fragment_parser_dir_test(
-        &["parser/fragments/path/ok"],
-        &["parser/fragments/path/err"],
-        crate::ast::Path::parse,
-    );
-}
-
-#[test]
-fn pattern_parser_tests() {
-    fragment_parser_dir_test(
-        &["parser/fragments/pattern/ok"],
-        &["parser/fragments/pattern/err"],
-        crate::ast::Pat::parse,
-    );
-}
-
-#[test]
-fn item_parser_tests() {
-    fragment_parser_dir_test(
-        &["parser/fragments/item/ok"],
-        &["parser/fragments/item/err"],
-        crate::ast::Item::parse,
-    );
-}
-
-#[test]
-fn type_parser_tests() {
-    fragment_parser_dir_test(
-        &["parser/fragments/type/ok"],
-        &["parser/fragments/type/err"],
-        crate::ast::Type::parse,
-    );
-}
-
-#[test]
-fn stmt_parser_tests() {
-    fragment_parser_dir_test(
-        &["parser/fragments/stmt/ok"],
-        &["parser/fragments/stmt/err"],
-        crate::ast::Stmt::parse,
-    );
-}
-
-#[test]
 fn parser_fuzz_tests() {
     for (_, text) in collect_rust_files(&test_data_dir(), &["parser/fuzz-failures"]) {
         fuzz::check_parser(&text)
@@ -172,24 +118,6 @@ fn assert_errors_are_present(errors: &[SyntaxError], path: &Path) {
     assert!(!errors.is_empty(), "There should be errors in the file {:?}", path.display());
 }
 
-fn fragment_parser_dir_test<T, F>(ok_paths: &[&str], err_paths: &[&str], f: F)
-where
-    T: crate::AstNode,
-    F: Fn(&str) -> Result<T, ()>,
-{
-    dir_tests(&test_data_dir(), ok_paths, "rast", |text, path| match f(text) {
-        Ok(node) => format!("{:#?}", crate::ast::AstNode::syntax(&node)),
-        Err(_) => panic!("Failed to parse '{:?}'", path),
-    });
-    dir_tests(&test_data_dir(), err_paths, "rast", |text, path| {
-        if f(text).is_ok() {
-            panic!("'{:?}' successfully parsed when it should have errored", path);
-        } else {
-            "ERROR\n".to_owned()
-        }
-    });
-}
-
 /// Calls callback `f` with input code and file paths for each `.rs` file in `test_data_dir`
 /// subdirectories defined by `paths`.
 ///
diff --git a/crates/syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rast b/crates/syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rast
deleted file mode 100644
index 5df7507e2de..00000000000
--- a/crates/syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rast
+++ /dev/null
@@ -1 +0,0 @@
-ERROR
diff --git a/crates/syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rs b/crates/syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rs
deleted file mode 100644
index ca49acb079e..00000000000
--- a/crates/syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rs
+++ /dev/null
@@ -1 +0,0 @@
-1 +
diff --git a/crates/syntax/test_data/parser/fragments/expr/ok/0000_add.rast b/crates/syntax/test_data/parser/fragments/expr/ok/0000_add.rast
deleted file mode 100644
index fa78a02a6bf..00000000000
--- a/crates/syntax/test_data/parser/fragments/expr/ok/0000_add.rast
+++ /dev/null
@@ -1,8 +0,0 @@
-BIN_EXPR@0..5
-  LITERAL@0..1
-    INT_NUMBER@0..1 "1"
-  WHITESPACE@1..2 " "
-  PLUS@2..3 "+"
-  WHITESPACE@3..4 " "
-  LITERAL@4..5
-    INT_NUMBER@4..5 "2"
diff --git a/crates/syntax/test_data/parser/fragments/expr/ok/0000_add.rs b/crates/syntax/test_data/parser/fragments/expr/ok/0000_add.rs
deleted file mode 100644
index e0ef5840209..00000000000
--- a/crates/syntax/test_data/parser/fragments/expr/ok/0000_add.rs
+++ /dev/null
@@ -1 +0,0 @@
-1 + 2
diff --git a/crates/syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rast b/crates/syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rast
deleted file mode 100644
index 5df7507e2de..00000000000
--- a/crates/syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rast
+++ /dev/null
@@ -1 +0,0 @@
-ERROR
diff --git a/crates/syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rs b/crates/syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rs
deleted file mode 100644
index dc32389bbb7..00000000000
--- a/crates/syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rs
+++ /dev/null
@@ -1 +0,0 @@
-fn fn foo() {}
diff --git a/crates/syntax/test_data/parser/fragments/item/ok/0000_fn.rast b/crates/syntax/test_data/parser/fragments/item/ok/0000_fn.rast
deleted file mode 100644
index 4ff9967beac..00000000000
--- a/crates/syntax/test_data/parser/fragments/item/ok/0000_fn.rast
+++ /dev/null
@@ -1,13 +0,0 @@
-FN@0..11
-  FN_KW@0..2 "fn"
-  WHITESPACE@2..3 " "
-  NAME@3..6
-    IDENT@3..6 "foo"
-  PARAM_LIST@6..8
-    L_PAREN@6..7 "("
-    R_PAREN@7..8 ")"
-  WHITESPACE@8..9 " "
-  BLOCK_EXPR@9..11
-    STMT_LIST@9..11
-      L_CURLY@9..10 "{"
-      R_CURLY@10..11 "}"
diff --git a/crates/syntax/test_data/parser/fragments/item/ok/0000_fn.rs b/crates/syntax/test_data/parser/fragments/item/ok/0000_fn.rs
deleted file mode 100644
index 8f3b7ef112a..00000000000
--- a/crates/syntax/test_data/parser/fragments/item/ok/0000_fn.rs
+++ /dev/null
@@ -1 +0,0 @@
-fn foo() {}
diff --git a/crates/syntax/test_data/parser/fragments/path/err/0000_reserved_word.rast b/crates/syntax/test_data/parser/fragments/path/err/0000_reserved_word.rast
deleted file mode 100644
index 5df7507e2de..00000000000
--- a/crates/syntax/test_data/parser/fragments/path/err/0000_reserved_word.rast
+++ /dev/null
@@ -1 +0,0 @@
-ERROR
diff --git a/crates/syntax/test_data/parser/fragments/path/err/0000_reserved_word.rs b/crates/syntax/test_data/parser/fragments/path/err/0000_reserved_word.rs
deleted file mode 100644
index 2046de04929..00000000000
--- a/crates/syntax/test_data/parser/fragments/path/err/0000_reserved_word.rs
+++ /dev/null
@@ -1 +0,0 @@
-struct
diff --git a/crates/syntax/test_data/parser/fragments/path/err/0001_expression.rast b/crates/syntax/test_data/parser/fragments/path/err/0001_expression.rast
deleted file mode 100644
index 5df7507e2de..00000000000
--- a/crates/syntax/test_data/parser/fragments/path/err/0001_expression.rast
+++ /dev/null
@@ -1 +0,0 @@
-ERROR
diff --git a/crates/syntax/test_data/parser/fragments/path/err/0001_expression.rs b/crates/syntax/test_data/parser/fragments/path/err/0001_expression.rs
deleted file mode 100644
index 745e8d376f7..00000000000
--- a/crates/syntax/test_data/parser/fragments/path/err/0001_expression.rs
+++ /dev/null
@@ -1 +0,0 @@
-a + b
diff --git a/crates/syntax/test_data/parser/fragments/path/ok/0000_single_ident.rast b/crates/syntax/test_data/parser/fragments/path/ok/0000_single_ident.rast
deleted file mode 100644
index 0c5d4360fa8..00000000000
--- a/crates/syntax/test_data/parser/fragments/path/ok/0000_single_ident.rast
+++ /dev/null
@@ -1,4 +0,0 @@
-PATH@0..3
-  PATH_SEGMENT@0..3
-    NAME_REF@0..3
-      IDENT@0..3 "foo"
diff --git a/crates/syntax/test_data/parser/fragments/path/ok/0000_single_ident.rs b/crates/syntax/test_data/parser/fragments/path/ok/0000_single_ident.rs
deleted file mode 100644
index 257cc5642cb..00000000000
--- a/crates/syntax/test_data/parser/fragments/path/ok/0000_single_ident.rs
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/crates/syntax/test_data/parser/fragments/path/ok/0001_multipart.rast b/crates/syntax/test_data/parser/fragments/path/ok/0001_multipart.rast
deleted file mode 100644
index 4a2b45e6a96..00000000000
--- a/crates/syntax/test_data/parser/fragments/path/ok/0001_multipart.rast
+++ /dev/null
@@ -1,14 +0,0 @@
-PATH@0..13
-  PATH@0..8
-    PATH@0..3
-      PATH_SEGMENT@0..3
-        NAME_REF@0..3
-          IDENT@0..3 "foo"
-    COLON2@3..5 "::"
-    PATH_SEGMENT@5..8
-      NAME_REF@5..8
-        IDENT@5..8 "bar"
-  COLON2@8..10 "::"
-  PATH_SEGMENT@10..13
-    NAME_REF@10..13
-      IDENT@10..13 "baz"
diff --git a/crates/syntax/test_data/parser/fragments/path/ok/0001_multipart.rs b/crates/syntax/test_data/parser/fragments/path/ok/0001_multipart.rs
deleted file mode 100644
index 81e0b21cd41..00000000000
--- a/crates/syntax/test_data/parser/fragments/path/ok/0001_multipart.rs
+++ /dev/null
@@ -1 +0,0 @@
-foo::bar::baz
diff --git a/crates/syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rast b/crates/syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rast
deleted file mode 100644
index 5df7507e2de..00000000000
--- a/crates/syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rast
+++ /dev/null
@@ -1 +0,0 @@
-ERROR
diff --git a/crates/syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rs b/crates/syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rs
deleted file mode 100644
index ae26fc45566..00000000000
--- a/crates/syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rs
+++ /dev/null
@@ -1 +0,0 @@
-fn
diff --git a/crates/syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rast b/crates/syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rast
deleted file mode 100644
index 5df7507e2de..00000000000
--- a/crates/syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rast
+++ /dev/null
@@ -1 +0,0 @@
-ERROR
diff --git a/crates/syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rs b/crates/syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rs
deleted file mode 100644
index 61a391d0846..00000000000
--- a/crates/syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rs
+++ /dev/null
@@ -1 +0,0 @@
-Some(x
diff --git a/crates/syntax/test_data/parser/fragments/pattern/ok/0000_enum.rast b/crates/syntax/test_data/parser/fragments/pattern/ok/0000_enum.rast
deleted file mode 100644
index dcf102339aa..00000000000
--- a/crates/syntax/test_data/parser/fragments/pattern/ok/0000_enum.rast
+++ /dev/null
@@ -1,10 +0,0 @@
-TUPLE_STRUCT_PAT@0..7
-  PATH@0..4
-    PATH_SEGMENT@0..4
-      NAME_REF@0..4
-        IDENT@0..4 "Some"
-  L_PAREN@4..5 "("
-  IDENT_PAT@5..6
-    NAME@5..6
-      IDENT@5..6 "x"
-  R_PAREN@6..7 ")"
diff --git a/crates/syntax/test_data/parser/fragments/pattern/ok/0000_enum.rs b/crates/syntax/test_data/parser/fragments/pattern/ok/0000_enum.rs
deleted file mode 100644
index 87114dd7881..00000000000
--- a/crates/syntax/test_data/parser/fragments/pattern/ok/0000_enum.rs
+++ /dev/null
@@ -1 +0,0 @@
-Some(x)
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast
deleted file mode 100644
index 5df7507e2de..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast
+++ /dev/null
@@ -1 +0,0 @@
-ERROR
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs
deleted file mode 100644
index 988df070597..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs
+++ /dev/null
@@ -1 +0,0 @@
-#[foo]
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast
deleted file mode 100644
index 5df7507e2de..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast
+++ /dev/null
@@ -1 +0,0 @@
-ERROR
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs
deleted file mode 100644
index 7e3b2fd4936..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs
+++ /dev/null
@@ -1 +0,0 @@
-a(); b(); c()
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast
deleted file mode 100644
index 5df7507e2de..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast
+++ /dev/null
@@ -1 +0,0 @@
-ERROR
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs
deleted file mode 100644
index 2d06f376636..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs
+++ /dev/null
@@ -1 +0,0 @@
-(
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast
deleted file mode 100644
index 5df7507e2de..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast
+++ /dev/null
@@ -1 +0,0 @@
-ERROR
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs
deleted file mode 100644
index 092bc2b0412..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs
+++ /dev/null
@@ -1 +0,0 @@
-;
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast
deleted file mode 100644
index 5df7507e2de..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast
+++ /dev/null
@@ -1 +0,0 @@
-ERROR
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs
deleted file mode 100644
index ca49acb079e..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs
+++ /dev/null
@@ -1 +0,0 @@
-1 +
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast
deleted file mode 100644
index 274fdf16deb..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast
+++ /dev/null
@@ -1,9 +0,0 @@
-EXPR_STMT@0..5
-  BIN_EXPR@0..5
-    LITERAL@0..1
-      INT_NUMBER@0..1 "1"
-    WHITESPACE@1..2 " "
-    PLUS@2..3 "+"
-    WHITESPACE@3..4 " "
-    LITERAL@4..5
-      INT_NUMBER@4..5 "1"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs
deleted file mode 100644
index 8d2f0971e2c..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs
+++ /dev/null
@@ -1 +0,0 @@
-1 + 1
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast
deleted file mode 100644
index a2d4f18988a..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast
+++ /dev/null
@@ -1,70 +0,0 @@
-EXPR_STMT@0..55
-  BLOCK_EXPR@0..55
-    STMT_LIST@0..55
-      L_CURLY@0..1 "{"
-      WHITESPACE@1..6 "\n    "
-      LET_STMT@6..20
-        LET_KW@6..9 "let"
-        WHITESPACE@9..10 " "
-        IDENT_PAT@10..11
-          NAME@10..11
-            IDENT@10..11 "x"
-        WHITESPACE@11..12 " "
-        EQ@12..13 "="
-        WHITESPACE@13..14 " "
-        CALL_EXPR@14..19
-          PATH_EXPR@14..17
-            PATH@14..17
-              PATH_SEGMENT@14..17
-                NAME_REF@14..17
-                  IDENT@14..17 "foo"
-          ARG_LIST@17..19
-            L_PAREN@17..18 "("
-            R_PAREN@18..19 ")"
-        SEMICOLON@19..20 ";"
-      WHITESPACE@20..25 "\n    "
-      LET_STMT@25..39
-        LET_KW@25..28 "let"
-        WHITESPACE@28..29 " "
-        IDENT_PAT@29..30
-          NAME@29..30
-            IDENT@29..30 "y"
-        WHITESPACE@30..31 " "
-        EQ@31..32 "="
-        WHITESPACE@32..33 " "
-        CALL_EXPR@33..38
-          PATH_EXPR@33..36
-            PATH@33..36
-              PATH_SEGMENT@33..36
-                NAME_REF@33..36
-                  IDENT@33..36 "bar"
-          ARG_LIST@36..38
-            L_PAREN@36..37 "("
-            R_PAREN@37..38 ")"
-        SEMICOLON@38..39 ";"
-      WHITESPACE@39..44 "\n    "
-      CALL_EXPR@44..53
-        PATH_EXPR@44..46
-          PATH@44..46
-            PATH_SEGMENT@44..46
-              NAME_REF@44..46
-                IDENT@44..46 "Ok"
-        ARG_LIST@46..53
-          L_PAREN@46..47 "("
-          BIN_EXPR@47..52
-            PATH_EXPR@47..48
-              PATH@47..48
-                PATH_SEGMENT@47..48
-                  NAME_REF@47..48
-                    IDENT@47..48 "x"
-            WHITESPACE@48..49 " "
-            PLUS@49..50 "+"
-            WHITESPACE@50..51 " "
-            PATH_EXPR@51..52
-              PATH@51..52
-                PATH_SEGMENT@51..52
-                  NAME_REF@51..52
-                    IDENT@51..52 "y"
-          R_PAREN@52..53 ")"
-      WHITESPACE@53..54 "\n"
-      R_CURLY@54..55 "}"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs
deleted file mode 100644
index ffa5c1e66ef..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-    let x = foo();
-    let y = bar();
-    Ok(x + y)
-}
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast
deleted file mode 100644
index 8c186da93ee..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast
+++ /dev/null
@@ -1,11 +0,0 @@
-EXPR_STMT@0..6
-  CALL_EXPR@0..5
-    PATH_EXPR@0..3
-      PATH@0..3
-        PATH_SEGMENT@0..3
-          NAME_REF@0..3
-            IDENT@0..3 "foo"
-    ARG_LIST@3..5
-      L_PAREN@3..4 "("
-      R_PAREN@4..5 ")"
-  SEMICOLON@5..6 ";"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs
deleted file mode 100644
index a280f9a5cce..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs
+++ /dev/null
@@ -1 +0,0 @@
-foo();
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast
deleted file mode 100644
index 8ab38da21cb..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast
+++ /dev/null
@@ -1,12 +0,0 @@
-LET_STMT@0..11
-  LET_KW@0..3 "let"
-  WHITESPACE@3..4 " "
-  IDENT_PAT@4..5
-    NAME@4..5
-      IDENT@4..5 "x"
-  WHITESPACE@5..6 " "
-  EQ@6..7 "="
-  WHITESPACE@7..8 " "
-  LITERAL@8..10
-    INT_NUMBER@8..10 "10"
-  SEMICOLON@10..11 ";"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs
deleted file mode 100644
index de8a7f1fc6c..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs
+++ /dev/null
@@ -1 +0,0 @@
-let x = 10;
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast
deleted file mode 100644
index 81d6df29a5f..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast
+++ /dev/null
@@ -1,21 +0,0 @@
-EXPR_STMT@0..18
-  MACRO_CALL@0..17
-    PATH@0..2
-      PATH_SEGMENT@0..2
-        NAME_REF@0..2
-          IDENT@0..2 "m1"
-    BANG@2..3 "!"
-    TOKEN_TREE@3..17
-      L_CURLY@3..4 "{"
-      WHITESPACE@4..5 " "
-      LET_KW@5..8 "let"
-      WHITESPACE@8..9 " "
-      IDENT@9..10 "a"
-      WHITESPACE@10..11 " "
-      EQ@11..12 "="
-      WHITESPACE@12..13 " "
-      INT_NUMBER@13..14 "0"
-      SEMICOLON@14..15 ";"
-      WHITESPACE@15..16 " "
-      R_CURLY@16..17 "}"
-  SEMICOLON@17..18 ";"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs
deleted file mode 100644
index 075f30159b2..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs
+++ /dev/null
@@ -1 +0,0 @@
-m1!{ let a = 0; };
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast
deleted file mode 100644
index 81d6df29a5f..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast
+++ /dev/null
@@ -1,21 +0,0 @@
-EXPR_STMT@0..18
-  MACRO_CALL@0..17
-    PATH@0..2
-      PATH_SEGMENT@0..2
-        NAME_REF@0..2
-          IDENT@0..2 "m1"
-    BANG@2..3 "!"
-    TOKEN_TREE@3..17
-      L_CURLY@3..4 "{"
-      WHITESPACE@4..5 " "
-      LET_KW@5..8 "let"
-      WHITESPACE@8..9 " "
-      IDENT@9..10 "a"
-      WHITESPACE@10..11 " "
-      EQ@11..12 "="
-      WHITESPACE@12..13 " "
-      INT_NUMBER@13..14 "0"
-      SEMICOLON@14..15 ";"
-      WHITESPACE@15..16 " "
-      R_CURLY@16..17 "}"
-  SEMICOLON@17..18 ";"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs
deleted file mode 100644
index 075f30159b2..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs
+++ /dev/null
@@ -1 +0,0 @@
-m1!{ let a = 0; };
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast
deleted file mode 100644
index 64c5d296911..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast
+++ /dev/null
@@ -1,22 +0,0 @@
-STRUCT@0..28
-  STRUCT_KW@0..6 "struct"
-  WHITESPACE@6..7 " "
-  NAME@7..10
-    IDENT@7..10 "Foo"
-  WHITESPACE@10..11 " "
-  RECORD_FIELD_LIST@11..28
-    L_CURLY@11..12 "{"
-    WHITESPACE@12..17 "\n    "
-    RECORD_FIELD@17..25
-      NAME@17..20
-        IDENT@17..20 "bar"
-      COLON@20..21 ":"
-      WHITESPACE@21..22 " "
-      PATH_TYPE@22..25
-        PATH@22..25
-          PATH_SEGMENT@22..25
-            NAME_REF@22..25
-              IDENT@22..25 "u32"
-    COMMA@25..26 ","
-    WHITESPACE@26..27 "\n"
-    R_CURLY@27..28 "}"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs
deleted file mode 100644
index e5473e3ac8d..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-struct Foo {
-    bar: u32,
-}
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast
deleted file mode 100644
index 9089906bcea..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast
+++ /dev/null
@@ -1,10 +0,0 @@
-EXPR_STMT@0..5
-  CALL_EXPR@0..5
-    PATH_EXPR@0..3
-      PATH@0..3
-        PATH_SEGMENT@0..3
-          NAME_REF@0..3
-            IDENT@0..3 "foo"
-    ARG_LIST@3..5
-      L_PAREN@3..4 "("
-      R_PAREN@4..5 ")"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs
deleted file mode 100644
index eb28ef4401b..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs
+++ /dev/null
@@ -1 +0,0 @@
-foo()
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast
deleted file mode 100644
index 37663671faf..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast
+++ /dev/null
@@ -1,11 +0,0 @@
-LET_STMT@0..10
-  LET_KW@0..3 "let"
-  WHITESPACE@3..4 " "
-  IDENT_PAT@4..5
-    NAME@4..5
-      IDENT@4..5 "x"
-  WHITESPACE@5..6 " "
-  EQ@6..7 "="
-  WHITESPACE@7..8 " "
-  LITERAL@8..10
-    INT_NUMBER@8..10 "10"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs
deleted file mode 100644
index 78364b2a96e..00000000000
--- a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs
+++ /dev/null
@@ -1 +0,0 @@
-let x = 10
diff --git a/crates/syntax/test_data/parser/fragments/type/err/0000_missing_close.rast b/crates/syntax/test_data/parser/fragments/type/err/0000_missing_close.rast
deleted file mode 100644
index 5df7507e2de..00000000000
--- a/crates/syntax/test_data/parser/fragments/type/err/0000_missing_close.rast
+++ /dev/null
@@ -1 +0,0 @@
-ERROR
diff --git a/crates/syntax/test_data/parser/fragments/type/err/0000_missing_close.rs b/crates/syntax/test_data/parser/fragments/type/err/0000_missing_close.rs
deleted file mode 100644
index caa4d7c0927..00000000000
--- a/crates/syntax/test_data/parser/fragments/type/err/0000_missing_close.rs
+++ /dev/null
@@ -1 +0,0 @@
-Result<Foo, Bar
diff --git a/crates/syntax/test_data/parser/fragments/type/ok/0000_result.rast b/crates/syntax/test_data/parser/fragments/type/ok/0000_result.rast
deleted file mode 100644
index 38c15b5815e..00000000000
--- a/crates/syntax/test_data/parser/fragments/type/ok/0000_result.rast
+++ /dev/null
@@ -1,22 +0,0 @@
-PATH_TYPE@0..16
-  PATH@0..16
-    PATH_SEGMENT@0..16
-      NAME_REF@0..6
-        IDENT@0..6 "Result"
-      GENERIC_ARG_LIST@6..16
-        L_ANGLE@6..7 "<"
-        TYPE_ARG@7..10
-          PATH_TYPE@7..10
-            PATH@7..10
-              PATH_SEGMENT@7..10
-                NAME_REF@7..10
-                  IDENT@7..10 "Foo"
-        COMMA@10..11 ","
-        WHITESPACE@11..12 " "
-        TYPE_ARG@12..15
-          PATH_TYPE@12..15
-            PATH@12..15
-              PATH_SEGMENT@12..15
-                NAME_REF@12..15
-                  IDENT@12..15 "Bar"
-        R_ANGLE@15..16 ">"
diff --git a/crates/syntax/test_data/parser/fragments/type/ok/0000_result.rs b/crates/syntax/test_data/parser/fragments/type/ok/0000_result.rs
deleted file mode 100644
index b50b3bb3bfd..00000000000
--- a/crates/syntax/test_data/parser/fragments/type/ok/0000_result.rs
+++ /dev/null
@@ -1 +0,0 @@
-Result<Foo, Bar>