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`, + /// 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`). This /// also includes bindings of associated types, like in `Iterator`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -92,9 +105,8 @@ pub struct GenericArgs { pub has_self_type: bool, /// Associated type bindings like in `Iterator`. 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`. @@ -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,78 +2303,83 @@ impl HirDisplayWithTypesMap for Path { if let Some(generic_args) = segment.args_and_bindings { // We should be in type context, so format as `Foo` instead of `Foo::`. // 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)?; + 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)?; + } + 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)), + " + ", + )?; + } + } } - } - 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)? + // There may be no generic arguments to print, in case of a trait having only a + // single `Self` bound which is converted to `::Assoc`. + if !first { + write!(f, ">")?; } - None => { - write!(f, ": ")?; - f.write_joined( - binding.bounds.iter().map(TypesMapAdapter::wrap(types_map)), - " + ", - )?; + + // Current position: `|` + if generic_args.has_self_type { + 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 `::Assoc`. - if !first { - write!(f, ">")?; - } - - // Current position: `|` - 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