about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/crates/syntax
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-01-09 21:14:32 +0000
committerbors <bors@rust-lang.org>2023-01-09 21:14:32 +0000
commit3254bef9a79bd762b8cece2078abe168cb29ccb1 (patch)
treeefee5b2f868754652158f690e5471a3c6a1293f0 /src/tools/rust-analyzer/crates/syntax
parent3020239de947ec52677e9b4e853a6a9fc073d1f9 (diff)
parent06e705f13dade399d629dd422e3fc84bddc58db0 (diff)
Auto merge of #105855 - arcnmx:rust-analyzer-2022-12, r=lnicola
:arrow_up: rust-analyzer

Updates rust-analyzer to https://github.com/rust-lang/rust-analyzer/commit/368e0bb32f1178cf162c2ce5f7e10b7ae211eb26

This is a continuation/replacement of #105834, close that if this is chosen instead.
Diffstat (limited to 'src/tools/rust-analyzer/crates/syntax')
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram4
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/algo.rs7
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs4
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs16
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs53
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs40
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs328
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/fuzz.rs6
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/hacks.rs2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ptr.rs2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ted.rs6
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests.rs6
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs5
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs10
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/validation.rs2
17 files changed, 442 insertions, 58 deletions
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 0a0cb0290d6..2c67586a390 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -359,6 +359,7 @@ Expr =
 | TupleExpr
 | WhileExpr
 | YieldExpr
+| YeetExpr
 | LetExpr
 | UnderscoreExpr
 
@@ -503,6 +504,9 @@ ReturnExpr =
 YieldExpr =
   Attr* 'yield' Expr?
 
+YeetExpr =
+  Attr* 'do' 'yeet' Expr?
+
 LetExpr =
   Attr* 'let' Pat '=' Expr
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/algo.rs b/src/tools/rust-analyzer/crates/syntax/src/algo.rs
index 8b14789dd91..c402a7bceae 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/algo.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/algo.rs
@@ -616,7 +616,7 @@ fn main() {
 
         let fmt_syntax = |syn: &SyntaxElement| match syn.kind() {
             SyntaxKind::WHITESPACE => format!("{:?}", syn.to_string()),
-            _ => format!("{}", syn),
+            _ => format!("{syn}"),
         };
 
         let insertions =
@@ -637,7 +637,7 @@ fn main() {
             .iter()
             .sorted_by_key(|(syntax, _)| syntax.text_range().start())
             .format_with("\n", |(k, v), f| {
-                f(&format!("Line {}: {:?} -> {}", line_number(k), k, fmt_syntax(v)))
+                f(&format!("Line {}: {k:?} -> {}", line_number(k), fmt_syntax(v)))
             });
 
         let deletions = diff
@@ -646,8 +646,7 @@ fn main() {
             .format_with("\n", |v, f| f(&format!("Line {}: {}", line_number(v), &fmt_syntax(v))));
 
         let actual = format!(
-            "insertions:\n\n{}\n\nreplacements:\n\n{}\n\ndeletions:\n\n{}\n",
-            insertions, replacements, deletions
+            "insertions:\n\n{insertions}\n\nreplacements:\n\n{replacements}\n\ndeletions:\n\n{deletions}\n"
         );
         expected_diff.assert_eq(&actual);
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
index 4aa64d0d6e8..10c04575833 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
@@ -9,6 +9,7 @@ mod operators;
 pub mod edit;
 pub mod edit_in_place;
 pub mod make;
+pub mod prec;
 
 use std::marker::PhantomData;
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs
index 15805dfc860..5bc6b780e47 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs
@@ -87,7 +87,7 @@ impl IndentLevel {
         for token in tokens {
             if let Some(ws) = ast::Whitespace::cast(token) {
                 if ws.text().contains('\n') {
-                    let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self));
+                    let new_ws = make::tokens::whitespace(&format!("{}{self}", ws.syntax()));
                     ted::replace(ws.syntax(), &new_ws);
                 }
             }
@@ -103,7 +103,7 @@ impl IndentLevel {
             if let Some(ws) = ast::Whitespace::cast(token) {
                 if ws.text().contains('\n') {
                     let new_ws = make::tokens::whitespace(
-                        &ws.syntax().text().replace(&format!("\n{}", self), "\n"),
+                        &ws.syntax().text().replace(&format!("\n{self}"), "\n"),
                     );
                     ted::replace(ws.syntax(), &new_ws);
                 }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
index 660c057e99c..a493c92e7da 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
@@ -481,7 +481,7 @@ impl ast::AssocItemList {
             },
         };
         let elements: Vec<SyntaxElement<_>> = vec![
-            make::tokens::whitespace(&format!("{}{}", whitespace, indent)).into(),
+            make::tokens::whitespace(&format!("{whitespace}{indent}")).into(),
             item.syntax().clone().into(),
         ];
         ted::insert_all(position, elements);
@@ -537,7 +537,7 @@ impl ast::MatchArmList {
             },
         };
         let indent = IndentLevel::from_node(self.syntax()) + 1;
-        elements.push(make::tokens::whitespace(&format!("\n{}", indent)).into());
+        elements.push(make::tokens::whitespace(&format!("\n{indent}")).into());
         elements.push(arm.syntax().clone().into());
         if needs_comma(&arm) {
             ted::append_child(arm.syntax(), make::token(SyntaxKind::COMMA));
@@ -555,7 +555,7 @@ impl ast::RecordExprFieldList {
         let is_multiline = self.syntax().text().contains_char('\n');
         let whitespace = if is_multiline {
             let indent = IndentLevel::from_node(self.syntax()) + 1;
-            make::tokens::whitespace(&format!("\n{}", indent))
+            make::tokens::whitespace(&format!("\n{indent}"))
         } else {
             make::tokens::single_space()
         };
@@ -616,7 +616,7 @@ impl ast::RecordPatFieldList {
         let is_multiline = self.syntax().text().contains_char('\n');
         let whitespace = if is_multiline {
             let indent = IndentLevel::from_node(self.syntax()) + 1;
-            make::tokens::whitespace(&format!("\n{}", indent))
+            make::tokens::whitespace(&format!("\n{indent}"))
         } else {
             make::tokens::single_space()
         };
@@ -681,7 +681,7 @@ impl ast::VariantList {
             },
         };
         let elements: Vec<SyntaxElement<_>> = vec![
-            make::tokens::whitespace(&format!("{}{}", "\n", indent)).into(),
+            make::tokens::whitespace(&format!("{}{indent}", "\n")).into(),
             variant.syntax().clone().into(),
             ast::make::token(T![,]).into(),
         ];
@@ -704,11 +704,11 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
     match l.next_sibling_or_token() {
         Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => {
             if ws.next_sibling_or_token()?.into_token()? == r {
-                ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent)));
+                ted::replace(ws, make::tokens::whitespace(&format!("\n{indent}")));
             }
         }
         Some(ws) if ws.kind() == T!['}'] => {
-            ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent)));
+            ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{indent}")));
         }
         _ => (),
     }
