about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorChayim Refael Friedman <chayimfr@gmail.com>2025-03-08 20:38:47 +0200
committerChayim Refael Friedman <chayimfr@gmail.com>2025-03-12 23:34:16 +0200
commitf335d00263fd429d12fd844134df54657357de56 (patch)
treef0a6868055073d9a8c4940a198ab3f61567a8771 /src
parent8148fa9324c274ae4be0e8f62a2d42adb8a371ff (diff)
downloadrust-f335d00263fd429d12fd844134df54657357de56.tar.gz
rust-f335d00263fd429d12fd844134df54657357de56.zip
Lower Return Type Notation (`Type::method(..): Send`)
We do it the way rustc does it, by only marking segments with it, and not the whole path. This will allow extending where it is allowed in the future.
Diffstat (limited to 'src')
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/generics.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path.rs29
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs26
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs131
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs17
5 files changed, 125 insertions, 80 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
index 6f1650adeb6..682f21ab3b4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
@@ -872,7 +872,7 @@ fn copy_generic_args(
             args,
             has_self_type: generic_args.has_self_type,
             bindings,
-            desugared_from_fn: generic_args.desugared_from_fn,
+            parenthesized: generic_args.parenthesized,
         }
     })
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
index 713e7389736..8f170fb983a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -79,6 +79,19 @@ thin_vec_with_header_struct! {
     }
 }
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum GenericArgsParentheses {
+    No,
+    /// Bounds of the form `Type::method(..): Send` or `impl Trait<method(..): Send>`,
+    /// aka. Return Type Notation or RTN.
+    ReturnTypeNotation,
+    /// `Fn`-family parenthesized traits, e.g. `impl Fn(u32) -> String`.
+    ///
+    /// This is desugared into one generic argument containing a tuple of all arguments,
+    /// and an associated type binding for `Output` for the return type.
+    ParenSugar,
+}
+
 /// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
 /// also includes bindings of associated types, like in `Iterator<Item = Foo>`.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -92,9 +105,8 @@ pub struct GenericArgs {
     pub has_self_type: bool,
     /// Associated type bindings like in `Iterator<Item = T>`.
     pub bindings: Box<[AssociatedTypeBinding]>,
-    /// Whether these generic args were desugared from `Trait(Arg) -> Output`
-    /// parenthesis notation typically used for the `Fn` traits.
-    pub desugared_from_fn: bool,
+    /// Whether these generic args were written with parentheses and how.
+    pub parenthesized: GenericArgsParentheses,
 }
 
 /// An associated type binding like in `Iterator<Item = T>`.
@@ -326,7 +338,16 @@ impl GenericArgs {
             args: Box::default(),
             has_self_type: false,
             bindings: Box::default(),
-            desugared_from_fn: false,
+            parenthesized: GenericArgsParentheses::No,
+        }
+    }
+
+    pub(crate) fn return_type_notation() -> GenericArgs {
+        GenericArgs {
+            args: Box::default(),
+            has_self_type: false,
+            bindings: Box::default(),
+            parenthesized: GenericArgsParentheses::ReturnTypeNotation,
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
index 7a6d6973298..c6ea3c4d710 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
@@ -13,7 +13,10 @@ use stdx::thin_vec::EmptyOptimizedThinVec;
 use syntax::ast::{self, AstNode, HasGenericArgs, HasTypeBounds};
 
 use crate::{
-    path::{AssociatedTypeBinding, GenericArg, GenericArgs, ModPath, Path, PathKind},
+    path::{
+        AssociatedTypeBinding, GenericArg, GenericArgs, GenericArgsParentheses, ModPath, Path,
+        PathKind,
+    },
     type_ref::{LifetimeRef, TypeBound, TypeRef},
 };
 
@@ -73,6 +76,9 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
                             segment.parenthesized_arg_list(),
                             segment.ret_type(),
                         )
+                    })
+                    .or_else(|| {
+                        segment.return_type_syntax().map(|_| GenericArgs::return_type_notation())
                     });
                 if args.is_some() {
                     generic_args.resize(segments.len(), None);
@@ -126,7 +132,7 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
 
                                 has_self_type: true,
                                 bindings: it.bindings.clone(),
-                                desugared_from_fn: it.desugared_from_fn,
+                                parenthesized: it.parenthesized,
                             },
                             None => GenericArgs {
                                 args: Box::new([self_type]),
@@ -281,7 +287,12 @@ pub(super) fn lower_generic_args(
                         let name = name_ref.as_name();
                         let args = assoc_type_arg
                             .generic_arg_list()
-                            .and_then(|args| lower_generic_args(lower_ctx, args));
+                            .and_then(|args| lower_generic_args(lower_ctx, args))
+                            .or_else(|| {
+                                assoc_type_arg
+                                    .return_type_syntax()
+                                    .map(|_| GenericArgs::return_type_notation())
+                            });
                         let type_ref =
                             assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it));
                         let type_ref = type_ref
