about summary refs log tree commit diff
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/rust.ungram5
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs50
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs63
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs21
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/validation.rs36
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rast25
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0224_dangling_dyn.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rast23
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rast25
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rs1
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