summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/syntax
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:58 +0000
commita4b7ed7a42c716ab9f05e351f003d589124fd55d (patch)
treeb620cd3f223850b28716e474e80c58059dca5dd4 /src/tools/rust-analyzer/crates/syntax
parentAdding upstream version 1.67.1+dfsg1. (diff)
downloadrustc-a4b7ed7a42c716ab9f05e351f003d589124fd55d.tar.xz
rustc-a4b7ed7a42c716ab9f05e351f003d589124fd55d.zip
Adding upstream version 1.68.2+dfsg1.upstream/1.68.2+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
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 0a0cb0290..2c67586a3 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 8b14789dd..c402a7bce 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 4aa64d0d6..10c045758 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 15805dfc8..5bc6b780e 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 660c057e9..a493c92e7 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 2ea715f47..a214a5e44 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 8c26009ad..d5b329698 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 000000000..4ec388914
--- /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 8990f7a7d..2cd312e7f 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 7c7a60d62..239a89f9b 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 ec3d3d444..a3023c319 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 a886972ff..1d4a89201 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 a47b4b11c..29788d05e 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 58fba8cfa..168439053 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 cf5be1c30..3ff6e0300 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 712ef5f63..03aa2c451 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 1eea23464..fb2381110 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",