Unnamed repository; edit this file 'description' to name the repository.
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.
Chayim Refael Friedman 2025-03-13
parent 3fc655b · commit eaa0a39
-rw-r--r--crates/hir-def/src/generics.rs2
-rw-r--r--crates/hir-def/src/path.rs29
-rw-r--r--crates/hir-def/src/path/lower.rs26
-rw-r--r--crates/hir-ty/src/display.rs131
-rw-r--r--crates/hir-ty/src/lower/path.rs17
5 files changed, 125 insertions, 80 deletions
diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs
index 6f1650adeb..682f21ab3b 100644
--- a/crates/hir-def/src/generics.rs
+++ b/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/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs
index 713e738973..8f170fb983 100644
--- a/crates/hir-def/src/path.rs
+++ b/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/crates/hir-def/src/path/lower.rs b/crates/hir-def/src/path/lower.rs
index 7a6d697329..c6ea3c4d71 100644
--- a/crates/hir-def/src/path/lower.rs
+++ b/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/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 2ae7e746ba..db305e98da 100644
--- a/crates/hir-ty/src/display.rs
+++ b/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/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs
index a165932ddc..58b143e84e 100644
--- a/crates/hir-ty/src/lower/path.rs
+++ b/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