about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2025-03-25 09:31:34 +0000
committerGitHub <noreply@github.com>2025-03-25 09:31:34 +0000
commite142e4e9c9b8d6f2437e22c84741ec787eb4d101 (patch)
treea112919c67abc040f82c7430474d2aaed485788f /src
parent69471d6f0f18fa584607566a38aa6c4666863931 (diff)
parenta0f47f070efe585642e2f72eae03aba7e7e004ac (diff)
downloadrust-e142e4e9c9b8d6f2437e22c84741ec787eb4d101.tar.gz
rust-e142e4e9c9b8d6f2437e22c84741ec787eb4d101.zip
Merge pull request #19450 from Veykril/push-vwrmzqmnvlxk
minor: Simplify impl-ty parse validation
Diffstat (limited to 'src')
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/validation.rs123
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl.rast2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/dangling_impl_reference.rast2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/impl_trait_lifetime_only.rast2
4 files changed, 74 insertions, 55 deletions
diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
index 12894a2bda8..6397b04481f 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
@@ -4,6 +4,7 @@
 
 mod block;
 
+use itertools::Itertools;
 use rowan::Direction;
 use rustc_lexer::unescape::{self, Mode, unescape_mixed, unescape_unicode};
 
@@ -37,7 +38,8 @@ 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),
+                ast::DynTraitType(it) => errors.extend(validate_trait_object_ty(it)),
+                ast::ImplTraitType(it) => errors.extend(validate_impl_object_ty(it)),
                 _ => (),
             }
         }
