summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/syntax/test_data/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/syntax/test_data/parser')
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs199
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0001.rs106
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0002.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0003.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0004.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast127
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs15
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast105
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rs6
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0038_endless_inclusive_range.rast30
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0038_endless_inclusive_range.rs4
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0040_illegal_crate_kw_location.rast96
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0040_illegal_crate_kw_location.rs4
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rast29
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rs2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0045_ambiguous_trait_object.rast196
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0045_ambiguous_trait_object.rs6
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0046_mutable_const_item.rast22
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0046_mutable_const_item.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/invalid_let_expr.rast216
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/invalid_let_expr.rs14
21 files changed, 1181 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs
new file mode 100644
index 000000000..f977d23c4
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs
@@ -0,0 +1,199 @@
+//! An experimental implementation of [Rust RFC#2256 lrs);
+ let root = SyntaxNode::new_owned(root);
+ validate_block_structure(root.borrowed());
+ File { root }
+ }
+ pub fn parse(text: &str) -> File {
+ let tokens = tokenize(&text);
+ let (green, errors) = parser_impl::parse_with::<syntax_node::GreenBuilder>(
+ text, &tokens, grammar::root,
+ );
+ File::new(green, errors)
+ }
+ pub fn reparse(&self, edit: &AtomTextEdit) -> File {
+ self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
+ }
+ pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<File> {
+ let (node, reparser) = find_reparsable_node(self.syntax(), edit.delete)?;
+ let text = replace_range(
+ node.text().to_string(),
+ edit.delete - node.range().start(),
+ &edit.insert,
+ );
+ let tokens = tokenize(&text);
+ if !is_balanced(&tokens) {
+ return None;
+ }
+ let (green, new_errors) = parser_impl::parse_with::<syntax_node::GreenBuilder>(
+ &te2t, &tokens, reparser,
+ );
+ let green_root = node.replace_with(green);
+ let errors = merge_errors(self.errors(), new_errors, node, edit);
+ Some(File::new(green_root, errors))
+ }
+ fn full_reparse(&self, edit: &AtomTextEdit) -> File {
+ let text = replace_range(self.syntax().text().to_string(), edit.delete, &edit.insert);
+ File::parse(&text)
+ }
+ pub fn ast(&self) -> ast::Root {
+ ast::Root::cast(self.syntax()).unwrap()
+ }
+ pub fn syntax(&self) -> SyntaxNodeRef {
+ self.root.brroowed()
+ }
+ mp_tree(root),
+ );
+ assert!(
+ node.next_sibling().is_none() && pair.prev_sibling().is_none(),
+ "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
+ node,
+ root.text(),
+ node.text(),
+ );
+ }
+ }
+ _ => (),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct AtomTextEdit {
+ pub delete: TextRange,
+ pub insert: String,
+}
+
+impl AtomTextEdit {
+ pub fn replace(range: TextRange, replace_with: String) -> AtomTextEdit {
+ AtomTextEdit { delete: range, insert: replace_with }
+ }
+
+ pub fn delete(range: TextRange) -> AtomTextEdit {
+ AtomTextEdit::replace(range, String::new())
+ }
+
+ pub fn insert(offset: TextUnit, text: String) -> AtomTextEdit {
+ AtomTextEdit::replace(TextRange::offset_len(offset, 0.into()), text)
+ }
+}
+
+fn find_reparsable_node(node: SyntaxNodeRef, range: TextRange) -> Option<(SyntaxNodeRef, fn(&mut Parser))> {
+ let node = algo::find_covering_node(node, range);
+ return algo::ancestors(node)
+ .filter_map(|node| reparser(node).map(|r| (node, r)))
+ .next();
+
+ fn reparser(node: SyntaxNodeRef) -> Option<fn(&mut Parser)> {
+ let res = match node.kind() {
+ BLOCK => grammar::block,
+ RECORD_FIELD_LIST => grammar::record_field_list,
+ _ => return None,
+ };
+ Some(res)
+ }
+}
+
+pub /*(meh)*/ fn replace_range(mut text: String, range: TextRange, replace_with: &str) -> String {
+ let start = u32::from(range.start()) as usize;
+ let end = u32::from(range.end()) as usize;
+ text.replace_range(start..end, replace_with);
+ text
+}
+
+fn is_balanced(tokens: &[Token]) -> bool {
+ if tokens.is_empty()
+ || tokens.first().unwrap().kind != L_CURLY
+ || tokens.last().unwrap().kind != R_CURLY {
+ return false
+ }
+ let mut balance = 0usize;
+ for t in tokens.iter() {
+ match t.kind {
+ L_CURLYt {
+ pub delete: TextRange,
+ pub insert: String,
+}
+
+impl AtomTextEdit {
+ pub fn replace(range: TextRange, replace_with: String) -> AtomTextEdit {
+ AtomTextEdit { delete: range, insert: replace_with }
+ }
+
+ pub fn delete(range: TextRange) -> AtomTextEdit {
+ AtomTextEdit::replace(range, String::new())
+ }
+
+ pub fn insert(offset: TextUnit, text: String) -> AtomTextEdit {
+ AtomTextEdit::replace(TextRange::offset_len(offset, 0.into()), text)
+ }
+}
+
+fn find_reparsable_node(node: SyntaxNodeRef, range: TextRange) -> Option<(SyntaxNodeRef, fn(&mut Parser))> {
+ let node = algo::find_covering_node(node, range);
+ return algo::ancestors(node)
+ .filter_map(|node| reparser(node).map(|r| (node, r)))
+ .next();
+
+ fn reparser(node: SyntaxNodeRef) -> Option<fn(&mut Parser)> {
+ let res = match node.kind() {
+ ;
+ let end = u32::from(range.end()) as usize;
+ text.replaT => grammar::record_field_list,
+ _ => return None,
+ };
+ Some(res)
+ }
+}
+
+pub /*(meh)*/ fn replace_range(mut text: String, range: TextRange, replace_with: &str) -> String {
+ let start = u32::from(range.start()) as usize;
+ let end = u32::from(range.end()) as usize;
+ text.replace_range(start..end, replace_with);
+ text
+}
+
+fn is_balanced(tokens: &[Token]) -> bool {
+ if tokens.is_empty()
+ || tokens.first().unwrap().kind != L_CURLY
+ || tokens.last().unwrap().kind != R_CURLY {
+ return false
+ }
+ let mut balance = 0usize;
+ for t in tokens.iter() {
+ match t.kind {
+ L_CURLY => balance += 1,
+ R_CURLY => balance = match balance.checked_sub(1) {
+ Some(b) => b,
+ None => return false,
+ },
+ _ => (),
+ }
+ }
+ balance == 0
+}
+
+fn merge_errors(
+ old_errors: Vec<SyntaxError>,
+ new_errors: Vec<SyntaxError>,
+ old_node: SyntaxNodeRef,
+ edit: &AtomTextEdit,
+) -> Vec<SyntaxError> {
+ let mut res = Vec::new();
+ for e in old_errors {
+ if e.offset < old_node.range().start() {
+ res.push(e)
+ } else if e.offset > old_node.range().end() {
+ res.push(SyntaxError {
+ msg: e.msg,
+ offset: e.offset + TextUnit::of_str(&edit.insert) - edit.delete.len(),
+ })
+ }
+ }
+ for e in new_errors {
+ res.push(SyntaxError {
+ msg: e.msg,
+ offset: e.offset + old_node.range().start(),
+ })
+ }
+ res
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0001.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0001.rs
new file mode 100644
index 000000000..f1148058e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0001.rs
@@ -0,0 +1,106 @@
+use syntax::{
+ File, TextRange, SyntaxNodeRef, TextUnit,
+ SyntaxKind::*,
+ algo::{find_leaf_at_offset, LeafAtOffset, find_covering_node, ancestors, Direction, siblings},
+};
+
+pub fn extend_selection(file: &File, range: TextRange) -> Option<TextRange> {
+ let syntax = file.syntax();
+ extend(syntax.borrowed(), range)
+}
+
+pub(crate) fn extend(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> {
+ if range.is_empty() {
+ let offset = range.start();
+ let mut leaves = find_leaf_at_offset(root, offset);
+ if leaves.clone().all(|it| it.kind() == WHITESPACE) {
+ return Some(extend_ws(root, leaves.next()?, offset));
+ }
+ let leaf = match leaves {
+ LeafAtOffset::None => return None,
+ LeafAtOffset::Single(l) => l,
+ LeafAtOffset::Between(l, r) => pick_best(l, r),
+ };
+ return Some(leaf.range());
+ };
+ let node = find_covering_node(root, range);
+ if node.kind() == COMMENT && range == node.range() {
+ if let Some(range) = extend_comments(node) {
+ return Some(range);
+ }
+ }
+
+ match ancestors(node).skip_while(|n| n.range() == range).next() {
+ None => None,
+ Some(parent) => Some(parent.range()),
+ }
+}
+
+fn extend_ws(root: SyntaxNodeRef, ws: SyntaxNodeRef, offset: TextUnit) -> TextRange {
+ let ws_text = ws.leaf_text().unwrap();
+ let suffix = TextRange::from_to(offset, ws.range().end()) - ws.range().start();
+ let prefix = TextRange::from_to(ws.range().start(), offset) - ws.range().start();
+ let ws_suffix = &ws_text.as_str()[suffix];
+ let ws_prefix = &ws_text.as_str()[prefix];
+ if ws_text.contains("\n") && !ws_suffix.contains("\n") {
+ if let Some(node) = ws.next_sibling() {
+ let start = match ws_prefix.rfind('\n') {
+ Some(idx) => ws.range().start() + TextUnit::from((idx + 1) as u32),
+ None => node.range().start()
+ };
+ let end = if root.text().char_at(node.range().end()) == Some('\n') {
+ node.range().end() + TextUnit::of_char('\n')
+ } else {
+ node.range().end()
+ };
+ return TextRange::from_to(start, end);
+ }
+ }
+ ws.range()
+}
+
+fn pick_best<'a>(l: SyntaxNodeRef<'a>, r: Syntd[axNodeRef<'a>) -> SyntaxNodeRef<'a> {
+ return if priority(r) > priority(l) { r } else { l };
+ fn priority(n: SyntaxNodeRef) -> usize {
+ match n.kind() {
+ WHITESPACE => 0,
+ IDENT | SELF_KW | SUPER_KW | CRATE_KW => 2,
+ _ => 1,
+ }
+ }
+}
+
+fn extend_comments(node: SyntaxNodeRef) -> Option<TextRange> {
+ let left = adj_com[ments(node, Direction::Backward);
+ let right = adj_comments(node, Direction::Forward);
+ if left != right {
+ Some(TextRange::from_to(
+ left.range().start(),
+ right.range().end(),
+ ))
+ } else {
+ None
+ }
+}
+
+fn adj_comments(node: SyntaxNodeRef, dir: Direction) -> SyntaxNodeRef {
+ let mut res = node;
+ for node in siblings(node, dir) {
+ match node.kind() {
+ COMMENT => res = node,
+ WHITESPACE if !node.leaf_text().unwrap().as_str().contains("\n\n") => (),
+ _ => break
+ }
+ }
+ res
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use test_utils::extract_offset;
+
+ fn do_check(before: &str, afters: &[&str]) {
+ let (cursor, before) = extract_offset(before);
+ let file = File::parse(&before);
+ let mut range = TextRange::of
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0002.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0002.rs
new file mode 100644
index 000000000..f35dc7289
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0002.rs
@@ -0,0 +1 @@
+!('\ \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0003.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0003.rs
new file mode 100644
index 000000000..0f59c4722
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0003.rs
@@ -0,0 +1 @@
+if'\xɿ \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0004.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0004.rs
new file mode 100644
index 000000000..003290f52
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0004.rs
@@ -0,0 +1 @@
+b"\xʿ \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
new file mode 100644
index 000000000..50057a02d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
@@ -0,0 +1,127 @@
+SOURCE_FILE@0..350
+ FN@0..349
+ FN_KW@0..2 "fn"
+ WHITESPACE@2..3 " "
+ NAME@3..8
+ IDENT@3..8 "block"
+ PARAM_LIST@8..10
+ L_PAREN@8..9 "("
+ R_PAREN@9..10 ")"
+ WHITESPACE@10..11 " "
+ BLOCK_EXPR@11..349
+ STMT_LIST@11..349
+ L_CURLY@11..12 "{"
+ WHITESPACE@12..17 "\n "
+ LET_STMT@17..129
+ LET_KW@17..20 "let"
+ WHITESPACE@20..21 " "
+ IDENT_PAT@21..26
+ NAME@21..26
+ IDENT@21..26 "inner"
+ WHITESPACE@26..27 " "
+ EQ@27..28 "="
+ WHITESPACE@28..29 " "
+ BLOCK_EXPR@29..128
+ STMT_LIST@29..128
+ L_CURLY@29..30 "{"
+ WHITESPACE@30..39 "\n "
+ ATTR@39..83
+ POUND@39..40 "#"
+ BANG@40..41 "!"
+ L_BRACK@41..42 "["
+ META@42..82
+ PATH@42..45
+ PATH_SEGMENT@42..45
+ NAME_REF@42..45
+ IDENT@42..45 "doc"
+ TOKEN_TREE@45..82
+ L_PAREN@45..46 "("
+ STRING@46..81 "\"Inner attributes not ..."
+ R_PAREN@81..82 ")"
+ R_BRACK@82..83 "]"
+ WHITESPACE@83..92 "\n "
+ COMMENT@92..122 "//! Nor are ModuleDoc ..."
+ WHITESPACE@122..127 "\n "
+ R_CURLY@127..128 "}"
+ SEMICOLON@128..129 ";"
+ WHITESPACE@129..134 "\n "
+ EXPR_STMT@134..257
+ IF_EXPR@134..257
+ IF_KW@134..136 "if"
+ WHITESPACE@136..137 " "
+ LITERAL@137..141
+ TRUE_KW@137..141 "true"
+ WHITESPACE@141..142 " "
+ BLOCK_EXPR@142..257
+ STMT_LIST@142..257
+ L_CURLY@142..143 "{"
+ WHITESPACE@143..152 "\n "
+ ATTR@152..171
+ POUND@152..153 "#"
+ BANG@153..154 "!"
+ L_BRACK@154..155 "["
+ META@155..170
+ PATH@155..158
+ PATH_SEGMENT@155..158
+ NAME_REF@155..158
+ IDENT@155..158 "doc"
+ TOKEN_TREE@158..170
+ L_PAREN@158..159 "("
+ STRING@159..169 "\"Nor here\""
+ R_PAREN@169..170 ")"
+ R_BRACK@170..171 "]"
+ WHITESPACE@171..180 "\n "
+ ATTR@180..212
+ POUND@180..181 "#"
+ BANG@181..182 "!"
+ L_BRACK@182..183 "["
+ META@183..211
+ PATH@183..186
+ PATH_SEGMENT@183..186
+ NAME_REF@183..186
+ IDENT@183..186 "doc"
+ TOKEN_TREE@186..211
+ L_PAREN@186..187 "("
+ STRING@187..210 "\"We error on each attr\""
+ R_PAREN@210..211 ")"
+ R_BRACK@211..212 "]"
+ WHITESPACE@212..221 "\n "
+ COMMENT@221..251 "//! Nor are ModuleDoc ..."
+ WHITESPACE@251..256 "\n "
+ R_CURLY@256..257 "}"
+ WHITESPACE@257..262 "\n "
+ WHILE_EXPR@262..347
+ WHILE_KW@262..267 "while"
+ WHITESPACE@267..268 " "
+ LITERAL@268..272
+ TRUE_KW@268..272 "true"
+ WHITESPACE@272..273 " "
+ BLOCK_EXPR@273..347
+ STMT_LIST@273..347
+ L_CURLY@273..274 "{"
+ WHITESPACE@274..283 "\n "
+ ATTR@283..302
+ POUND@283..284 "#"
+ BANG@284..285 "!"
+ L_BRACK@285..286 "["
+ META@286..301
+ PATH@286..289
+ PATH_SEGMENT@286..289
+ NAME_REF@286..289
+ IDENT@286..289 "doc"
+ TOKEN_TREE@289..301
+ L_PAREN@289..290 "("
+ STRING@290..300 "\"Nor here\""
+ R_PAREN@300..301 ")"
+ R_BRACK@301..302 "]"
+ WHITESPACE@302..311 "\n "
+ COMMENT@311..341 "//! Nor are ModuleDoc ..."
+ WHITESPACE@341..346 "\n "
+ R_CURLY@346..347 "}"
+ WHITESPACE@347..348 "\n"
+ R_CURLY@348..349 "}"
+ WHITESPACE@349..350 "\n"
+error 39..83: A block in this position cannot accept inner attributes
+error 152..171: A block in this position cannot accept inner attributes
+error 180..212: A block in this position cannot accept inner attributes
+error 283..302: A block in this position cannot accept inner attributes
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs
new file mode 100644
index 000000000..6a04f2d0a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs
@@ -0,0 +1,15 @@
+fn block() {
+ let inner = {
+ #![doc("Inner attributes not allowed here")]
+ //! Nor are ModuleDoc comments
+ };
+ if true {
+ #![doc("Nor here")]
+ #![doc("We error on each attr")]
+ //! Nor are ModuleDoc comments
+ }
+ while true {
+ #![doc("Nor here")]
+ //! Nor are ModuleDoc comments
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast
new file mode 100644
index 000000000..90c258cd1
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast
@@ -0,0 +1,105 @@
+SOURCE_FILE@0..118
+ IMPL@0..117
+ IMPL_KW@0..4 "impl"
+ WHITESPACE@4..5 " "
+ PATH_TYPE@5..6
+ PATH@5..6
+ PATH_SEGMENT@5..6
+ NAME_REF@5..6
+ IDENT@5..6 "T"
+ WHITESPACE@6..7 " "
+ FOR_KW@7..10 "for"
+ WHITESPACE@10..11 " "
+ TUPLE_TYPE@11..13
+ L_PAREN@11..12 "("
+ R_PAREN@12..13 ")"
+ WHITESPACE@13..14 " "
+ ASSOC_ITEM_LIST@14..117
+ L_CURLY@14..15 "{"
+ WHITESPACE@15..20 "\n "
+ FN@20..31
+ FN_KW@20..22 "fn"
+ WHITESPACE@22..23 " "
+ NAME@23..26
+ IDENT@23..26 "foo"
+ PARAM_LIST@26..28
+ L_PAREN@26..27 "("
+ R_PAREN@27..28 ")"
+ WHITESPACE@28..29 " "
+ BLOCK_EXPR@29..31
+ STMT_LIST@29..31
+ L_CURLY@29..30 "{"
+ R_CURLY@30..31 "}"
+ WHITESPACE@31..36 "\n "
+ FN@36..51
+ VISIBILITY@36..39
+ PUB_KW@36..39 "pub"
+ WHITESPACE@39..40 " "
+ FN_KW@40..42 "fn"
+ WHITESPACE@42..43 " "
+ NAME@43..46
+ IDENT@43..46 "bar"
+ PARAM_LIST@46..48
+ L_PAREN@46..47 "("
+ R_PAREN@47..48 ")"
+ WHITESPACE@48..49 " "
+ BLOCK_EXPR@49..51
+ STMT_LIST@49..51
+ L_CURLY@49..50 "{"
+ R_CURLY@50..51 "}"
+ WHITESPACE@51..56 "\n "
+ TYPE_ALIAS@56..81
+ VISIBILITY@56..66
+ PUB_KW@56..59 "pub"
+ L_PAREN@59..60 "("
+ PATH@60..65
+ PATH_SEGMENT@60..65
+ NAME_REF@60..65
+ CRATE_KW@60..65 "crate"
+ R_PAREN@65..66 ")"
+ WHITESPACE@66..67 " "
+ TYPE_KW@67..71 "type"
+ WHITESPACE@71..72 " "
+ NAME@72..75
+ IDENT@72..75 "Baz"
+ WHITESPACE@75..76 " "
+ EQ@76..77 "="
+ WHITESPACE@77..78 " "
+ TUPLE_TYPE@78..80
+ L_PAREN@78..79 "("
+ R_PAREN@79..80 ")"
+ SEMICOLON@80..81 ";"
+ WHITESPACE@81..86 "\n "
+ CONST@86..115
+ VISIBILITY@86..96
+ PUB_KW@86..89 "pub"
+ L_PAREN@89..90 "("
+ PATH@90..95
+ PATH_SEGMENT@90..95
+ NAME_REF@90..95
+ CRATE_KW@90..95 "crate"
+ R_PAREN@95..96 ")"
+ WHITESPACE@96..97 " "
+ CONST_KW@97..102 "const"
+ WHITESPACE@102..103 " "
+ NAME@103..104
+ IDENT@103..104 "C"
+ COLON@104..105 ":"
+ WHITESPACE@105..106 " "
+ PATH_TYPE@106..109
+ PATH@106..109
+ PATH_SEGMENT@106..109
+ NAME_REF@106..109
+ IDENT@106..109 "i32"
+ WHITESPACE@109..110 " "
+ EQ@110..111 "="
+ WHITESPACE@111..112 " "
+ LITERAL@112..114
+ INT_NUMBER@112..114 "92"
+ SEMICOLON@114..115 ";"
+ WHITESPACE@115..116 "\n"
+ R_CURLY@116..117 "}"
+ WHITESPACE@117..118 "\n"
+error 36..39: Unnecessary visibility qualifier
+error 56..66: Unnecessary visibility qualifier
+error 86..96: Unnecessary visibility qualifier
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rs
new file mode 100644
index 000000000..a43e7ef10
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rs
@@ -0,0 +1,6 @@
+impl T for () {
+ fn foo() {}
+ pub fn bar() {}
+ pub(crate) type Baz = ();
+ pub(crate) const C: i32 = 92;
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0038_endless_inclusive_range.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0038_endless_inclusive_range.rast
new file mode 100644
index 000000000..fd302fb4d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0038_endless_inclusive_range.rast
@@ -0,0 +1,30 @@
+SOURCE_FILE@0..33
+ FN@0..32
+ FN_KW@0..2 "fn"
+ WHITESPACE@2..3 " "
+ NAME@3..7
+ IDENT@3..7 "main"
+ PARAM_LIST@7..9
+ L_PAREN@7..8 "("
+ R_PAREN@8..9 ")"
+ WHITESPACE@9..10 " "
+ BLOCK_EXPR@10..32
+ STMT_LIST@10..32
+ L_CURLY@10..11 "{"
+ WHITESPACE@11..16 "\n "
+ EXPR_STMT@16..21
+ RANGE_EXPR@16..20
+ LITERAL@16..17
+ INT_NUMBER@16..17 "0"
+ DOT2EQ@17..20 "..="
+ SEMICOLON@20..21 ";"
+ WHITESPACE@21..26 "\n "
+ EXPR_STMT@26..30
+ RANGE_EXPR@26..29
+ DOT2EQ@26..29 "..="
+ SEMICOLON@29..30 ";"
+ WHITESPACE@30..31 "\n"
+ R_CURLY@31..32 "}"
+ WHITESPACE@32..33 "\n"
+error 16..20: An inclusive range must have an end expression
+error 26..29: An inclusive range must have an end expression
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0038_endless_inclusive_range.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0038_endless_inclusive_range.rs
new file mode 100644
index 000000000..0b4ed7a2b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0038_endless_inclusive_range.rs
@@ -0,0 +1,4 @@
+fn main() {
+ 0..=;
+ ..=;
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0040_illegal_crate_kw_location.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0040_illegal_crate_kw_location.rast
new file mode 100644
index 000000000..7449b5ddf
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0040_illegal_crate_kw_location.rast
@@ -0,0 +1,96 @@
+SOURCE_FILE@0..98
+ USE@0..12
+ USE_KW@0..3 "use"
+ WHITESPACE@3..4 " "
+ USE_TREE@4..11
+ PATH@4..11
+ PATH_SEGMENT@4..11
+ COLON2@4..6 "::"
+ NAME_REF@6..11
+ CRATE_KW@6..11 "crate"
+ SEMICOLON@11..12 ";"
+ WHITESPACE@12..13 "\n"
+ USE@13..54
+ USE_KW@13..16 "use"
+ WHITESPACE@16..17 " "
+ USE_TREE@17..53
+ USE_TREE_LIST@17..53
+ L_CURLY@17..18 "{"
+ USE_TREE@18..23
+ PATH@18..23
+ PATH_SEGMENT@18..23
+ NAME_REF@18..23
+ CRATE_KW@18..23 "crate"
+ COMMA@23..24 ","
+ WHITESPACE@24..25 " "
+ USE_TREE@25..52
+ PATH@25..28
+ PATH_SEGMENT@25..28
+ NAME_REF@25..28
+ IDENT@25..28 "foo"
+ COLON2@28..30 "::"
+ USE_TREE_LIST@30..52
+ L_CURLY@30..31 "{"
+ USE_TREE@31..51
+ PATH@31..51
+ PATH@31..46
+ PATH@31..41
+ PATH@31..36
+ PATH_SEGMENT@31..36
+ NAME_REF@31..36
+ CRATE_KW@31..36 "crate"
+ COLON2@36..38 "::"
+ PATH_SEGMENT@38..41
+ NAME_REF@38..41
+ IDENT@38..41 "foo"
+ COLON2@41..43 "::"
+ PATH_SEGMENT@43..46
+ NAME_REF@43..46
+ IDENT@43..46 "bar"
+ COLON2@46..48 "::"
+ PATH_SEGMENT@48..51
+ NAME_REF@48..51
+ IDENT@48..51 "baz"
+ R_CURLY@51..52 "}"
+ R_CURLY@52..53 "}"
+ SEMICOLON@53..54 ";"
+ WHITESPACE@54..55 "\n"
+ USE@55..72
+ USE_KW@55..58 "use"
+ WHITESPACE@58..59 " "
+ USE_TREE@59..71
+ PATH@59..71
+ PATH@59..64
+ PATH_SEGMENT@59..64
+ NAME_REF@59..64
+ IDENT@59..64 "hello"
+ COLON2@64..66 "::"
+ PATH_SEGMENT@66..71
+ NAME_REF@66..71
+ CRATE_KW@66..71 "crate"
+ SEMICOLON@71..72 ";"
+ WHITESPACE@72..73 "\n"
+ USE@73..97
+ USE_KW@73..76 "use"
+ WHITESPACE@76..77 " "
+ USE_TREE@77..96
+ PATH@77..96
+ PATH@77..89
+ PATH@77..82
+ PATH_SEGMENT@77..82
+ NAME_REF@77..82
+ IDENT@77..82 "hello"
+ COLON2@82..84 "::"
+ PATH_SEGMENT@84..89
+ NAME_REF@84..89
+ CRATE_KW@84..89 "crate"
+ COLON2@89..91 "::"
+ PATH_SEGMENT@91..96
+ NAME_REF@91..96
+ IDENT@91..96 "there"
+ SEMICOLON@96..97 ";"
+ WHITESPACE@97..98 "\n"
+error 6..11: The `crate` keyword is only allowed as the first segment of a path
+error 31..36: The `crate` keyword is only allowed as the first segment of a path
+error 66..71: The `crate` keyword is only allowed as the first segment of a path
+error 84..89: The `crate` keyword is only allowed as the first segment of a path
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0040_illegal_crate_kw_location.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0040_illegal_crate_kw_location.rs
new file mode 100644
index 000000000..508def2c7
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0040_illegal_crate_kw_location.rs
@@ -0,0 +1,4 @@
+use ::crate;
+use {crate, foo::{crate::foo::bar::baz}};
+use hello::crate;
+use hello::crate::there;
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rast
new file mode 100644
index 000000000..01f601091
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rast
@@ -0,0 +1,29 @@
+SOURCE_FILE@0..25
+ USE@0..11
+ USE_KW@0..3 "use"
+ WHITESPACE@3..4 " "
+ USE_TREE@4..10
+ PATH@4..10
+ PATH_SEGMENT@4..10
+ COLON2@4..6 "::"
+ NAME_REF@6..10
+ SELF_KW@6..10 "self"
+ SEMICOLON@10..11 ";"
+ WHITESPACE@11..12 "\n"
+ USE@12..24
+ USE_KW@12..15 "use"
+ WHITESPACE@15..16 " "
+ USE_TREE@16..23
+ PATH@16..23
+ PATH@16..17
+ PATH_SEGMENT@16..17
+ NAME_REF@16..17
+ IDENT@16..17 "a"
+ COLON2@17..19 "::"
+ PATH_SEGMENT@19..23
+ NAME_REF@19..23
+ SELF_KW@19..23 "self"
+ SEMICOLON@23..24 ";"
+ WHITESPACE@24..25 "\n"
+error 6..10: The `self` keyword is only allowed as the first segment of a path
+error 19..23: The `self` keyword is only allowed as the first segment of a path
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rs
new file mode 100644
index 000000000..b9e1d7d8b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rs
@@ -0,0 +1,2 @@
+use ::self;
+use a::self;
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0045_ambiguous_trait_object.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0045_ambiguous_trait_object.rast
new file mode 100644
index 000000000..d94daacdc
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0045_ambiguous_trait_object.rast
@@ -0,0 +1,196 @@
+SOURCE_FILE@0..187
+ TYPE_ALIAS@0..35
+ TYPE_KW@0..4 "type"
+ WHITESPACE@4..5 " "
+ NAME@5..8
+ IDENT@5..8 "Foo"
+ GENERIC_PARAM_LIST@8..12
+ L_ANGLE@8..9 "<"
+ LIFETIME_PARAM@9..11
+ LIFETIME@9..11
+ LIFETIME_IDENT@9..11 "'a"
+ R_ANGLE@11..12 ">"
+ WHITESPACE@12..13 " "
+ EQ@13..14 "="
+ WHITESPACE@14..15 " "
+ REF_TYPE@15..34
+ AMP@15..16 "&"
+ LIFETIME@16..18
+ LIFETIME_IDENT@16..18 "'a"
+ WHITESPACE@18..19 " "
+ DYN_TRAIT_TYPE@19..34
+ DYN_KW@19..22 "dyn"
+ WHITESPACE@22..23 " "
+ TYPE_BOUND_LIST@23..34
+ TYPE_BOUND@23..27
+ PATH_TYPE@23..27
+ PATH@23..27
+ PATH_SEGMENT@23..27
+ NAME_REF@23..27
+ IDENT@23..27 "Send"
+ WHITESPACE@27..28 " "
+ PLUS@28..29 "+"
+ WHITESPACE@29..30 " "
+ TYPE_BOUND@30..34
+ PATH_TYPE@30..34
+ PATH@30..34
+ PATH_SEGMENT@30..34
+ NAME_REF@30..34
+ IDENT@30..34 "Sync"
+ SEMICOLON@34..35 ";"
+ WHITESPACE@35..36 "\n"
+ TYPE_ALIAS@36..70
+ TYPE_KW@36..40 "type"
+ WHITESPACE@40..41 " "
+ NAME@41..44
+ IDENT@41..44 "Foo"
+ WHITESPACE@44..45 " "
+ EQ@45..46 "="
+ WHITESPACE@46..47 " "
+ PTR_TYPE@47..69
+ STAR@47..48 "*"
+ CONST_KW@48..53 "const"
+ WHITESPACE@53..54 " "
+ DYN_TRAIT_TYPE@54..69
+ DYN_KW@54..57 "dyn"
+ WHITESPACE@57..58 " "
+ TYPE_BOUND_LIST@58..69
+ TYPE_BOUND@58..62
+ PATH_TYPE@58..62
+ PATH@58..62
+ PATH_SEGMENT@58..62
+ NAME_REF@58..62
+ IDENT@58..62 "Send"
+ WHITESPACE@62..63 " "
+ PLUS@63..64 "+"
+ WHITESPACE@64..65 " "
+ TYPE_BOUND@65..69
+ PATH_TYPE@65..69
+ PATH@65..69
+ PATH_SEGMENT@65..69
+ NAME_REF@65..69
+ IDENT@65..69 "Sync"
+ SEMICOLON@69..70 ";"
+ WHITESPACE@70..71 "\n"
+ TYPE_ALIAS@71..109
+ TYPE_KW@71..75 "type"
+ WHITESPACE@75..76 " "
+ NAME@76..79
+ IDENT@76..79 "Foo"
+ WHITESPACE@79..80 " "
+ EQ@80..81 "="
+ WHITESPACE@81..82 " "
+ FN_PTR_TYPE@82..108
+ FN_KW@82..84 "fn"
+ PARAM_LIST@84..86
+ L_PAREN@84..85 "("
+ R_PAREN@85..86 ")"
+ WHITESPACE@86..87 " "
+ RET_TYPE@87..108
+ THIN_ARROW@87..89 "->"
+ WHITESPACE@89..90 " "
+ DYN_TRAIT_TYPE@90..108
+ DYN_KW@90..93 "dyn"
+ WHITESPACE@93..94 " "
+ TYPE_BOUND_LIST@94..108
+ TYPE_BOUND@94..98
+ PATH_TYPE@94..98
+ PATH@94..98
+ PATH_SEGMENT@94..98
+ NAME_REF@94..98
+ IDENT@94..98 "Send"
+ WHITESPACE@98..99 " "
+ PLUS@99..100 "+"
+ WHITESPACE@100..101 " "
+ TYPE_BOUND@101..108
+ LIFETIME@101..108
+ LIFETIME_IDENT@101..108 "'static"
+ SEMICOLON@108..109 ";"
+ WHITESPACE@109..110 "\n"
+ FN@110..186
+ FN_KW@110..112 "fn"
+ WHITESPACE@112..113 " "
+ NAME@113..117
+ IDENT@113..117 "main"
+ PARAM_LIST@117..119
+ L_PAREN@117..118 "("
+ R_PAREN@118..119 ")"
+ WHITESPACE@119..120 " "
+ BLOCK_EXPR@120..186
+ STMT_LIST@120..186
+ L_CURLY@120..121 "{"
+ WHITESPACE@121..126 "\n "
+ LET_STMT@126..184
+ LET_KW@126..129 "let"
+ WHITESPACE@129..130 " "
+ IDENT_PAT@130..131
+ NAME@130..131
+ IDENT@130..131 "b"
+ WHITESPACE@131..132 " "
+ EQ@132..133 "="
+ WHITESPACE@133..134 " "
+ CAST_EXPR@134..183
+ PAREN_EXPR@134..138
+ L_PAREN@134..135 "("
+ REF_EXPR@135..137
+ AMP@135..136 "&"
+ PATH_EXPR@136..137
+ PATH@136..137
+ PATH_SEGMENT@136..137
+ NAME_REF@136..137
+ IDENT@136..137 "a"
+ R_PAREN@137..138 ")"
+ WHITESPACE@138..139 " "
+ AS_KW@139..141 "as"
+ WHITESPACE@141..142 " "
+ REF_TYPE@142..183
+ AMP@142..143 "&"
+ DYN_TRAIT_TYPE@143..183
+ DYN_KW@143..146 "dyn"
+ WHITESPACE@146..147 " "
+ TYPE_BOUND_LIST@147..183
+ TYPE_BOUND@147..175
+ PATH_TYPE@147..175
+ PATH@147..175
+ PATH_SEGMENT@147..175
+ NAME_REF@147..150
+ IDENT@147..150 "Add"
+ GENERIC_ARG_LIST@150..175
+ L_ANGLE@150..151 "<"
+ TYPE_ARG@151..156
+ PATH_TYPE@151..156
+ PATH@151..156
+ PATH_SEGMENT@151..156
+ NAME_REF@151..156
+ IDENT@151..156 "Other"
+ COMMA@156..157 ","
+ WHITESPACE@157..158 " "
+ ASSOC_TYPE_ARG@158..174
+ NAME_REF@158..164
+ IDENT@158..164 "Output"
+ WHITESPACE@164..165 " "
+ EQ@165..166 "="
+ WHITESPACE@166..167 " "
+ PATH_TYPE@167..174
+ PATH@167..174
+ PATH_SEGMENT@167..174
+ NAME_REF@167..174
+ IDENT@167..174 "Addable"
+ R_ANGLE@174..175 ">"
+ WHITESPACE@175..176 " "
+ PLUS@176..177 "+"
+ WHITESPACE@177..178 " "
+ TYPE_BOUND@178..183
+ PATH_TYPE@178..183
+ PATH@178..183
+ PATH_SEGMENT@178..183
+ NAME_REF@178..183
+ IDENT@178..183 "Other"
+ SEMICOLON@183..184 ";"
+ WHITESPACE@184..185 "\n"
+ R_CURLY@185..186 "}"
+ WHITESPACE@186..187 "\n"
+error 19..34: ambiguous `+` in a type
+error 54..69: ambiguous `+` in a type
+error 90..108: ambiguous `+` in a type
+error 143..183: ambiguous `+` in a type
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0045_ambiguous_trait_object.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0045_ambiguous_trait_object.rs
new file mode 100644
index 000000000..0a5958f25
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0045_ambiguous_trait_object.rs
@@ -0,0 +1,6 @@
+type Foo<'a> = &'a dyn Send + Sync;
+type Foo = *const dyn Send + Sync;
+type Foo = fn() -> dyn Send + 'static;
+fn main() {
+ let b = (&a) as &dyn Add<Other, Output = Addable> + Other;
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0046_mutable_const_item.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0046_mutable_const_item.rast
new file mode 100644
index 000000000..c7eb312c9
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0046_mutable_const_item.rast
@@ -0,0 +1,22 @@
+SOURCE_FILE@0..24
+ CONST@0..23
+ CONST_KW@0..5 "const"
+ WHITESPACE@5..6 " "
+ MUT_KW@6..9 "mut"
+ WHITESPACE@9..10 " "
+ NAME@10..13
+ IDENT@10..13 "FOO"
+ COLON@13..14 ":"
+ WHITESPACE@14..15 " "
+ TUPLE_TYPE@15..17
+ L_PAREN@15..16 "("
+ R_PAREN@16..17 ")"
+ WHITESPACE@17..18 " "
+ EQ@18..19 "="
+ WHITESPACE@19..20 " "
+ TUPLE_EXPR@20..22
+ L_PAREN@20..21 "("
+ R_PAREN@21..22 ")"
+ SEMICOLON@22..23 ";"
+ WHITESPACE@23..24 "\n"
+error 6..9: const globals cannot be mutable
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0046_mutable_const_item.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0046_mutable_const_item.rs
new file mode 100644
index 000000000..ccab6bccf
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0046_mutable_const_item.rs
@@ -0,0 +1 @@
+const mut FOO: () = ();
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/invalid_let_expr.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/invalid_let_expr.rast
new file mode 100644
index 000000000..9e1e48864
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/invalid_let_expr.rast
@@ -0,0 +1,216 @@
+SOURCE_FILE@0..282
+ FN@0..281
+ 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..281
+ STMT_LIST@9..281
+ L_CURLY@9..10 "{"
+ WHITESPACE@10..15 "\n "
+ CONST@15..42
+ CONST_KW@15..20 "const"
+ WHITESPACE@20..21 " "
+ UNDERSCORE@21..22 "_"
+ COLON@22..23 ":"
+ WHITESPACE@23..24 " "
+ TUPLE_TYPE@24..26
+ L_PAREN@24..25 "("
+ R_PAREN@25..26 ")"
+ WHITESPACE@26..27 " "
+ EQ@27..28 "="
+ WHITESPACE@28..29 " "
+ LET_EXPR@29..41
+ LET_KW@29..32 "let"
+ WHITESPACE@32..33 " "
+ WILDCARD_PAT@33..34
+ UNDERSCORE@33..34 "_"
+ WHITESPACE@34..35 " "
+ EQ@35..36 "="
+ WHITESPACE@36..37 " "
+ PATH_EXPR@37..41
+ PATH@37..41
+ PATH_SEGMENT@37..41
+ NAME_REF@37..41
+ IDENT@37..41 "None"
+ SEMICOLON@41..42 ";"
+ WHITESPACE@42..48 "\n\n "
+ LET_STMT@48..83
+ LET_KW@48..51 "let"
+ WHITESPACE@51..52 " "
+ WILDCARD_PAT@52..53
+ UNDERSCORE@52..53 "_"
+ WHITESPACE@53..54 " "
+ EQ@54..55 "="
+ WHITESPACE@55..56 " "
+ IF_EXPR@56..82
+ IF_KW@56..58 "if"
+ WHITESPACE@58..59 " "
+ LITERAL@59..63
+ TRUE_KW@59..63 "true"
+ WHITESPACE@63..64 " "
+ BLOCK_EXPR@64..82
+ STMT_LIST@64..82
+ L_CURLY@64..65 "{"
+ WHITESPACE@65..66 " "
+ PAREN_EXPR@66..80
+ L_PAREN@66..67 "("
+ LET_EXPR@67..79
+ LET_KW@67..70 "let"
+ WHITESPACE@70..71 " "
+ WILDCARD_PAT@71..72
+ UNDERSCORE@71..72 "_"
+ WHITESPACE@72..73 " "
+ EQ@73..74 "="
+ WHITESPACE@74..75 " "
+ PATH_EXPR@75..79
+ PATH@75..79
+ PATH_SEGMENT@75..79
+ NAME_REF@75..79
+ IDENT@75..79 "None"
+ R_PAREN@79..80 ")"
+ WHITESPACE@80..81 " "
+ R_CURLY@81..82 "}"
+ SEMICOLON@82..83 ";"
+ WHITESPACE@83..89 "\n\n "
+ IF_EXPR@89..279
+ IF_KW@89..91 "if"
+ WHITESPACE@91..92 " "
+ BIN_EXPR@92..114
+ LITERAL@92..96
+ TRUE_KW@92..96 "true"
+ WHITESPACE@96..97 " "
+ AMP2@97..99 "&&"
+ WHITESPACE@99..100 " "
+ PAREN_EXPR@100..114
+ L_PAREN@100..101 "("
+ LET_EXPR@101..113
+ LET_KW@101..104 "let"
+ WHITESPACE@104..105 " "
+ WILDCARD_PAT@105..106
+ UNDERSCORE@105..106 "_"
+ WHITESPACE@106..107 " "
+ EQ@107..108 "="
+ WHITESPACE@108..109 " "
+ PATH_EXPR@109..113
+ PATH@109..113
+ PATH_SEGMENT@109..113
+ NAME_REF@109..113
+ IDENT@109..113 "None"
+ R_PAREN@113..114 ")"
+ WHITESPACE@114..115 " "
+ BLOCK_EXPR@115..279
+ STMT_LIST@115..279
+ L_CURLY@115..116 "{"
+ WHITESPACE@116..125 "\n "
+ EXPR_STMT@125..140
+ PAREN_EXPR@125..139
+ L_PAREN@125..126 "("
+ LET_EXPR@126..138
+ LET_KW@126..129 "let"
+ WHITESPACE@129..130 " "
+ WILDCARD_PAT@130..131
+ UNDERSCORE@130..131 "_"
+ WHITESPACE@131..132 " "
+ EQ@132..133 "="
+ WHITESPACE@133..134 " "
+ PATH_EXPR@134..138
+ PATH@134..138
+ PATH_SEGMENT@134..138
+ NAME_REF@134..138
+ IDENT@134..138 "None"
+ R_PAREN@138..139 ")"
+ SEMICOLON@139..140 ";"
+ WHITESPACE@140..149 "\n "
+ WHILE_EXPR@149..273
+ WHILE_KW@149..154 "while"
+ WHITESPACE@154..155 " "
+ LET_EXPR@155..167
+ LET_KW@155..158 "let"
+ WHITESPACE@158..159 " "
+ WILDCARD_PAT@159..160
+ UNDERSCORE@159..160 "_"
+ WHITESPACE@160..161 " "
+ EQ@161..162 "="
+ WHITESPACE@162..163 " "
+ PATH_EXPR@163..167
+ PATH@163..167
+ PATH_SEGMENT@163..167
+ NAME_REF@163..167
+ IDENT@163..167 "None"
+ WHITESPACE@167..168 " "
+ BLOCK_EXPR@168..273
+ STMT_LIST@168..273
+ L_CURLY@168..169 "{"
+ WHITESPACE@169..182 "\n "
+ MATCH_EXPR@182..263
+ MATCH_KW@182..187 "match"
+ WHITESPACE@187..188 " "
+ PATH_EXPR@188..192
+ PATH@188..192
+ PATH_SEGMENT@188..192
+ NAME_REF@188..192
+ IDENT@188..192 "None"
+ WHITESPACE@192..193 " "
+ MATCH_ARM_LIST@193..263
+ L_CURLY@193..194 "{"
+ WHITESPACE@194..211 "\n "
+ MATCH_ARM@211..249
+ WILDCARD_PAT@211..212
+ UNDERSCORE@211..212 "_"
+ WHITESPACE@212..213 " "
+ MATCH_GUARD@213..228
+ IF_KW@213..215 "if"
+ WHITESPACE@215..216 " "
+ LET_EXPR@216..228
+ LET_KW@216..219 "let"
+ WHITESPACE@219..220 " "
+ WILDCARD_PAT@220..221
+ UNDERSCORE@220..221 "_"
+ WHITESPACE@221..222 " "
+ EQ@222..223 "="
+ WHITESPACE@223..224 " "
+ PATH_EXPR@224..228
+ PATH@224..228
+ PATH_SEGMENT@224..228
+ NAME_REF@224..228
+ IDENT@224..228 "None"
+ WHITESPACE@228..229 " "
+ FAT_ARROW@229..231 "=>"
+ WHITESPACE@231..232 " "
+ BLOCK_EXPR@232..249
+ STMT_LIST@232..249
+ L_CURLY@232..233 "{"
+ WHITESPACE@233..234 " "
+ LET_STMT@234..247
+ LET_KW@234..237 "let"
+ WHITESPACE@237..238 " "
+ WILDCARD_PAT@238..239
+ UNDERSCORE@238..239 "_"
+ WHITESPACE@239..240 " "
+ EQ@240..241 "="
+ WHITESPACE@241..242 " "
+ PATH_EXPR@242..246
+ PATH@242..246
+ PATH_SEGMENT@242..246
+ NAME_REF@242..246
+ IDENT@242..246 "None"
+ SEMICOLON@246..247 ";"
+ WHITESPACE@247..248 " "
+ R_CURLY@248..249 "}"
+ WHITESPACE@249..262 "\n "
+ R_CURLY@262..263 "}"
+ WHITESPACE@263..272 "\n "
+ R_CURLY@272..273 "}"
+ WHITESPACE@273..278 "\n "
+ R_CURLY@278..279 "}"
+ WHITESPACE@279..280 "\n"
+ R_CURLY@280..281 "}"
+ WHITESPACE@281..282 "\n"
+error 29..41: `let` expressions are not supported here
+error 67..79: `let` expressions are not supported here
+error 126..138: `let` expressions are not supported here
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/invalid_let_expr.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/invalid_let_expr.rs
new file mode 100644
index 000000000..1515ae533
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/invalid_let_expr.rs
@@ -0,0 +1,14 @@
+fn foo() {
+ const _: () = let _ = None;
+
+ let _ = if true { (let _ = None) };
+
+ if true && (let _ = None) {
+ (let _ = None);
+ while let _ = None {
+ match None {
+ _ if let _ = None => { let _ = None; }
+ }
+ }
+ }
+}