@@ -888,6 +888,6 @@ enum Foo {
         let enum_ = ast_mut_from_text::<ast::Enum>(before);
         enum_.variant_list().map(|it| it.add_variant(variant));
         let after = enum_.to_string();
-        assert_eq_text!(&trim_indent(expected.trim()), &trim_indent(&after.trim()));
+        assert_eq_text!(&trim_indent(expected.trim()), &trim_indent(after.trim()));
     }
 }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 2ea715f47fb..a214a5e4462 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -1064,6 +1064,17 @@ impl YieldExpr {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct YeetExpr {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasAttrs for YeetExpr {}
+impl YeetExpr {
+    pub fn do_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![do]) }
+    pub fn yeet_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![yeet]) }
+    pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct LetExpr {
     pub(crate) syntax: SyntaxNode,
 }
@@ -1541,6 +1552,7 @@ pub enum Expr {
     TupleExpr(TupleExpr),
     WhileExpr(WhileExpr),
     YieldExpr(YieldExpr),
+    YeetExpr(YeetExpr),
     LetExpr(LetExpr),
     UnderscoreExpr(UnderscoreExpr),
 }
@@ -2694,6 +2706,17 @@ impl AstNode for YieldExpr {
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl AstNode for YeetExpr {
+    fn can_cast(kind: SyntaxKind) -> bool { kind == YEET_EXPR }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
 impl AstNode for LetExpr {
     fn can_cast(kind: SyntaxKind) -> bool { kind == LET_EXPR }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -3382,6 +3405,9 @@ impl From<WhileExpr> for Expr {
 impl From<YieldExpr> for Expr {
     fn from(node: YieldExpr) -> Expr { Expr::YieldExpr(node) }
 }
+impl From<YeetExpr> for Expr {
+    fn from(node: YeetExpr) -> Expr { Expr::YeetExpr(node) }
+}
 impl From<LetExpr> for Expr {
     fn from(node: LetExpr) -> Expr { Expr::LetExpr(node) }
 }
@@ -3422,6 +3448,7 @@ impl AstNode for Expr {
                 | TUPLE_EXPR
                 | WHILE_EXPR
                 | YIELD_EXPR
+                | YEET_EXPR
                 | LET_EXPR
                 | UNDERSCORE_EXPR
         )
@@ -3458,6 +3485,7 @@ impl AstNode for Expr {
             TUPLE_EXPR => Expr::TupleExpr(TupleExpr { syntax }),
             WHILE_EXPR => Expr::WhileExpr(WhileExpr { syntax }),
             YIELD_EXPR => Expr::YieldExpr(YieldExpr { syntax }),
+            YEET_EXPR => Expr::YeetExpr(YeetExpr { syntax }),
             LET_EXPR => Expr::LetExpr(LetExpr { syntax }),
             UNDERSCORE_EXPR => Expr::UnderscoreExpr(UnderscoreExpr { syntax }),
             _ => return None,
@@ -3496,6 +3524,7 @@ impl AstNode for Expr {
             Expr::TupleExpr(it) => &it.syntax,
             Expr::WhileExpr(it) => &it.syntax,
             Expr::YieldExpr(it) => &it.syntax,
+            Expr::YeetExpr(it) => &it.syntax,
             Expr::LetExpr(it) => &it.syntax,
             Expr::UnderscoreExpr(it) => &it.syntax,
         }
@@ -3892,7 +3921,7 @@ impl AnyHasArgList {
 impl AstNode for AnyHasArgList {
     fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, CALL_EXPR | METHOD_CALL_EXPR) }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
-        Self::can_cast(syntax.kind()).then(|| AnyHasArgList { syntax })
+        Self::can_cast(syntax.kind()).then_some(AnyHasArgList { syntax })
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
@@ -3963,6 +3992,7 @@ impl AstNode for AnyHasAttrs {
                 | TUPLE_EXPR
                 | WHILE_EXPR
                 | YIELD_EXPR
+                | YEET_EXPR
                 | LET_EXPR
                 | UNDERSCORE_EXPR
                 | STMT_LIST
@@ -3976,7 +4006,7 @@ impl AstNode for AnyHasAttrs {
         )
     }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
-        Self::can_cast(syntax.kind()).then(|| AnyHasAttrs { syntax })
+        Self::can_cast(syntax.kind()).then_some(AnyHasAttrs { syntax })
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
@@ -4013,7 +4043,7 @@ impl AstNode for AnyHasDocComments {
         )
     }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
-        Self::can_cast(syntax.kind()).then(|| AnyHasDocComments { syntax })
+        Self::can_cast(syntax.kind()).then_some(AnyHasDocComments { syntax })
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
@@ -4028,7 +4058,7 @@ impl AstNode for AnyHasGenericParams {
         matches!(kind, ENUM | FN | IMPL | STRUCT | TRAIT | TYPE_ALIAS | UNION)
     }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
-        Self::can_cast(syntax.kind()).then(|| AnyHasGenericParams { syntax })
+        Self::can_cast(syntax.kind()).then_some(AnyHasGenericParams { syntax })
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
@@ -4041,7 +4071,7 @@ impl AnyHasLoopBody {
 impl AstNode for AnyHasLoopBody {
     fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, FOR_EXPR | LOOP_EXPR | WHILE_EXPR) }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
-        Self::can_cast(syntax.kind()).then(|| AnyHasLoopBody { syntax })
+        Self::can_cast(syntax.kind()).then_some(AnyHasLoopBody { syntax })
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
@@ -4054,7 +4084,7 @@ impl AnyHasModuleItem {
 impl AstNode for AnyHasModuleItem {
     fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, MACRO_ITEMS | SOURCE_FILE | ITEM_LIST) }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
-        Self::can_cast(syntax.kind()).then(|| AnyHasModuleItem { syntax })
+        Self::can_cast(syntax.kind()).then_some(AnyHasModuleItem { syntax })
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
@@ -4089,7 +4119,7 @@ impl AstNode for AnyHasName {
         )
     }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
-        Self::can_cast(syntax.kind()).then(|| AnyHasName { syntax })
+        Self::can_cast(syntax.kind()).then_some(AnyHasName { syntax })
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
@@ -4107,7 +4137,7 @@ impl AstNode for AnyHasTypeBounds {
         )
     }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
-        Self::can_cast(syntax.kind()).then(|| AnyHasTypeBounds { syntax })
+        Self::can_cast(syntax.kind()).then_some(AnyHasTypeBounds { syntax })
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
@@ -4141,7 +4171,7 @@ impl AstNode for AnyHasVisibility {
         )
     }
     fn cast(syntax: SyntaxNode) -> Option<Self> {
-        Self::can_cast(syntax.kind()).then(|| AnyHasVisibility { syntax })
+        Self::can_cast(syntax.kind()).then_some(AnyHasVisibility { syntax })
     }
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
@@ -4655,6 +4685,11 @@ impl std::fmt::Display for YieldExpr {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for YeetExpr {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for LetExpr {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index 8c26009add2..d5b3296980c 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -339,10 +339,10 @@ pub fn tail_only_block_expr(tail_expr: ast::Expr) -> ast::BlockExpr {
 }
 
 /// Ideally this function wouldn't exist since it involves manual indenting.
-/// It differs from `make::block_expr` by also supporting comments.
+/// It differs from `make::block_expr` by also supporting comments and whitespace.
 ///
 /// FIXME: replace usages of this with the mutable syntax tree API
-pub fn hacky_block_expr_with_comments(
+pub fn hacky_block_expr(
     elements: impl IntoIterator<Item = crate::SyntaxElement>,
     tail_expr: Option<ast::Expr>,
 ) -> ast::BlockExpr {
@@ -350,10 +350,17 @@ pub fn hacky_block_expr_with_comments(
     for node_or_token in elements.into_iter() {
         match node_or_token {
             rowan::NodeOrToken::Node(n) => format_to!(buf, "    {n}\n"),
-            rowan::NodeOrToken::Token(t) if t.kind() == SyntaxKind::COMMENT => {
-                format_to!(buf, "    {t}\n")
+            rowan::NodeOrToken::Token(t) => {
+                let kind = t.kind();
+                if kind == SyntaxKind::COMMENT {
+                    format_to!(buf, "    {t}\n")
+                } else if kind == SyntaxKind::WHITESPACE {
+                    let content = t.text().trim_matches(|c| c != '\n');
+                    if content.len() >= 1 {
+                        format_to!(buf, "{}", &content[1..])
+                    }
+                }
             }
-            _ => (),
         }
     }
     if let Some(tail_expr) = tail_expr {
@@ -719,12 +726,23 @@ pub fn param_list(
     ast_from_text(&list)
 }
 
-pub fn type_param(name: ast::Name, ty: Option<ast::TypeBoundList>) -> ast::TypeParam {
-    let bound = match ty {
-        Some(it) => format!(": {it}"),
-        None => String::new(),
-    };
-    ast_from_text(&format!("fn f<{name}{bound}>() {{ }}"))
+pub fn type_bound(bound: &str) -> ast::TypeBound {
+    ast_from_text(&format!("fn f<T: {bound}>() {{ }}"))
+}
+
+pub fn type_bound_list(
+    bounds: impl IntoIterator<Item = ast::TypeBound>,
+) -> Option<ast::TypeBoundList> {
+    let bounds = bounds.into_iter().map(|it| it.to_string()).unique().join(" + ");
+    if bounds.is_empty() {
+        return None;
+    }
+    Some(ast_from_text(&format!("fn f<T: {bounds}>() {{ }}")))
+}
+
+pub fn type_param(name: ast::Name, bounds: Option<ast::TypeBoundList>) -> ast::TypeParam {
+    let bounds = bounds.map_or_else(String::new, |it| format!(": {it}"));
+    ast_from_text(&format!("fn f<{name}{bounds}>() {{ }}"))
 }
 
 pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
new file mode 100644
index 00000000000..4ec388914e6
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
@@ -0,0 +1,328 @@
+//! Precedence representation.
+
+use crate::{
+    ast::{self, BinaryOp, Expr, HasArgList},
+    match_ast, AstNode, SyntaxNode,
+};
+
+impl Expr {
+    // Implementation is based on
+    // - https://doc.rust-lang.org/reference/expressions.html#expression-precedence
+    // - https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
+    // - rustc source, including, but not limited to
+    //   - https://github.com/rust-lang/rust/blob/b6852428a8ea9728369b64b9964cad8e258403d3/compiler/rustc_ast/src/util/parser.rs#L296
+
+    /// Returns `true` if `self` would need to be wrapped in parentheses given that its parent is `parent`.
+    pub fn needs_parens_in(&self, parent: SyntaxNode) -> bool {
+        match_ast! {
+            match parent {
+                ast::Expr(e) => self.needs_parens_in_expr(&e),
+                ast::Stmt(e) => self.needs_parens_in_stmt(Some(&e)),
+                ast::StmtList(_) => self.needs_parens_in_stmt(None),
+                ast::ArgList(_) => false,
+                ast::MatchArm(_) => false,
+                _ => false,
+            }
+        }
+    }
+
+    fn needs_parens_in_expr(&self, parent: &Expr) -> bool {
+        // Special-case block weirdness
+        if parent.child_is_followed_by_a_block() {
+            use Expr::*;
+            match self {
+                // Cases like `if return {}` (need parens or else `{}` is returned, instead of being `if`'s body)
+                ReturnExpr(e) if e.expr().is_none() => return true,
+                BreakExpr(e) if e.expr().is_none() => return true,
+                YieldExpr(e) if e.expr().is_none() => return true,
+
+                // Same but with `..{}`
+                RangeExpr(e) if matches!(e.end(), Some(BlockExpr(..))) => return true,
+
+                // Similarly with struct literals, e.g. `if S{} == 1 {}`
+                _ if self.contains_exterior_struct_lit() => return true,
+                _ => {}
+            }
+        }
+
+        // Special-case `return.f()`
+        if self.is_ret_like_with_no_value() && parent.is_postfix() {
+            return false;
+        }
+
+        if self.is_paren_like()
+            || parent.is_paren_like()
+            || self.is_prefix() && (parent.is_prefix() || !self.is_ordered_before(parent))
+            || self.is_postfix() && (parent.is_postfix() || self.is_ordered_before(parent))
+        {
+            return false;
+        }
+
+        let (left, right, inv) = match self.is_ordered_before(parent) {
+            true => (self, parent, false),
+            false => (parent, self, true),
+        };
+
+        let (_, left_right_bp) = left.binding_power();
+        let (right_left_bp, _) = right.binding_power();
+
+        (left_right_bp < right_left_bp) ^ inv
+    }
+
+    fn needs_parens_in_stmt(&self, stmt: Option<&ast::Stmt>) -> bool {
+        use Expr::*;
+
+        // Prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`,
+        // `{ { 0 } + 1 }` won't parse -- `{ 0 }` would be parsed as a self-contained stmt,
+        // leaving `+ 1` as a parse error.
+        let mut innermost = self.clone();
+        loop {
+            let next = match &innermost {
+                BinExpr(e) => e.lhs(),
+                CallExpr(e) => e.expr(),
+                CastExpr(e) => e.expr(),
+                IndexExpr(e) => e.base(),
+                _ => break,
+            };
+
+            if let Some(next) = next {
+                innermost = next;
+                if !innermost.requires_semi_to_be_stmt() {
+                    return true;
+                }
+            } else {
+                break;
+            }
+        }
+
+        // Not every expression can be followed by `else` in the `let-else`
+        if let Some(ast::Stmt::LetStmt(e)) = stmt {
+            if e.let_else().is_some() {
+                match self {
+                    BinExpr(e)
+                        if e.op_kind()
+                            .map(|op| matches!(op, BinaryOp::LogicOp(_)))
+                            .unwrap_or(false) =>
+                    {
+                        return true
+                    }
+                    _ if self.clone().trailing_brace().is_some() => return true,
+                    _ => {}
+                }
+            }
+        }
+
+        false
+    }
+
+    /// Returns left and right so-called "binding powers" of this expression.
+    fn binding_power(&self) -> (u8, u8) {
+        use ast::{ArithOp::*, BinaryOp::*, Expr::*, LogicOp::*};
+
+        match self {
+            // (0, 0)   -- paren-like/nullary
+            // (0, N)   -- prefix
+            // (N, 0)   -- postfix
+            // (N, N)   -- infix, requires parens
+            // (N, N+1) -- infix, left to right associative
+            // (N+1, N) -- infix, right to left associative
+            // N is odd
+            //
+            ContinueExpr(_) => (0, 0),
+
+            ClosureExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_) | BreakExpr(_) => (0, 1),
+
+            RangeExpr(_) => (5, 5),
+
+            BinExpr(e) => {
+                // Return a dummy value if we don't know the op
+                let Some(op) = e.op_kind() else { return (0, 0) };
+                match op {
+                    Assignment { .. } => (4, 3),
+                    //
+                    // Ranges are here in order :)
+                    //
+                    LogicOp(op) => match op {
+                        Or => (7, 8),
+                        And => (9, 10),
+                    },
+                    CmpOp(_) => (11, 11),
+                    ArithOp(op) => match op {
+                        BitOr => (13, 14),
+                        BitXor => (15, 16),
+                        BitAnd => (17, 18),
+                        Shl | Shr => (19, 20),
+                        Add | Sub => (21, 22),
+                        Mul | Div | Rem => (23, 24),
+                    },
+                }
+            }
+
+            CastExpr(_) => (25, 26),
+
+            BoxExpr(_) | RefExpr(_) | LetExpr(_) | PrefixExpr(_) => (0, 27),
+
+            AwaitExpr(_) | CallExpr(_) | MethodCallExpr(_) | IndexExpr(_) | TryExpr(_)
+            | MacroExpr(_) => (29, 0),
+
+            FieldExpr(_) => (31, 32),
+
+            ArrayExpr(_) | TupleExpr(_) | Literal(_) | PathExpr(_) | ParenExpr(_) | IfExpr(_)
+            | WhileExpr(_) | ForExpr(_) | LoopExpr(_) | MatchExpr(_) | BlockExpr(_)
+            | RecordExpr(_) | UnderscoreExpr(_) => (0, 0),
+        }
+    }
+
+    fn is_paren_like(&self) -> bool {
+        matches!(self.binding_power(), (0, 0))
+    }
+
+    fn is_prefix(&self) -> bool {
+        matches!(self.binding_power(), (0, 1..))
+    }
+
+    fn is_postfix(&self) -> bool {
+        matches!(self.binding_power(), (1.., 0))
+    }
+
+    /// Returns `true` if this expression can't be a standalone statement.
+    fn requires_semi_to_be_stmt(&self) -> bool {
+        use Expr::*;
+        !matches!(
+            self,
+            IfExpr(..) | MatchExpr(..) | BlockExpr(..) | WhileExpr(..) | LoopExpr(..) | ForExpr(..)
+        )
+    }
+
+    /// If an expression ends with `}`, returns the innermost expression ending in this `}`.
+    fn trailing_brace(mut self) -> Option<Expr> {
+        use Expr::*;
+
+        loop {
+            let rhs = match self {
+                RefExpr(e) => e.expr(),
+                BinExpr(e) => e.rhs(),
+                BoxExpr(e) => e.expr(),
+                BreakExpr(e) => e.expr(),
+                LetExpr(e) => e.expr(),
+                RangeExpr(e) => e.end(),
+                ReturnExpr(e) => e.expr(),
+                PrefixExpr(e) => e.expr(),
+                YieldExpr(e) => e.expr(),
+                ClosureExpr(e) => e.body(),
+
+                BlockExpr(..) | ForExpr(..) | IfExpr(..) | LoopExpr(..) | MatchExpr(..)
+                | RecordExpr(..) | WhileExpr(..) => break Some(self),
+                _ => break None,
+            };
+
+            self = rhs?;
+        }
+    }
+
+    /// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any
+    /// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and
+    /// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not.
+    fn contains_exterior_struct_lit(&self) -> bool {
+        return contains_exterior_struct_lit_inner(self).is_some();
+
+        fn contains_exterior_struct_lit_inner(expr: &Expr) -> Option<()> {
+            use Expr::*;
+
+            match expr {
+                RecordExpr(..) => Some(()),
+
+                // X { y: 1 } + X { y: 2 }
+                BinExpr(e) => e
+                    .lhs()
+                    .as_ref()
+                    .and_then(contains_exterior_struct_lit_inner)
+                    .or_else(|| e.rhs().as_ref().and_then(contains_exterior_struct_lit_inner)),
+
+                // `&X { y: 1 }`, `X { y: 1 }.y`, `X { y: 1 }.bar(...)`, etc
+                IndexExpr(e) => contains_exterior_struct_lit_inner(&e.base()?),
+                AwaitExpr(e) => contains_exterior_struct_lit_inner(&e.expr()?),
+                PrefixExpr(e) => contains_exterior_struct_lit_inner(&e.expr()?),
+                CastExpr(e) => contains_exterior_struct_lit_inner(&e.expr()?),
+                FieldExpr(e) => contains_exterior_struct_lit_inner(&e.expr()?),
+                MethodCallExpr(e) => contains_exterior_struct_lit_inner(&e.receiver()?),
+
+                _ => None,
+            }
+        }
+    }
+
+    /// Returns true if self is one of `return`, `break`, `continue` or `yield` with **no associated value**.
+    fn is_ret_like_with_no_value(&self) -> bool {
+        use Expr::*;
+
+        match self {
+            ReturnExpr(e) => e.expr().is_none(),
+            BreakExpr(e) => e.expr().is_none(),
+            ContinueExpr(_) => true,
+            YieldExpr(e) => e.expr().is_none(),
+            _ => false,
+        }
+    }
+
+    fn is_ordered_before(&self, other: &Expr) -> bool {
+        use Expr::*;
+
+        return order(self) < order(other);
+
+        /// Returns text range that can be used to compare two expression for order (which goes first).
+        fn order(this: &Expr) -> rowan::TextSize {
+            // For non-paren-like operators: get the operator itself
+            let token = match this {
+                RangeExpr(e) => e.op_token(),
+                BinExpr(e) => e.op_token(),
+                CastExpr(e) => e.as_token(),
+                FieldExpr(e) => e.dot_token(),
+                AwaitExpr(e) => e.dot_token(),
+                BoxExpr(e) => e.box_token(),
+                BreakExpr(e) => e.break_token(),
+                CallExpr(e) => e.arg_list().and_then(|args| args.l_paren_token()),
+                ClosureExpr(e) => e.param_list().and_then(|params| params.l_paren_token()),
+                ContinueExpr(e) => e.continue_token(),
+                IndexExpr(e) => e.l_brack_token(),
+                MethodCallExpr(e) => e.dot_token(),
+                PrefixExpr(e) => e.op_token(),
+                RefExpr(e) => e.amp_token(),
+                ReturnExpr(e) => e.return_token(),
+                TryExpr(e) => e.question_mark_token(),
+                YieldExpr(e) => e.yield_token(),
+                YeetExpr(e) => e.do_token(),
+                LetExpr(e) => e.let_token(),
+
+                ArrayExpr(_) | TupleExpr(_) | Literal(_) | PathExpr(_) | ParenExpr(_)
+                | IfExpr(_) | WhileExpr(_) | ForExpr(_) | LoopExpr(_) | MatchExpr(_)
+                | BlockExpr(_) | RecordExpr(_) | UnderscoreExpr(_) | MacroExpr(_) => None,
+            };
+
+            token.map(|t| t.text_range()).unwrap_or_else(|| this.syntax().text_range()).start()
+        }
+    }
+
+    fn child_is_followed_by_a_block(&self) -> bool {
+        use Expr::*;
+
+        match self {
+            ArrayExpr(_) | AwaitExpr(_) | BlockExpr(_) | CallExpr(_) | CastExpr(_)
+            | ClosureExpr(_) | FieldExpr(_) | IndexExpr(_) | Literal(_) | LoopExpr(_)
+            | MacroExpr(_) | MethodCallExpr(_) | ParenExpr(_) | PathExpr(_) | RecordExpr(_)
+            | TryExpr(_) | TupleExpr(_) | UnderscoreExpr(_) => false,
+
+            // For BinExpr and RangeExpr this is technically wrong -- the child can be on the left...
+            BinExpr(_) | RangeExpr(_) | BoxExpr(_) | BreakExpr(_) | ContinueExpr(_)
+            | PrefixExpr(_) | RefExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_)
+            | LetExpr(_) => self
+                .syntax()
+                .parent()
+                .and_then(Expr::cast)
+                .map(|e| e.child_is_followed_by_a_block())
+                .unwrap_or(false),
+
+            ForExpr(_) | IfExpr(_) | MatchExpr(_) | WhileExpr(_) => true,
+        }
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
index 8990f7a7d4e..2cd312e7f4f 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
@@ -436,9 +436,7 @@ mod tests {
 
     fn check_string_value<'a>(lit: &str, expected: impl Into<Option<&'a str>>) {
         assert_eq!(
-            ast::String { syntax: make::tokens::literal(&format!("\"{}\"", lit)) }
-                .value()
-                .as_deref(),
+            ast::String { syntax: make::tokens::literal(&format!("\"{lit}\"")) }.value().as_deref(),
             expected.into()
         );
     }
@@ -461,7 +459,7 @@ bcde", "abcde",
         expected: impl Into<Option<&'a [u8; N]>>,
     ) {
         assert_eq!(
-            ast::ByteString { syntax: make::tokens::literal(&format!("b\"{}\"", lit)) }
+            ast::ByteString { syntax: make::tokens::literal(&format!("b\"{lit}\"")) }
                 .value()
                 .as_deref(),
             expected.into().map(|value| &value[..])
@@ -483,7 +481,7 @@ bcde", b"abcde",
 
     #[test]
     fn test_value_underscores() {
-        check_float_value("3.141592653589793_f64", 3.141592653589793_f64);
+        check_float_value("1.234567891011121_f64", 1.234567891011121_f64);
         check_float_value("1__0.__0__f32", 10.0);
         check_int_value("0b__1_0_", 2);
         check_int_value("1_1_1_1_1_1", 111111);
diff --git a/src/tools/rust-analyzer/crates/syntax/src/fuzz.rs b/src/tools/rust-analyzer/crates/syntax/src/fuzz.rs
index 7c7a60d6299..239a89f9b2d 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/fuzz.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/fuzz.rs
@@ -36,7 +36,7 @@ impl CheckReparse {
         let delete_len = usize::from_str(lines.next()?).ok()?;
         let insert = lines.next()?.to_string();
         let text = lines.collect::<Vec<_>>().join("\n");
-        let text = format!("{}{}{}", PREFIX, text, SUFFIX);
+        let text = format!("{PREFIX}{text}{SUFFIX}");
         text.get(delete_start..delete_start.checked_add(delete_len)?)?; // make sure delete is a valid range
         let delete =
             TextRange::at(delete_start.try_into().unwrap(), delete_len.try_into().unwrap());
@@ -60,8 +60,8 @@ impl CheckReparse {
                 eprint!("reparsed:\n{:#?}", new_parse.tree().syntax());
                 eprint!("full reparse:\n{:#?}", full_reparse.tree().syntax());
                 assert_eq!(
-                    format!("{:?}", a),
-                    format!("{:?}", b),
+                    format!("{a:?}"),
+                    format!("{b:?}"),
                     "different syntax tree produced by the full reparse"
                 );
             }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/hacks.rs b/src/tools/rust-analyzer/crates/syntax/src/hacks.rs
index ec3d3d444c3..a3023c3195f 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/hacks.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/hacks.rs
@@ -6,7 +6,7 @@ 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 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;
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs
index a886972fff9..1d4a89201ae 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs
@@ -82,7 +82,7 @@ impl<N: AstNode> AstPtr<N> {
 
     /// Like `SyntaxNodePtr::cast` but the trait bounds work out.
     pub fn try_from_raw(raw: SyntaxNodePtr) -> Option<AstPtr<N>> {
-        N::can_cast(raw.kind()).then(|| AstPtr { raw, _ty: PhantomData })
+        N::can_cast(raw.kind()).then_some(AstPtr { raw, _ty: PhantomData })
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ted.rs b/src/tools/rust-analyzer/crates/syntax/src/ted.rs
index a47b4b11c0a..29788d05e84 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ted.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ted.rs
@@ -157,7 +157,7 @@ fn ws_before(position: &Position, new: &SyntaxElement) -> Option<SyntaxToken> {
         if let Some(item_list) = prev.parent().and_then(ast::ItemList::cast) {
             let mut indent = IndentLevel::from_element(&item_list.syntax().clone().into());
             indent.0 += 1;
-            return Some(make::tokens::whitespace(&format!("\n{}", indent)));
+            return Some(make::tokens::whitespace(&format!("\n{indent}")));
         }
     }
 
@@ -165,7 +165,7 @@ fn ws_before(position: &Position, new: &SyntaxElement) -> Option<SyntaxToken> {
         if let Some(stmt_list) = prev.parent().and_then(ast::StmtList::cast) {
             let mut indent = IndentLevel::from_element(&stmt_list.syntax().clone().into());
             indent.0 += 1;
-            return Some(make::tokens::whitespace(&format!("\n{}", indent)));
+            return Some(make::tokens::whitespace(&format!("\n{indent}")));
         }
     }
 
@@ -200,7 +200,7 @@ fn ws_between(left: &SyntaxElement, right: &SyntaxElement) -> Option<SyntaxToken
         if left.kind() == SyntaxKind::USE {
             indent.0 = IndentLevel::from_element(right).0.max(indent.0);
         }
-        return Some(make::tokens::whitespace(&format!("\n{}", indent)));
+        return Some(make::tokens::whitespace(&format!("\n{indent}")));
     }
     Some(make::tokens::single_space())
 }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests.rs b/src/tools/rust-analyzer/crates/syntax/src/tests.rs
index 58fba8cfa8f..168439053c2 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests.rs
@@ -108,7 +108,7 @@ fn self_hosting_parsing() {
             .into_iter()
             .map(|(path, err)| format!("{}: {:?}\n", path.display(), err[0]))
             .collect::<String>();
-        panic!("Parsing errors:\n{}\n", errors);
+        panic!("Parsing errors:\n{errors}\n");
     }
 }
 
@@ -157,7 +157,7 @@ fn collect_rust_files(root_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, String)>
 /// Collects paths to all `.rs` files from `dir` in a sorted `Vec<PathBuf>`.
 fn rust_files_in_dir(dir: &Path) -> Vec<PathBuf> {
     let mut acc = Vec::new();
-    for file in fs::read_dir(&dir).unwrap() {
+    for file in fs::read_dir(dir).unwrap() {
         let file = file.unwrap();
         let path = file.path();
         if path.extension().unwrap_or_default() == "rs" {
@@ -181,6 +181,6 @@ fn rust_files_in_dir(dir: &Path) -> Vec<PathBuf> {
 /// so this should always be correct.
 fn read_text(path: &Path) -> String {
     fs::read_to_string(path)
-        .unwrap_or_else(|_| panic!("File at {:?} should be valid", path))
+        .unwrap_or_else(|_| panic!("File at {path:?} should be valid"))
         .replace("\r\n", "\n")
 }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
index cf5be1c30fb..3ff6e03006b 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
@@ -65,12 +65,12 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
         (">>=", "SHREQ"),
     ],
     keywords: &[
-        "as", "async", "await", "box", "break", "const", "continue", "crate", "dyn", "else",
+        "as", "async", "await", "box", "break", "const", "continue", "crate", "do", "dyn", "else",
         "enum", "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "macro",
         "match", "mod", "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct",
         "super", "trait", "true", "try", "type", "unsafe", "use", "where", "while", "yield",
     ],
-    contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules"],
+    contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules", "yeet"],
     literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING"],
     tokens: &["ERROR", "IDENT", "WHITESPACE", "LIFETIME_IDENT", "COMMENT", "SHEBANG"],
     nodes: &[
@@ -142,6 +142,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
         "STMT_LIST",
         "RETURN_EXPR",
         "YIELD_EXPR",
+        "YEET_EXPR",
         "LET_EXPR",
         "UNDERSCORE_EXPR",
         "MACRO_EXPR",
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
index 712ef5f63b6..03aa2c451e8 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
@@ -253,7 +253,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> String {
                             matches!(kind, #(#kinds)|*)
                         }
                         fn cast(syntax: SyntaxNode) -> Option<Self> {
-                            Self::can_cast(syntax.kind()).then(|| #name { syntax })
+                            Self::can_cast(syntax.kind()).then_some(#name { syntax })
                         }
                         fn syntax(&self) -> &SyntaxNode {
                             &self.syntax
@@ -328,7 +328,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> String {
 
 fn write_doc_comment(contents: &[String], dest: &mut String) {
     for line in contents {
-        writeln!(dest, "///{}", line).unwrap();
+        writeln!(dest, "///{line}").unwrap();
     }
 }
 
@@ -501,7 +501,7 @@ fn to_pascal_case(s: &str) -> String {
 }
 
 fn pluralize(s: &str) -> String {
-    format!("{}s", s)
+    format!("{s}s")
 }
 
 impl Field {
@@ -637,7 +637,7 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r
             let mut name = grammar[*token].name.clone();
             if name != "int_number" && name != "string" {
                 if "[]{}()".contains(&name) {
-                    name = format!("'{}'", name);
+                    name = format!("'{name}'");
                 }
                 let field = Field::Token(name);
                 acc.push(field);
@@ -651,7 +651,7 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r
                 acc.push(field);
                 return;
             }
-            panic!("unhandled rule: {:?}", rule)
+            panic!("unhandled rule: {rule:?}")
         }
         Rule::Labeled { label: l, rule } => {
             assert!(label.is_none());
diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
index 1eea2346451..fb2381110bf 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
@@ -196,7 +196,7 @@ pub(crate) fn validate_block_structure(root: &SyntaxNode) {
 
 fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<SyntaxError>) {
     if let Some(int_token) = int_token(name_ref) {
-        if int_token.text().chars().any(|c| !c.is_digit(10)) {
+        if int_token.text().chars().any(|c| !c.is_ascii_digit()) {
             errors.push(SyntaxError::new(
                 "Tuple (struct) field access is only allowed through \
                 decimal integers with no underscores or suffix",