@@ -316,87 +318,104 @@ fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxErro
 }
 
 fn validate_trait_object_ref_ty(ty: ast::RefType, errors: &mut Vec<SyntaxError>) {
-    if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
-        if let Some(err) = validate_trait_object_ty(ty) {
-            errors.push(err);
+    match ty.ty() {
+        Some(ast::Type::DynTraitType(ty)) => {
+            if let Some(err) = validate_trait_object_ty_plus(ty) {
+                errors.push(err);
+            }
+        }
+        Some(ast::Type::ImplTraitType(ty)) => {
+            if let Some(err) = validate_impl_object_ty_plus(ty) {
+                errors.push(err);
+            }
         }
+        _ => (),
     }
 }
 
 fn validate_trait_object_ptr_ty(ty: ast::PtrType, errors: &mut Vec<SyntaxError>) {
-    if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
-        if let Some(err) = validate_trait_object_ty(ty) {
-            errors.push(err);
+    match ty.ty() {
+        Some(ast::Type::DynTraitType(ty)) => {
+            if let Some(err) = validate_trait_object_ty_plus(ty) {
+                errors.push(err);
+            }
         }
+        Some(ast::Type::ImplTraitType(ty)) => {
+            if let Some(err) = validate_impl_object_ty_plus(ty) {
+                errors.push(err);
+            }
+        }
+        _ => (),
     }
 }
 
 fn validate_trait_object_fn_ptr_ret_ty(ty: ast::FnPtrType, errors: &mut Vec<SyntaxError>) {
-    if let Some(ast::Type::DynTraitType(ty)) = ty.ret_type().and_then(|ty| ty.ty()) {
-        if let Some(err) = validate_trait_object_ty(ty) {
-            errors.push(err);
+    match ty.ret_type().and_then(|ty| ty.ty()) {
+        Some(ast::Type::DynTraitType(ty)) => {
+            if let Some(err) = validate_trait_object_ty_plus(ty) {
+                errors.push(err);
+            }
+        }
+        Some(ast::Type::ImplTraitType(ty)) => {
+            if let Some(err) = validate_impl_object_ty_plus(ty) {
+                errors.push(err);
+            }
         }
+        _ => (),
     }
 }
 
 fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> {
     let tbl = ty.type_bound_list()?;
-    let bounds_count = tbl.bounds().count();
+    let no_bounds = tbl.bounds().filter_map(|it| it.ty()).next().is_none();
 
-    match bounds_count {
-        0 => Some(SyntaxError::new(
+    match no_bounds {
+        true => 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,
+        false => None,
     }
 }
 
-fn validate_impl_object_ty(ty: ast::ImplTraitType, errors: &mut Vec<SyntaxError>) {
-    let Some(bound_list) = ty.type_bound_list() else {
-        errors.push(SyntaxError::new(
-            "At least one trait must be specified",
-            ty.syntax().text_range(),
-        ));
-        return;
-    };
-
-    let bounds: Vec<_> = bound_list.bounds().collect();
+fn validate_impl_object_ty(ty: ast::ImplTraitType) -> Option<SyntaxError> {
+    let tbl = ty.type_bound_list()?;
+    let no_bounds = tbl.bounds().filter_map(|it| it.ty()).next().is_none();
 
-    if !bounds.iter().any(|b| !matches!(b.kind(), ast::TypeBoundKind::Lifetime(_))) {
-        errors.push(SyntaxError::new(
-            "At least one trait must be specified",
+    match no_bounds {
+        true => Some(SyntaxError::new(
+            "At least one trait is required for an object type",
             ty.syntax().text_range(),
-        ));
-        return;
+        )),
+        false => None,
     }
+}
 
-    if bounds.len() == 1 {
-        return;
+// FIXME: This is not a validation error, this is a context dependent parse error
+fn validate_trait_object_ty_plus(ty: ast::DynTraitType) -> Option<SyntaxError> {
+    let dyn_token = ty.dyn_token()?;
+    let preceding_token = algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?;
+    let tbl = ty.type_bound_list()?;
+    let more_than_one_bound = tbl.bounds().next_tuple::<(_, _)>().is_some();
+
+    if more_than_one_bound && !matches!(preceding_token.kind(), T!['('] | T![<] | T![=]) {
+        Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()))
+    } else {
+        None
     }
+}
 
-    let Some(preceding_token) = ty
-        .impl_token()
-        .and_then(|token| token.prev_token())
-        .and_then(|prev| algo::skip_trivia_token(prev, Direction::Prev))
-    else {
-        return;
-    };
+// FIXME: This is not a validation error, this is a context dependent parse error
+fn validate_impl_object_ty_plus(ty: ast::ImplTraitType) -> Option<SyntaxError> {
+    let dyn_token = ty.impl_token()?;
+    let preceding_token = algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?;
+    let tbl = ty.type_bound_list()?;
+    let more_than_one_bound = tbl.bounds().next_tuple::<(_, _)>().is_some();
 
-    if !matches!(preceding_token.kind(), T!['('] | T![<] | T![=])
-        && matches!(preceding_token.kind(), T![&])
-    {
-        errors.push(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()));
+    if more_than_one_bound && !matches!(preceding_token.kind(), T!['('] | T![<] | T![=]) {
+        Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()))
+    } else {
+        None
     }
 }
 
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
index 2db07ae12a9..c337ee8bbf4 100644
--- 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
@@ -20,4 +20,4 @@ SOURCE_FILE@0..16
       STMT_LIST@14..16
         L_CURLY@14..15 "{"
         R_CURLY@15..16 "}"
-error 8..12: At least one trait must be specified
+error 8..12: At least one trait is required for an object type
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
index dbe6535ac66..cb73cb4e058 100644
--- 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
@@ -22,4 +22,4 @@ SOURCE_FILE@0..17
       STMT_LIST@15..17
         L_CURLY@15..16 "{"
         R_CURLY@16..17 "}"
-error 9..13: At least one trait must be specified
+error 9..13: At least one trait is required for an object type
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/impl_trait_lifetime_only.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/impl_trait_lifetime_only.rast
index 98aa862bc88..ed938d63db4 100644
--- a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/impl_trait_lifetime_only.rast
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/impl_trait_lifetime_only.rast
@@ -26,4 +26,4 @@ SOURCE_FILE@0..20
       STMT_LIST@18..20
         L_CURLY@18..19 "{"
         R_CURLY@19..20 "}"
-error 9..16: At least one trait must be specified
+error 9..16: At least one trait is required for an object type