summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/syntax
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/syntax')
-rw-r--r--src/tools/rust-analyzer/crates/syntax/Cargo.toml14
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs191
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs93
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs42
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs22
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs3
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/lib.rs87
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs12
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ptr.rs26
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests.rs6
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs4
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/token_text.rs2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/utils.rs42
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/validation.rs2
16 files changed, 398 insertions, 158 deletions
diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
index 5ee0c4792..7a7c0d267 100644
--- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
@@ -14,16 +14,16 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
-either = "1.7.0"
-itertools = "0.10.5"
-rowan = "0.15.11"
+either.workspace = true
+itertools.workspace = true
+rowan = "0.15.15"
rustc-hash = "1.1.0"
once_cell = "1.17.0"
-indexmap = "2.0.0"
+indexmap.workspace = true
smol_str.workspace = true
triomphe.workspace = true
-rustc_lexer.workspace = true
+rustc-dependencies.workspace = true
parser.workspace = true
profile.workspace = true
@@ -31,7 +31,7 @@ stdx.workspace = true
text-edit.workspace = true
[dev-dependencies]
-rayon = "1.6.1"
+rayon.workspace = true
expect-test = "1.4.0"
proc-macro2 = "1.0.47"
quote = "1.0.20"
@@ -41,4 +41,4 @@ test-utils.workspace = true
sourcegen.workspace = true
[features]
-in-rust-tree = []
+in-rust-tree = ["rustc-dependencies/in-rust-tree"]
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 3603560d3..c3010d090 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -36,7 +36,7 @@ PathSegment =
'::'? NameRef
| NameRef GenericArgList?
| NameRef ParamList RetType?
-| '<' PathType ('as' PathType)? '>'
+| '<' Type ('as' PathType)? '>'
GenericArgList =
'::'? '<' (GenericArg (',' GenericArg)* ','?)? '>'
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 a150d9e6c..37d821204 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
@@ -3,18 +3,17 @@
use std::iter::{empty, successors};
use parser::{SyntaxKind, T};
-use rowan::SyntaxElement;
use crate::{
algo::{self, neighbor},
ast::{self, edit::IndentLevel, make, HasGenericParams},
ted::{self, Position},
- AstNode, AstToken, Direction,
+ AstNode, AstToken, Direction, SyntaxElement,
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
SyntaxNode, SyntaxToken,
};
-use super::HasName;
+use super::{HasArgList, HasName};
pub trait GenericParamsOwnerEdit: ast::HasGenericParams {
fn get_or_create_generic_param_list(&self) -> ast::GenericParamList;
@@ -224,7 +223,7 @@ pub trait AttrsOwnerEdit: ast::HasAttrs {
let after_attrs_and_comments = node
.children_with_tokens()
.find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR))
- .map_or(Position::first_child_of(node), |it| Position::before(it));
+ .map_or(Position::first_child_of(node), Position::before);
ted::insert_all(
after_attrs_and_comments,
@@ -362,6 +361,24 @@ impl ast::PathSegment {
}
}
+impl ast::MethodCallExpr {
+ pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
+ if self.generic_arg_list().is_none() {
+ let generic_arg_list = make::turbofish_generic_arg_list(empty()).clone_for_update();
+
+ if let Some(arg_list) = self.arg_list() {
+ ted::insert_raw(
+ ted::Position::before(arg_list.syntax()),
+ generic_arg_list.syntax(),
+ );
+ } else {
+ ted::append_child(self.syntax(), generic_arg_list.syntax());
+ }
+ }
+ self.generic_arg_list().unwrap()
+ }
+}
+
impl Removable for ast::UseTree {
fn remove(&self) {
for dir in [Direction::Next, Direction::Prev] {
@@ -433,7 +450,9 @@ impl ast::UseTree {
if &path == prefix && self.use_tree_list().is_none() {
if self.star_token().is_some() {
// path$0::* -> *
- self.coloncolon_token().map(ted::remove);
+ if let Some(a) = self.coloncolon_token() {
+ ted::remove(a)
+ }
ted::remove(prefix.syntax());
} else {
// path$0 -> self
@@ -460,7 +479,9 @@ impl ast::UseTree {
for p in successors(parent.parent_path(), |it| it.parent_path()) {
p.segment()?;
}
- prefix.parent_path().and_then(|p| p.coloncolon_token()).map(ted::remove);
+ if let Some(a) = prefix.parent_path().and_then(|p| p.coloncolon_token()) {
+ ted::remove(a)
+ }
ted::remove(prefix.syntax());
Some(())
}
@@ -555,7 +576,7 @@ impl ast::AssocItemList {
None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"),
},
};
- let elements: Vec<SyntaxElement<_>> = vec![
+ let elements: Vec<SyntaxElement> = vec![
make::tokens::whitespace(&format!("{whitespace}{indent}")).into(),
item.syntax().clone().into(),
];
@@ -625,6 +646,50 @@ impl ast::MatchArmList {
}
}
+impl ast::LetStmt {
+ pub fn set_ty(&self, ty: Option<ast::Type>) {
+ match ty {
+ None => {
+ if let Some(colon_token) = self.colon_token() {
+ ted::remove(colon_token);
+ }
+
+ if let Some(existing_ty) = self.ty() {
+ if let Some(sibling) = existing_ty.syntax().prev_sibling_or_token() {
+ if sibling.kind() == SyntaxKind::WHITESPACE {
+ ted::remove(sibling);
+ }
+ }
+
+ ted::remove(existing_ty.syntax());
+ }
+
+ // Remove any trailing ws
+ if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE)
+ {
+ last.detach();
+ }
+ }
+ Some(new_ty) => {
+ if self.colon_token().is_none() {
+ ted::insert_raw(
+ Position::after(
+ self.pat().expect("let stmt should have a pattern").syntax(),
+ ),
+ make::token(T![:]),
+ );
+ }
+
+ if let Some(old_ty) = self.ty() {
+ ted::replace(old_ty.syntax(), new_ty.syntax());
+ } else {
+ ted::insert(Position::after(self.colon_token().unwrap()), new_ty.syntax());
+ }
+ }
+ }
+ }
+}
+
impl ast::RecordExprFieldList {
pub fn add_field(&self, field: ast::RecordExprField) {
let is_multiline = self.syntax().text().contains_char('\n');
@@ -749,7 +814,7 @@ impl ast::VariantList {
None => (IndentLevel::single(), Position::last_child_of(self.syntax())),
},
};
- let elements: Vec<SyntaxElement<_>> = vec![
+ let elements: Vec<SyntaxElement> = vec![
make::tokens::whitespace(&format!("{}{indent}", "\n")).into(),
variant.syntax().clone().into(),
ast::make::token(T![,]).into(),
@@ -784,6 +849,53 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
Some(())
}
+impl ast::IdentPat {
+ pub fn set_pat(&self, pat: Option<ast::Pat>) {
+ match pat {
+ None => {
+ if let Some(at_token) = self.at_token() {
+ // Remove `@ Pat`
+ let start = at_token.clone().into();
+ let end = self
+ .pat()
+ .map(|it| it.syntax().clone().into())
+ .unwrap_or_else(|| at_token.into());
+
+ ted::remove_all(start..=end);
+
+ // Remove any trailing ws
+ if let Some(last) =
+ self.syntax().last_token().filter(|it| it.kind() == WHITESPACE)
+ {
+ last.detach();
+ }
+ }
+ }
+ Some(pat) => {
+ if let Some(old_pat) = self.pat() {
+ // Replace existing pattern
+ ted::replace(old_pat.syntax(), pat.syntax())
+ } else if let Some(at_token) = self.at_token() {
+ // Have an `@` token but not a pattern yet
+ ted::insert(ted::Position::after(at_token), pat.syntax());
+ } else {
+ // Don't have an `@`, should have a name
+ let name = self.name().unwrap();
+
+ ted::insert_all(
+ ted::Position::after(name.syntax()),
+ vec![
+ make::token(T![@]).into(),
+ make::tokens::single_space().into(),
+ pat.syntax().clone().into(),
+ ],
+ )
+ }
+ }
+ }
+ }
+}
+
pub trait HasVisibilityEdit: ast::HasVisibility {
fn set_visibility(&self, visbility: ast::Visibility) {
match self.visibility() {
@@ -886,6 +998,65 @@ mod tests {
}
#[test]
+ fn test_ident_pat_set_pat() {
+ #[track_caller]
+ fn check(before: &str, expected: &str, pat: Option<ast::Pat>) {
+ let pat = pat.map(|it| it.clone_for_update());
+
+ let ident_pat = ast_mut_from_text::<ast::IdentPat>(&format!("fn f() {{ {before} }}"));
+ ident_pat.set_pat(pat);
+
+ let after = ast_mut_from_text::<ast::IdentPat>(&format!("fn f() {{ {expected} }}"));
+ assert_eq!(ident_pat.to_string(), after.to_string());
+ }
+
+ // replacing
+ check("let a @ _;", "let a @ ();", Some(make::tuple_pat([]).into()));
+
+ // note: no trailing semicolon is added for the below tests since it
+ // seems to be picked up by the ident pat during error recovery?
+
+ // adding
+ check("let a ", "let a @ ()", Some(make::tuple_pat([]).into()));
+ check("let a @ ", "let a @ ()", Some(make::tuple_pat([]).into()));
+
+ // removing
+ check("let a @ ()", "let a", None);
+ check("let a @ ", "let a", None);
+ }
+
+ #[test]
+ fn test_let_stmt_set_ty() {
+ #[track_caller]
+ fn check(before: &str, expected: &str, ty: Option<ast::Type>) {
+ let ty = ty.map(|it| it.clone_for_update());
+
+ let let_stmt = ast_mut_from_text::<ast::LetStmt>(&format!("fn f() {{ {before} }}"));
+ let_stmt.set_ty(ty);
+
+ let after = ast_mut_from_text::<ast::LetStmt>(&format!("fn f() {{ {expected} }}"));
+ assert_eq!(let_stmt.to_string(), after.to_string(), "{let_stmt:#?}\n!=\n{after:#?}");
+ }
+
+ // adding
+ check("let a;", "let a: ();", Some(make::ty_tuple([])));
+ // no semicolon due to it being eaten during error recovery
+ check("let a:", "let a: ()", Some(make::ty_tuple([])));
+
+ // replacing
+ check("let a: u8;", "let a: ();", Some(make::ty_tuple([])));
+ check("let a: u8 = 3;", "let a: () = 3;", Some(make::ty_tuple([])));
+ check("let a: = 3;", "let a: () = 3;", Some(make::ty_tuple([])));
+
+ // removing
+ check("let a: u8;", "let a;", None);
+ check("let a:;", "let a;", None);
+
+ check("let a: u8 = 3;", "let a = 3;", None);
+ check("let a: = 3;", "let a = 3;", None);
+ }
+
+ #[test]
fn add_variant_to_empty_enum() {
let variant = make::variant(make::name("Bar"), None).clone_for_update();
@@ -976,7 +1147,9 @@ enum Foo {
fn check_add_variant(before: &str, expected: &str, variant: ast::Variant) {
let enum_ = ast_mut_from_text::<ast::Enum>(before);
- enum_.variant_list().map(|it| it.add_variant(variant));
+ if let Some(it) = enum_.variant_list() {
+ it.add_variant(variant)
+ }
let after = enum_.to_string();
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 7ba0d4dc6..6c86e5910 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
@@ -59,8 +59,9 @@ impl PathSegment {
pub fn param_list(&self) -> Option<ParamList> { support::child(&self.syntax) }
pub fn ret_type(&self) -> Option<RetType> { support::child(&self.syntax) }
pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
- pub fn path_type(&self) -> Option<PathType> { support::child(&self.syntax) }
+ pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
pub fn as_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![as]) }
+ pub fn path_type(&self) -> Option<PathType> { support::child(&self.syntax) }
pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
}
@@ -1577,14 +1578,6 @@ impl RecordPatField {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum GenericArg {
- TypeArg(TypeArg),
- AssocTypeArg(AssocTypeArg),
- LifetimeArg(LifetimeArg),
- ConstArg(ConstArg),
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Type {
ArrayType(ArrayType),
DynTraitType(DynTraitType),
@@ -1603,6 +1596,14 @@ pub enum Type {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum GenericArg {
+ TypeArg(TypeArg),
+ AssocTypeArg(AssocTypeArg),
+ LifetimeArg(LifetimeArg),
+ ConstArg(ConstArg),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expr {
ArrayExpr(ArrayExpr),
AsmExpr(AsmExpr),
@@ -3319,41 +3320,6 @@ impl AstNode for RecordPatField {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
-impl From<TypeArg> for GenericArg {
- fn from(node: TypeArg) -> GenericArg { GenericArg::TypeArg(node) }
-}
-impl From<AssocTypeArg> for GenericArg {
- fn from(node: AssocTypeArg) -> GenericArg { GenericArg::AssocTypeArg(node) }
-}
-impl From<LifetimeArg> for GenericArg {
- fn from(node: LifetimeArg) -> GenericArg { GenericArg::LifetimeArg(node) }
-}
-impl From<ConstArg> for GenericArg {
- fn from(node: ConstArg) -> GenericArg { GenericArg::ConstArg(node) }
-}
-impl AstNode for GenericArg {
- fn can_cast(kind: SyntaxKind) -> bool {
- matches!(kind, TYPE_ARG | ASSOC_TYPE_ARG | LIFETIME_ARG | CONST_ARG)
- }
- fn cast(syntax: SyntaxNode) -> Option<Self> {
- let res = match syntax.kind() {
- TYPE_ARG => GenericArg::TypeArg(TypeArg { syntax }),
- ASSOC_TYPE_ARG => GenericArg::AssocTypeArg(AssocTypeArg { syntax }),
- LIFETIME_ARG => GenericArg::LifetimeArg(LifetimeArg { syntax }),
- CONST_ARG => GenericArg::ConstArg(ConstArg { syntax }),
- _ => return None,
- };
- Some(res)
- }
- fn syntax(&self) -> &SyntaxNode {
- match self {
- GenericArg::TypeArg(it) => &it.syntax,
- GenericArg::AssocTypeArg(it) => &it.syntax,
- GenericArg::LifetimeArg(it) => &it.syntax,
- GenericArg::ConstArg(it) => &it.syntax,
- }
- }
-}
impl From<ArrayType> for Type {
fn from(node: ArrayType) -> Type { Type::ArrayType(node) }
}
@@ -3455,6 +3421,41 @@ impl AstNode for Type {
}
}
}
+impl From<TypeArg> for GenericArg {
+ fn from(node: TypeArg) -> GenericArg { GenericArg::TypeArg(node) }
+}
+impl From<AssocTypeArg> for GenericArg {
+ fn from(node: AssocTypeArg) -> GenericArg { GenericArg::AssocTypeArg(node) }
+}
+impl From<LifetimeArg> for GenericArg {
+ fn from(node: LifetimeArg) -> GenericArg { GenericArg::LifetimeArg(node) }
+}
+impl From<ConstArg> for GenericArg {
+ fn from(node: ConstArg) -> GenericArg { GenericArg::ConstArg(node) }
+}
+impl AstNode for GenericArg {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ matches!(kind, TYPE_ARG | ASSOC_TYPE_ARG | LIFETIME_ARG | CONST_ARG)
+ }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ TYPE_ARG => GenericArg::TypeArg(TypeArg { syntax }),
+ ASSOC_TYPE_ARG => GenericArg::AssocTypeArg(AssocTypeArg { syntax }),
+ LIFETIME_ARG => GenericArg::LifetimeArg(LifetimeArg { syntax }),
+ CONST_ARG => GenericArg::ConstArg(ConstArg { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ GenericArg::TypeArg(it) => &it.syntax,
+ GenericArg::AssocTypeArg(it) => &it.syntax,
+ GenericArg::LifetimeArg(it) => &it.syntax,
+ GenericArg::ConstArg(it) => &it.syntax,
+ }
+ }
+}
impl From<ArrayExpr> for Expr {
fn from(node: ArrayExpr) -> Expr { Expr::ArrayExpr(node) }
}
@@ -4340,12 +4341,12 @@ impl AstNode for AnyHasVisibility {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
-impl std::fmt::Display for GenericArg {
+impl std::fmt::Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
-impl std::fmt::Display for Type {
+impl std::fmt::Display for GenericArg {
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 17e311c0c..ad63cc558 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -433,7 +433,6 @@ pub fn record_field(
ast_from_text(&format!("struct S {{ {visibility}{name}: {ty}, }}"))
}
-// TODO
pub fn block_expr(
stmts: impl IntoIterator<Item = ast::Stmt>,
tail_expr: Option<ast::Expr>,
@@ -853,6 +852,10 @@ pub fn self_param() -> ast::SelfParam {
ast_from_text("fn f(&self) { }")
}
+pub fn mut_self_param() -> ast::SelfParam {
+ ast_from_text("fn f(&mut self) { }")
+}
+
pub fn ret_type(ty: ast::Type) -> ast::RetType {
ast_from_text(&format!("fn f() -> {ty} {{ }}"))
}
@@ -938,6 +941,13 @@ pub fn lifetime_arg(lifetime: ast::Lifetime) -> ast::LifetimeArg {
ast_from_text(&format!("const S: T<{lifetime}> = ();"))
}
+pub fn turbofish_generic_arg_list(
+ args: impl IntoIterator<Item = ast::GenericArg>,
+) -> ast::GenericArgList {
+ let args = args.into_iter().join(", ");
+ ast_from_text(&format!("const S: T::<{args}> = ();"))
+}
+
pub(crate) fn generic_arg_list(
args: impl IntoIterator<Item = ast::GenericArg>,
) -> ast::GenericArgList {
@@ -973,6 +983,11 @@ pub fn tuple_field(visibility: Option<ast::Visibility>, ty: ast::Type) -> ast::T
ast_from_text(&format!("struct f({visibility}{ty});"))
}
+pub fn variant_list(variants: impl IntoIterator<Item = ast::Variant>) -> ast::VariantList {
+ let variants = variants.into_iter().join(", ");
+ ast_from_text(&format!("enum f {{ {variants} }}"))
+}
+
pub fn variant(name: ast::Name, field_list: Option<ast::FieldList>) -> ast::Variant {
let field_list = match field_list {
None => String::new(),
@@ -1037,6 +1052,19 @@ pub fn struct_(
ast_from_text(&format!("{visibility}struct {strukt_name}{type_params}{field_list}{semicolon}",))
}
+pub fn enum_(
+ visibility: Option<ast::Visibility>,
+ enum_name: ast::Name,
+ variant_list: ast::VariantList,
+) -> ast::Enum {
+ let visibility = match visibility {
+ None => String::new(),
+ Some(it) => format!("{it} "),
+ };
+
+ ast_from_text(&format!("{visibility}enum {enum_name} {variant_list}"))
+}
+
pub fn attr_outer(meta: ast::Meta) -> ast::Attr {
ast_from_text(&format!("#[{meta}]"))
}
@@ -1105,7 +1133,7 @@ pub mod tokens {
pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| {
SourceFile::parse(
- "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n",
+ "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let a @ [] })\n;\n\n",
)
});
@@ -1149,6 +1177,16 @@ pub mod tokens {
lit.syntax().first_child_or_token().unwrap().into_token().unwrap()
}
+ pub fn ident(text: &str) -> SyntaxToken {
+ assert_eq!(text.trim(), text);
+ let path: ast::Path = super::ext::ident_path(text);
+ path.syntax()
+ .descendants_with_tokens()
+ .filter_map(|it| it.into_token())
+ .find(|it| it.kind() == IDENT)
+ .unwrap()
+ }
+
pub fn single_newline() -> SyntaxToken {
let res = SOURCE_FILE
.tree()
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
index 691d0c618..f81dff884 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
@@ -275,10 +275,19 @@ impl ast::Path {
successors(Some(self.clone()), ast::Path::qualifier).last().unwrap()
}
+ pub fn first_qualifier(&self) -> Option<ast::Path> {
+ successors(self.qualifier(), ast::Path::qualifier).last()
+ }
+
pub fn first_segment(&self) -> Option<ast::PathSegment> {
self.first_qualifier_or_self().segment()
}
+ // FIXME: Check usages of Self::segments, they might be wrong because of the logic of the bloew function
+ pub fn segments_of_this_path_only_rev(&self) -> impl Iterator<Item = ast::PathSegment> + Clone {
+ self.qualifiers_and_self().filter_map(|it| it.segment())
+ }
+
pub fn segments(&self) -> impl Iterator<Item = ast::PathSegment> + Clone {
successors(self.first_segment(), |p| {
p.parent_path().parent_path().and_then(|p| p.segment())
@@ -289,6 +298,10 @@ impl ast::Path {
successors(self.qualifier(), |p| p.qualifier())
}
+ pub fn qualifiers_and_self(&self) -> impl Iterator<Item = ast::Path> + Clone {
+ successors(Some(self.clone()), |p| p.qualifier())
+ }
+
pub fn top_path(&self) -> ast::Path {
let mut this = self.clone();
while let Some(path) = this.parent_path() {
@@ -361,6 +374,15 @@ impl ast::Impl {
}
}
+// [#15778](https://github.com/rust-lang/rust-analyzer/issues/15778)
+impl ast::PathSegment {
+ pub fn qualifying_trait(&self) -> Option<ast::PathType> {
+ let mut path_types = support::children(self.syntax());
+ let first = path_types.next()?;
+ path_types.next().or(Some(first))
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StructKind {
Record(ast::RecordFieldList),
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 87fd51d70..d5d565a01 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
@@ -2,6 +2,8 @@
use std::borrow::Cow;
+use rustc_dependencies::lexer as rustc_lexer;
+
use rustc_lexer::unescape::{
unescape_byte, unescape_c_string, unescape_char, unescape_literal, CStrUnit, Mode,
};
@@ -119,6 +121,7 @@ impl ast::Whitespace {
}
}
+#[derive(Debug)]
pub struct QuoteOffsets {
pub quotes: (TextRange, TextRange),
pub contents: TextRange,
@@ -165,6 +168,11 @@ pub trait IsString: AstToken {
fn text_range_between_quotes(&self) -> Option<TextRange> {
self.quote_offsets().map(|it| it.contents)
}
+ fn text_without_quotes(&self) -> &str {
+ let text = self.text();
+ let Some(offsets) = self.text_range_between_quotes() else { return text };
+ &text[offsets - self.syntax().text_range().start()]
+ }
fn open_quote_text_range(&self) -> Option<TextRange> {
self.quote_offsets().map(|it| it.quotes.0)
}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
index 3e43df2d0..16f7356b1 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs
@@ -76,9 +76,6 @@ pub trait HasDocComments: HasAttrs {
fn doc_comments(&self) -> DocCommentIter {
DocCommentIter { iter: self.syntax().children_with_tokens() }
}
- fn doc_comments_and_attrs(&self) -> AttrDocCommentIter {
- AttrDocCommentIter { iter: self.syntax().children_with_tokens() }
- }
}
impl DocCommentIter {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
index 27c8a13e5..d60069804 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
@@ -19,7 +19,8 @@
//! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256>
//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md>
-#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
+#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
#[allow(unused)]
macro_rules! eprintln {
@@ -74,7 +75,7 @@ pub use smol_str::SmolStr;
#[derive(Debug, PartialEq, Eq)]
pub struct Parse<T> {
green: GreenNode,
- errors: Arc<Vec<SyntaxError>>,
+ errors: Option<Arc<[SyntaxError]>>,
_ty: PhantomData<fn() -> T>,
}
@@ -86,14 +87,18 @@ impl<T> Clone for Parse<T> {
impl<T> Parse<T> {
fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> {
- Parse { green, errors: Arc::new(errors), _ty: PhantomData }
+ Parse {
+ green,
+ errors: if errors.is_empty() { None } else { Some(errors.into()) },
+ _ty: PhantomData,
+ }
}
pub fn syntax_node(&self) -> SyntaxNode {
SyntaxNode::new_root(self.green.clone())
}
pub fn errors(&self) -> &[SyntaxError] {
- &self.errors
+ self.errors.as_deref().unwrap_or_default()
}
}
@@ -106,11 +111,10 @@ impl<T: AstNode> Parse<T> {
T::cast(self.syntax_node()).unwrap()
}
- pub fn ok(self) -> Result<T, Arc<Vec<SyntaxError>>> {
- if self.errors.is_empty() {
- Ok(self.tree())
- } else {
- Err(self.errors)
+ pub fn ok(self) -> Result<T, Arc<[SyntaxError]>> {
+ match self.errors {
+ Some(e) => Err(e),
+ None => Ok(self.tree()),
}
}
}
@@ -128,7 +132,7 @@ impl Parse<SyntaxNode> {
impl Parse<SourceFile> {
pub fn debug_dump(&self) -> String {
let mut buf = format!("{:#?}", self.tree().syntax());
- for err in self.errors.iter() {
+ for err in self.errors.as_deref().into_iter().flat_map(<[_]>::iter) {
format_to!(buf, "error {:?}: {}\n", err.range(), err);
}
buf
@@ -140,13 +144,16 @@ impl Parse<SourceFile> {
fn incremental_reparse(&self, indel: &Indel) -> Option<Parse<SourceFile>> {
// FIXME: validation errors are not handled here
- parsing::incremental_reparse(self.tree().syntax(), indel, self.errors.to_vec()).map(
- |(green_node, errors, _reparsed_range)| Parse {
- green: green_node,
- errors: Arc::new(errors),
- _ty: PhantomData,
- },
+ parsing::incremental_reparse(
+ self.tree().syntax(),
+ indel,
+ self.errors.as_deref().unwrap_or_default().iter().cloned(),
)
+ .map(|(green_node, errors, _reparsed_range)| Parse {
+ green: green_node,
+ errors: if errors.is_empty() { None } else { Some(errors.into()) },
+ _ty: PhantomData,
+ })
}
fn full_reparse(&self, indel: &Indel) -> Parse<SourceFile> {
@@ -167,7 +174,11 @@ impl SourceFile {
errors.extend(validation::validate(&root));
assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
- Parse { green, errors: Arc::new(errors), _ty: PhantomData }
+ Parse {
+ green,
+ errors: if errors.is_empty() { None } else { Some(errors.into()) },
+ _ty: PhantomData,
+ }
}
}
@@ -181,29 +192,27 @@ impl ast::TokenTree {
let kind = t.kind();
if kind.is_trivia() {
was_joint = false
+ } else if kind == SyntaxKind::IDENT {
+ let token_text = t.text();
+ let contextual_kw =
+ SyntaxKind::from_contextual_keyword(token_text).unwrap_or(SyntaxKind::IDENT);
+ parser_input.push_ident(contextual_kw);
} else {
- if kind == SyntaxKind::IDENT {
- let token_text = t.text();
- let contextual_kw = SyntaxKind::from_contextual_keyword(token_text)
- .unwrap_or(SyntaxKind::IDENT);
- parser_input.push_ident(contextual_kw);
- } else {
- if was_joint {
+ if was_joint {
+ parser_input.was_joint();
+ }
+ parser_input.push(kind);
+ // Tag the token as joint if it is float with a fractional part
+ // we use this jointness to inform the parser about what token split
+ // event to emit when we encounter a float literal in a field access
+ if kind == SyntaxKind::FLOAT_NUMBER {
+ if !t.text().ends_with('.') {
parser_input.was_joint();
- }
- parser_input.push(kind);
- // Tag the token as joint if it is float with a fractional part
- // we use this jointness to inform the parser about what token split
- // event to emit when we encounter a float literal in a field access
- if kind == SyntaxKind::FLOAT_NUMBER {
- if !t.text().ends_with('.') {
- parser_input.was_joint();
- } else {
- was_joint = false;
- }
} else {
- was_joint = true;
+ was_joint = false;
}
+ } else {
+ was_joint = true;
}
}
}
@@ -276,7 +285,11 @@ impl ast::TokenTree {
let (green, errors) = builder.finish_raw();
- Parse { green, errors: Arc::new(errors), _ty: PhantomData }
+ Parse {
+ green,
+ errors: if errors.is_empty() { None } else { Some(errors.into()) },
+ _ty: PhantomData,
+ }
}
}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
index 45e591609..0ddc64171 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
@@ -20,7 +20,7 @@ use crate::{
pub(crate) fn incremental_reparse(
node: &SyntaxNode,
edit: &Indel,
- errors: Vec<SyntaxError>,
+ errors: impl IntoIterator<Item = SyntaxError>,
) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
if let Some((green, new_errors, old_range)) = reparse_token(node, edit) {
return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
@@ -147,7 +147,7 @@ fn is_balanced(lexed: &parser::LexedStr<'_>) -> bool {
}
fn merge_errors(
- old_errors: Vec<SyntaxError>,
+ old_errors: impl IntoIterator<Item = SyntaxError>,
new_errors: Vec<SyntaxError>,
range_before_reparse: TextRange,
edit: &Indel,
@@ -191,8 +191,12 @@ mod tests {
let fully_reparsed = SourceFile::parse(&after);
let incrementally_reparsed: Parse<SourceFile> = {
let before = SourceFile::parse(&before);
- let (green, new_errors, range) =
- incremental_reparse(before.tree().syntax(), &edit, before.errors.to_vec()).unwrap();
+ let (green, new_errors, range) = incremental_reparse(
+ before.tree().syntax(),
+ &edit,
+ before.errors.as_deref().unwrap_or_default().iter().cloned(),
+ )
+ .unwrap();
assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length");
Parse::new(green, new_errors)
};
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs
index 1d4a89201..8750147ee 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs
@@ -22,12 +22,18 @@ use crate::{syntax_node::RustLanguage, AstNode, SyntaxNode};
pub type SyntaxNodePtr = rowan::ast::SyntaxNodePtr<RustLanguage>;
/// Like `SyntaxNodePtr`, but remembers the type of node.
-#[derive(Debug)]
pub struct AstPtr<N: AstNode> {
raw: SyntaxNodePtr,
_ty: PhantomData<fn() -> N>,
}
+impl<N: AstNode + std::fmt::Debug> std::fmt::Debug for AstPtr<N> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_tuple("AstPtr").field(&self.raw).finish()
+ }
+}
+
+impl<N: AstNode> Copy for AstPtr<N> {}
impl<N: AstNode> Clone for AstPtr<N> {
fn clone(&self) -> AstPtr<N> {
AstPtr { raw: self.raw.clone(), _ty: PhantomData }
@@ -73,6 +79,10 @@ impl<N: AstNode> AstPtr<N> {
Some(AstPtr { raw: self.raw, _ty: PhantomData })
}
+ pub fn kind(&self) -> parser::SyntaxKind {
+ self.raw.kind()
+ }
+
pub fn upcast<M: AstNode>(self) -> AstPtr<M>
where
N: Into<M>,
@@ -84,6 +94,20 @@ impl<N: AstNode> AstPtr<N> {
pub fn try_from_raw(raw: SyntaxNodePtr) -> Option<AstPtr<N>> {
N::can_cast(raw.kind()).then_some(AstPtr { raw, _ty: PhantomData })
}
+
+ pub fn wrap_left<R>(self) -> AstPtr<either::Either<N, R>>
+ where
+ either::Either<N, R>: AstNode,
+ {
+ AstPtr { raw: self.raw, _ty: PhantomData }
+ }
+
+ pub fn wrap_right<L>(self) -> AstPtr<either::Either<L, N>>
+ where
+ either::Either<L, N>: AstNode,
+ {
+ AstPtr { raw: self.raw, _ty: PhantomData }
+ }
}
impl<N: AstNode> From<AstPtr<N>> for SyntaxNodePtr {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests.rs b/src/tools/rust-analyzer/crates/syntax/src/tests.rs
index 168439053..8ae1242cf 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests.rs
@@ -17,11 +17,11 @@ use crate::{ast, fuzz, AstNode, SourceFile, SyntaxError};
#[test]
fn parse_smoke_test() {
- let code = r##"
+ let code = r#"
fn main() {
println!("Hello, world!")
}
- "##;
+ "#;
let parse = SourceFile::parse(code);
// eprintln!("{:#?}", parse.syntax_node());
@@ -38,7 +38,7 @@ fn benchmark_parser() {
let tree = {
let _b = bench("parsing");
let p = SourceFile::parse(&data);
- assert!(p.errors.is_empty());
+ assert!(p.errors.is_none());
assert_eq!(p.tree().syntax.text_range().len(), 352474.into());
p.tree()
};
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 dc6c96343..c2e921e4b 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
@@ -622,7 +622,7 @@ fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {
}
fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, rule: &Rule) {
- if lower_seperated_list(acc, grammar, label, rule) {
+ if lower_separated_list(acc, grammar, label, rule) {
return;
}
@@ -688,7 +688,7 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r
}
// (T (',' T)* ','?)
-fn lower_seperated_list(
+fn lower_separated_list(
acc: &mut Vec<Field>,
grammar: &Grammar,
label: Option<&String>,
diff --git a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs b/src/tools/rust-analyzer/crates/syntax/src/token_text.rs
index 09c080c0c..e69deb49c 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/token_text.rs
@@ -13,7 +13,7 @@ pub(crate) enum Repr<'a> {
}
impl<'a> TokenText<'a> {
- pub(crate) fn borrowed(text: &'a str) -> Self {
+ pub fn borrowed(text: &'a str) -> Self {
TokenText(Repr::Borrowed(text))
}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/utils.rs b/src/tools/rust-analyzer/crates/syntax/src/utils.rs
index 25f34ea9d..a38f8b2b5 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/utils.rs
@@ -1,48 +1,8 @@
//! A set of utils methods to reuse on other abstraction levels
-use itertools::Itertools;
-
-use crate::{ast, match_ast, AstNode, SyntaxKind};
-
-pub fn path_to_string_stripping_turbo_fish(path: &ast::Path) -> String {
- path.syntax()
- .children()
- .filter_map(|node| {
- match_ast! {
- match node {
- ast::PathSegment(it) => {
- Some(it.name_ref()?.to_string())
- },
- ast::Path(it) => {
- Some(path_to_string_stripping_turbo_fish(&it))
- },
- _ => None,
- }
- }
- })
- .join("::")
-}
+use crate::SyntaxKind;
pub fn is_raw_identifier(name: &str) -> bool {
let is_keyword = SyntaxKind::from_keyword(name).is_some();
is_keyword && !matches!(name, "self" | "crate" | "super" | "Self")
}
-
-#[cfg(test)]
-mod tests {
- use super::path_to_string_stripping_turbo_fish;
- use crate::ast::make;
-
- #[test]
- fn turbofishes_are_stripped() {
- assert_eq!("Vec", path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>")),);
- assert_eq!(
- "Vec::new",
- path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>::new")),
- );
- assert_eq!(
- "Vec::new",
- path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::new()")),
- );
- }
-}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
index e0ec6a242..2b1bbac08 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
@@ -5,7 +5,7 @@
mod block;
use rowan::Direction;
-use rustc_lexer::unescape::{self, unescape_literal, Mode};
+use rustc_dependencies::lexer::unescape::{self, unescape_literal, Mode};
use crate::{
algo,