@@ -315,7 +326,7 @@ pub(super) fn lower_generic_args(
         args: args.into_boxed_slice(),
         has_self_type: false,
         bindings: bindings.into_boxed_slice(),
-        desugared_from_fn: false,
+        parenthesized: GenericArgsParentheses::No,
     })
 }
 
@@ -353,5 +364,10 @@ fn lower_generic_args_from_fn_path(
             bounds: Box::default(),
         }])
     };
-    Some(GenericArgs { args, has_self_type: false, bindings, desugared_from_fn: true })
+    Some(GenericArgs {
+        args,
+        has_self_type: false,
+        bindings,
+        parenthesized: GenericArgsParentheses::ParenSugar,
+    })
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index 2ae7e746ba2..db305e98da4 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -2303,77 +2303,82 @@ impl HirDisplayWithTypesMap for Path {
             if let Some(generic_args) = segment.args_and_bindings {
                 // We should be in type context, so format as `Foo<Bar>` instead of `Foo::<Bar>`.
                 // Do we actually format expressions?
-                if generic_args.desugared_from_fn {
-                    // First argument will be a tuple, which already includes the parentheses.
-                    // If the tuple only contains 1 item, write it manually to avoid the trailing `,`.
-                    let tuple = match generic_args.args[0] {
-                        hir_def::path::GenericArg::Type(ty) => match &types_map[ty] {
-                            TypeRef::Tuple(it) => Some(it),
+                match generic_args.parenthesized {
+                    hir_def::path::GenericArgsParentheses::ReturnTypeNotation => {
+                        write!(f, "(..)")?;
+                    }
+                    hir_def::path::GenericArgsParentheses::ParenSugar => {
+                        // First argument will be a tuple, which already includes the parentheses.
+                        // If the tuple only contains 1 item, write it manually to avoid the trailing `,`.
+                        let tuple = match generic_args.args[0] {
+                            hir_def::path::GenericArg::Type(ty) => match &types_map[ty] {
+                                TypeRef::Tuple(it) => Some(it),
+                                _ => None,
+                            },
                             _ => None,
-                        },
-                        _ => None,
-                    };
-                    if let Some(v) = tuple {
-                        if v.len() == 1 {
-                            write!(f, "(")?;
-                            v[0].hir_fmt(f, types_map)?;
-                            write!(f, ")")?;
-                        } else {
-                            generic_args.args[0].hir_fmt(f, types_map)?;
+                        };
+                        if let Some(v) = tuple {
+                            if v.len() == 1 {
+                                write!(f, "(")?;
+                                v[0].hir_fmt(f, types_map)?;
+                                write!(f, ")")?;
+                            } else {
+                                generic_args.args[0].hir_fmt(f, types_map)?;
+                            }
                         }
-                    }
-                    if let Some(ret) = generic_args.bindings[0].type_ref {
-                        if !matches!(&types_map[ret], TypeRef::Tuple(v) if v.is_empty()) {
-                            write!(f, " -> ")?;
-                            ret.hir_fmt(f, types_map)?;
+                        if let Some(ret) = generic_args.bindings[0].type_ref {
+                            if !matches!(&types_map[ret], TypeRef::Tuple(v) if v.is_empty()) {
+                                write!(f, " -> ")?;
+                                ret.hir_fmt(f, types_map)?;
+                            }
                         }
                     }
-                    return Ok(());
-                }
-
-                let mut first = true;
-                // Skip the `Self` bound if exists. It's handled outside the loop.
-                for arg in &generic_args.args[generic_args.has_self_type as usize..] {
-                    if first {
-                        first = false;
-                        write!(f, "<")?;
-                    } else {
-                        write!(f, ", ")?;
-                    }
-                    arg.hir_fmt(f, types_map)?;
-                }
-                for binding in generic_args.bindings.iter() {
-                    if first {
-                        first = false;
-                        write!(f, "<")?;
-                    } else {
-                        write!(f, ", ")?;
-                    }
-                    write!(f, "{}", binding.name.display(f.db.upcast(), f.edition()))?;
-                    match &binding.type_ref {
-                        Some(ty) => {
-                            write!(f, " = ")?;
-                            ty.hir_fmt(f, types_map)?
+                    hir_def::path::GenericArgsParentheses::No => {
+                        let mut first = true;
+                        // Skip the `Self` bound if exists. It's handled outside the loop.
+                        for arg in &generic_args.args[generic_args.has_self_type as usize..] {
+                            if first {
+                                first = false;
+                                write!(f, "<")?;
+                            } else {
+                                write!(f, ", ")?;
+                            }
+                            arg.hir_fmt(f, types_map)?;
                         }
-                        None => {
-                            write!(f, ": ")?;
-                            f.write_joined(
-                                binding.bounds.iter().map(TypesMapAdapter::wrap(types_map)),
-                                " + ",
-                            )?;
+                        for binding in generic_args.bindings.iter() {
+                            if first {
+                                first = false;
+                                write!(f, "<")?;
+                            } else {
+                                write!(f, ", ")?;
+                            }
+                            write!(f, "{}", binding.name.display(f.db.upcast(), f.edition()))?;
+                            match &binding.type_ref {
+                                Some(ty) => {
+                                    write!(f, " = ")?;
+                                    ty.hir_fmt(f, types_map)?
+                                }
+                                None => {
+                                    write!(f, ": ")?;
+                                    f.write_joined(
+                                        binding.bounds.iter().map(TypesMapAdapter::wrap(types_map)),
+                                        " + ",
+                                    )?;
+                                }
+                            }
                         }
-                    }
-                }
 
-                // There may be no generic arguments to print, in case of a trait having only a
-                // single `Self` bound which is converted to `<Ty as Trait>::Assoc`.
-                if !first {
-                    write!(f, ">")?;
-                }
+                        // There may be no generic arguments to print, in case of a trait having only a
+                        // single `Self` bound which is converted to `<Ty as Trait>::Assoc`.
+                        if !first {
+                            write!(f, ">")?;
+                        }
 
-                // Current position: `<Ty as Trait<Args>|`
-                if generic_args.has_self_type {
-                    write!(f, ">")?;
+                        // Current position: `<Ty as Trait<Args>|`
+                        if generic_args.has_self_type {
+                            write!(f, ">")?;
+                        }
+                    }
                 }
             }
         }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
index a165932ddcc..58b143e84e0 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
@@ -8,7 +8,7 @@ use hir_def::{
     data::TraitFlags,
     expr_store::HygieneId,
     generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
-    path::{GenericArg, GenericArgs, Path, PathSegment, PathSegments},
+    path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments},
     resolver::{ResolveValueResult, TypeNs, ValueNs},
     type_ref::{TypeBound, TypeRef, TypesMap},
     GenericDefId, GenericParamId, ItemContainerId, Lookup, TraitId,
@@ -138,12 +138,15 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
 
     fn prohibit_parenthesized_generic_args(&mut self) -> bool {
         if let Some(generic_args) = self.current_or_prev_segment.args_and_bindings {
-            if generic_args.desugared_from_fn {
-                let segment = self.current_segment_u32();
-                self.on_diagnostic(
-                    PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment },
-                );
-                return true;
+            match generic_args.parenthesized {
+                GenericArgsParentheses::No => {}
+                GenericArgsParentheses::ReturnTypeNotation | GenericArgsParentheses::ParenSugar => {
+                    let segment = self.current_segment_u32();
+                    self.on_diagnostic(
+                        PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment },
+                    );
+                    return true;
+                }
             }
         }
         false