diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/syntax')
11 files changed, 234 insertions, 17 deletions
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 88d7beb897e..70a91af6c47 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -279,6 +279,11 @@ Adt = | Struct | Union +VariantDef = + Struct +| Union +| Variant + Const = Attr* Visibility? 'default'? 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 58c76a456ab..ebee5e9af2c 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 @@ -2447,6 +2447,17 @@ pub enum UseBoundGenericArg { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum VariantDef { + Struct(Struct), + Union(Union), + Variant(Variant), +} +impl ast::HasAttrs for VariantDef {} +impl ast::HasDocComments for VariantDef {} +impl ast::HasName for VariantDef {} +impl ast::HasVisibility for VariantDef {} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AnyHasArgList { pub(crate) syntax: SyntaxNode, } @@ -6738,6 +6749,40 @@ impl AstNode for UseBoundGenericArg { } } } +impl From<Struct> for VariantDef { + #[inline] + fn from(node: Struct) -> VariantDef { VariantDef::Struct(node) } +} +impl From<Union> for VariantDef { + #[inline] + fn from(node: Union) -> VariantDef { VariantDef::Union(node) } +} +impl From<Variant> for VariantDef { + #[inline] + fn from(node: Variant) -> VariantDef { VariantDef::Variant(node) } +} +impl AstNode for VariantDef { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, STRUCT | UNION | VARIANT) } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + let res = match syntax.kind() { + STRUCT => VariantDef::Struct(Struct { syntax }), + UNION => VariantDef::Union(Union { syntax }), + VARIANT => VariantDef::Variant(Variant { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + VariantDef::Struct(it) => &it.syntax, + VariantDef::Union(it) => &it.syntax, + VariantDef::Variant(it) => &it.syntax, + } + } +} impl AnyHasArgList { #[inline] pub fn new<T: ast::HasArgList>(node: T) -> AnyHasArgList { @@ -7753,6 +7798,11 @@ impl std::fmt::Display for UseBoundGenericArg { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for VariantDef { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for Abi { 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/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs index 0c4da762992..4f0e2cad174 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs @@ -1,5 +1,7 @@ //! Precedence representation. +use stdx::always; + use crate::{ ast::{self, BinaryOp, Expr, HasArgList, RangeItem}, match_ast, AstNode, SyntaxNode, @@ -140,6 +142,22 @@ pub fn precedence(expr: &ast::Expr) -> ExprPrecedence { } } +fn check_ancestry(ancestor: &SyntaxNode, descendent: &SyntaxNode) -> bool { + let bail = || always!(false, "{} is not an ancestor of {}", ancestor, descendent); + + if !ancestor.text_range().contains_range(descendent.text_range()) { + return bail(); + } + + for anc in descendent.ancestors() { + if anc == *ancestor { + return true; + } + } + + bail() +} + impl Expr { pub fn precedence(&self) -> ExprPrecedence { precedence(self) @@ -153,9 +171,19 @@ impl Expr { /// 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 { + self.needs_parens_in_place_of(parent, self.syntax()) + } + + /// Returns `true` if `self` would need to be wrapped in parentheses if it replaces `place_of` + /// given that `place_of`'s parent is `parent`. + pub fn needs_parens_in_place_of(&self, parent: &SyntaxNode, place_of: &SyntaxNode) -> bool { + if !check_ancestry(parent, place_of) { + return false; + } + match_ast! { match parent { - ast::Expr(e) => self.needs_parens_in_expr(&e), + ast::Expr(e) => self.needs_parens_in_expr(&e, place_of), ast::Stmt(e) => self.needs_parens_in_stmt(Some(&e)), ast::StmtList(_) => self.needs_parens_in_stmt(None), ast::ArgList(_) => false, @@ -165,7 +193,7 @@ impl Expr { } } - fn needs_parens_in_expr(&self, parent: &Expr) -> bool { + fn needs_parens_in_expr(&self, parent: &Expr, place_of: &SyntaxNode) -> bool { // Parentheses are necessary when calling a function-like pointer that is a member of a struct or union // (e.g. `(a.f)()`). let is_parent_call_expr = matches!(parent, ast::Expr::CallExpr(_)); @@ -199,13 +227,17 @@ impl Expr { 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)) + || self.is_prefix() + && (parent.is_prefix() + || !self.is_ordered_before_parent_in_place_of(parent, place_of)) + || self.is_postfix() + && (parent.is_postfix() + || self.is_ordered_before_parent_in_place_of(parent, place_of)) { return false; } - let (left, right, inv) = match self.is_ordered_before(parent) { + let (left, right, inv) = match self.is_ordered_before_parent_in_place_of(parent, place_of) { true => (self, parent, false), false => (parent, self, true), }; @@ -413,13 +445,28 @@ impl Expr { } } - fn is_ordered_before(&self, other: &Expr) -> bool { + fn is_ordered_before_parent_in_place_of(&self, parent: &Expr, place_of: &SyntaxNode) -> bool { + use rowan::TextSize; use Expr::*; - return order(self) < order(other); + let self_range = self.syntax().text_range(); + let place_of_range = place_of.text_range(); + + let self_order_adjusted = order(self) - self_range.start() + place_of_range.start(); + + let parent_order = order(parent); + let parent_order_adjusted = if parent_order <= place_of_range.start() { + parent_order + } else if parent_order >= place_of_range.end() { + parent_order - place_of_range.len() + self_range.len() + } else { + return false; + }; + + return self_order_adjusted < parent_order_adjusted; /// Returns text range that can be used to compare two expression for order (which goes first). - fn order(this: &Expr) -> rowan::TextSize { + fn order(this: &Expr) -> TextSize { // For non-paren-like operators: get the operator itself let token = match this { RangeExpr(e) => e.op_token(), diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 85393ca5b4c..44f13041c24 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -783,6 +783,27 @@ impl SyntaxFactory { ast } + pub fn record_expr_field( + &self, + name: ast::NameRef, + expr: Option<ast::Expr>, + ) -> ast::RecordExprField { + let ast = make::record_expr_field(name.clone(), expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + + builder.map_node(name.syntax().clone(), ast.name_ref().unwrap().syntax().clone()); + if let Some(expr) = expr { + builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone()); + } + + builder.finish(&mut mapping); + } + + ast + } + pub fn record_field_list( &self, fields: impl IntoIterator<Item = ast::RecordField>, diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs index 13d352d3c69..85eefac734b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs @@ -37,6 +37,7 @@ pub(crate) fn validate(root: &SyntaxNode, errors: &mut Vec<SyntaxError>) { ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, errors), ast::MacroRules(it) => validate_macro_rules(it, errors), ast::LetExpr(it) => validate_let_expr(it, errors), + ast::ImplTraitType(it) => validate_impl_object_ty(it, errors), _ => (), } } @@ -340,17 +341,34 @@ fn validate_trait_object_fn_ptr_ret_ty(ty: ast::FnPtrType, errors: &mut Vec<Synt fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> { let tbl = ty.type_bound_list()?; - - if tbl.bounds().count() > 1 { - let dyn_token = ty.dyn_token()?; - let potential_parenthesis = - algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?; - let kind = potential_parenthesis.kind(); - if !matches!(kind, T!['('] | T![<] | T![=]) { - return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range())); + let bounds_count = tbl.bounds().count(); + + match bounds_count { + 0 => Some(SyntaxError::new( + "At least one trait is required for an object type", + ty.syntax().text_range(), + )), + _ if bounds_count > 1 => { + let dyn_token = ty.dyn_token()?; + let preceding_token = + algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?; + + if !matches!(preceding_token.kind(), T!['('] | T![<] | T![=]) { + return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range())); + } + None } + _ => None, + } +} + +fn validate_impl_object_ty(ty: ast::ImplTraitType, errors: &mut Vec<SyntaxError>) { + if ty.type_bound_list().map_or(0, |tbl| tbl.bounds().count()) == 0 { + errors.push(SyntaxError::new( + "At least one trait must be specified", + ty.syntax().text_range(), + )); } - None } fn validate_macro_rules(mac: ast::MacroRules, errors: &mut Vec<SyntaxError>) { diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rast new file mode 100644 index 00000000000..b31af5fbc6a --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rast @@ -0,0 +1,25 @@ +SOURCE_FILE@0..16 + FN@0..16 + FN_KW@0..2 "fn" + WHITESPACE@2..3 " " + NAME@3..4 + IDENT@3..4 "f" + PARAM_LIST@4..13 + L_PAREN@4..5 "(" + PARAM@5..12 + WILDCARD_PAT@5..6 + UNDERSCORE@5..6 "_" + COLON@6..7 ":" + WHITESPACE@7..8 " " + REF_TYPE@8..12 + AMP@8..9 "&" + DYN_TRAIT_TYPE@9..12 + DYN_KW@9..12 "dyn" + TYPE_BOUND_LIST@12..12 + R_PAREN@12..13 ")" + WHITESPACE@13..14 " " + BLOCK_EXPR@14..16 + STMT_LIST@14..16 + L_CURLY@14..15 "{" + R_CURLY@15..16 "}" +error 9..12: At least one trait is required for an object type diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rs new file mode 100644 index 00000000000..2fdbb42846b --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rs @@ -0,0 +1 @@ +fn f(_: &dyn) {} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rast new file mode 100644 index 00000000000..2db07ae12a9 --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rast @@ -0,0 +1,23 @@ +SOURCE_FILE@0..16 + FN@0..16 + FN_KW@0..2 "fn" + WHITESPACE@2..3 " " + NAME@3..4 + IDENT@3..4 "f" + PARAM_LIST@4..13 + L_PAREN@4..5 "(" + PARAM@5..12 + WILDCARD_PAT@5..6 + UNDERSCORE@5..6 "_" + COLON@6..7 ":" + WHITESPACE@7..8 " " + IMPL_TRAIT_TYPE@8..12 + IMPL_KW@8..12 "impl" + TYPE_BOUND_LIST@12..12 + R_PAREN@12..13 ")" + WHITESPACE@13..14 " " + BLOCK_EXPR@14..16 + STMT_LIST@14..16 + L_CURLY@14..15 "{" + R_CURLY@15..16 "}" +error 8..12: At least one trait must be specified diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rs new file mode 100644 index 00000000000..61706d9e412 --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rs @@ -0,0 +1 @@ +fn f(_: impl) {} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rast new file mode 100644 index 00000000000..dbe6535ac66 --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rast @@ -0,0 +1,25 @@ +SOURCE_FILE@0..17 + FN@0..17 + FN_KW@0..2 "fn" + WHITESPACE@2..3 " " + NAME@3..4 + IDENT@3..4 "f" + PARAM_LIST@4..14 + L_PAREN@4..5 "(" + PARAM@5..13 + WILDCARD_PAT@5..6 + UNDERSCORE@5..6 "_" + COLON@6..7 ":" + WHITESPACE@7..8 " " + REF_TYPE@8..13 + AMP@8..9 "&" + IMPL_TRAIT_TYPE@9..13 + IMPL_KW@9..13 "impl" + TYPE_BOUND_LIST@13..13 + R_PAREN@13..14 ")" + WHITESPACE@14..15 " " + BLOCK_EXPR@15..17 + STMT_LIST@15..17 + L_CURLY@15..16 "{" + R_CURLY@16..17 "}" +error 9..13: At least one trait must be specified diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rs new file mode 100644 index 00000000000..0b440b4c5af --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rs @@ -0,0 +1 @@ +fn f(_: &impl) {} \ No newline at end of file |
