Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/lower.rs')
-rw-r--r--crates/hir-ty/src/lower.rs187
1 files changed, 155 insertions, 32 deletions
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index fc0af0cec9..b73967be71 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -102,6 +102,31 @@ impl ImplTraitLoweringState {
}
}
+type TypeSource = Either<TypeRefId, hir_def::type_ref::TypeSource>;
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct TyLoweringDiagnostic {
+ pub source: TypeSource,
+ pub kind: TyLoweringDiagnosticKind,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum TyLoweringDiagnosticKind {
+ GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason },
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum GenericArgsProhibitedReason {
+ Module,
+ TyParam,
+ SelfTy,
+ PrimitiveTy,
+ /// When there is a generic enum, within the expression `Enum::Variant`,
+ /// either `Enum` or `Variant` are allowed to have generic arguments, but not both.
+ // FIXME: This is not used now but it should be.
+ EnumVariant,
+}
+
#[derive(Debug)]
pub struct TyLoweringContext<'a> {
pub db: &'a dyn HirDatabase,
@@ -125,6 +150,7 @@ pub struct TyLoweringContext<'a> {
expander: Option<Expander>,
/// Tracks types with explicit `?Sized` bounds.
pub(crate) unsized_types: FxHashSet<Ty>,
+ pub(crate) diagnostics: Vec<TyLoweringDiagnostic>,
}
impl<'a> TyLoweringContext<'a> {
@@ -159,6 +185,7 @@ impl<'a> TyLoweringContext<'a> {
type_param_mode,
expander: None,
unsized_types: FxHashSet::default(),
+ diagnostics: Vec::new(),
}
}
@@ -198,6 +225,20 @@ impl<'a> TyLoweringContext<'a> {
self.type_param_mode = type_param_mode;
self
}
+
+ pub fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) {
+ let source = match self.types_source_map {
+ Some(source_map) => {
+ let Ok(source) = source_map.type_syntax(type_ref) else {
+ stdx::never!("error in synthetic type");
+ return;
+ };
+ Either::Right(source)
+ }
+ None => Either::Left(type_ref),
+ };
+ self.diagnostics.push(TyLoweringDiagnostic { source, kind });
+ }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
@@ -464,6 +505,7 @@ impl<'a> TyLoweringContext<'a> {
impl_trait_mode: mem::take(&mut self.impl_trait_mode),
expander: self.expander.take(),
unsized_types: mem::take(&mut self.unsized_types),
+ diagnostics: mem::take(&mut self.diagnostics),
};
let ty = inner_ctx.lower_ty(type_ref);
@@ -471,6 +513,7 @@ impl<'a> TyLoweringContext<'a> {
self.impl_trait_mode = inner_ctx.impl_trait_mode;
self.expander = inner_ctx.expander;
self.unsized_types = inner_ctx.unsized_types;
+ self.diagnostics = inner_ctx.diagnostics;
self.expander.as_mut().unwrap().exit(mark);
Some(ty)
@@ -542,6 +585,10 @@ impl<'a> TyLoweringContext<'a> {
resolved_segment: PathSegment<'_>,
remaining_segments: PathSegments<'_>,
infer_args: bool,
+ on_prohibited_generics_for_resolved_segment: &mut dyn FnMut(
+ &mut Self,
+ GenericArgsProhibitedReason,
+ ),
) -> (Ty, Option<TypeNs>) {
let ty = match resolution {
TypeNs::TraitId(trait_) => {
@@ -608,28 +655,44 @@ impl<'a> TyLoweringContext<'a> {
// FIXME(trait_alias): Implement trait alias.
return (TyKind::Error.intern(Interner), None);
}
- TypeNs::GenericParam(param_id) => match self.type_param_mode {
- ParamLoweringMode::Placeholder => {
- TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
+ TypeNs::GenericParam(param_id) => {
+ if resolved_segment.args_and_bindings.is_some() {
+ on_prohibited_generics_for_resolved_segment(
+ self,
+ GenericArgsProhibitedReason::TyParam,
+ );
}
- ParamLoweringMode::Variable => {
- let idx = match self
- .generics()
- .expect("generics in scope")
- .type_or_const_param_idx(param_id.into())
- {
- None => {
- never!("no matching generics");
- return (TyKind::Error.intern(Interner), None);
- }
- Some(idx) => idx,
- };
- TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
+ match self.type_param_mode {
+ ParamLoweringMode::Placeholder => {
+ TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
+ }
+ ParamLoweringMode::Variable => {
+ let idx = match self
+ .generics()
+ .expect("generics in scope")
+ .type_or_const_param_idx(param_id.into())
+ {
+ None => {
+ never!("no matching generics");
+ return (TyKind::Error.intern(Interner), None);
+ }
+ Some(idx) => idx,
+ };
+
+ TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
+ }
}
+ .intern(Interner)
}
- .intern(Interner),
TypeNs::SelfType(impl_id) => {
+ if resolved_segment.args_and_bindings.is_some() {
+ on_prohibited_generics_for_resolved_segment(
+ self,
+ GenericArgsProhibitedReason::SelfTy,
+ );
+ }
+
let generics = self.generics().expect("impl should have generic param scope");
match self.type_param_mode {
@@ -655,6 +718,13 @@ impl<'a> TyLoweringContext<'a> {
}
}
TypeNs::AdtSelfType(adt) => {
+ if resolved_segment.args_and_bindings.is_some() {
+ on_prohibited_generics_for_resolved_segment(
+ self,
+ GenericArgsProhibitedReason::SelfTy,
+ );
+ }
+
let generics = generics(self.db.upcast(), adt.into());
let substs = match self.type_param_mode {
ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db),
@@ -667,6 +737,12 @@ impl<'a> TyLoweringContext<'a> {
TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args),
TypeNs::BuiltinType(it) => {
+ if resolved_segment.args_and_bindings.is_some() {
+ on_prohibited_generics_for_resolved_segment(
+ self,
+ GenericArgsProhibitedReason::PrimitiveTy,
+ );
+ }
self.lower_path_inner(resolved_segment, it.into(), infer_args)
}
TypeNs::TypeAliasId(it) => {
@@ -698,14 +774,39 @@ impl<'a> TyLoweringContext<'a> {
return (ty, None);
}
- let (resolved_segment, remaining_segments) = match remaining_index {
- None => (
- path.segments().last().expect("resolved path has at least one element"),
- PathSegments::EMPTY,
- ),
- Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
- };
- self.lower_partly_resolved_path(resolution, resolved_segment, remaining_segments, false)
+ let (module_segments, resolved_segment_idx, resolved_segment, remaining_segments) =
+ match remaining_index {
+ None => (
+ path.segments().strip_last(),
+ path.segments().len() - 1,
+ path.segments().last().expect("resolved path has at least one element"),
+ PathSegments::EMPTY,
+ ),
+ Some(i) => (
+ path.segments().take(i - 1),
+ i - 1,
+ path.segments().get(i - 1).unwrap(),
+ path.segments().skip(i),
+ ),
+ };
+
+ self.prohibit_generics(path_id, 0, module_segments, GenericArgsProhibitedReason::Module);
+
+ self.lower_partly_resolved_path(
+ resolution,
+ resolved_segment,
+ remaining_segments,
+ false,
+ &mut |this, reason| {
+ this.push_diagnostic(
+ path_id.type_ref(),
+ TyLoweringDiagnosticKind::GenericArgsProhibited {
+ segment: resolved_segment_idx as u32,
+ reason,
+ },
+ )
+ },
+ )
}
fn select_associated_type(&mut self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
@@ -742,12 +843,8 @@ impl<'a> TyLoweringContext<'a> {
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`t.substitution`).
- let substs = self.substs_from_path_segment(
- segment.clone(),
- Some(associated_ty.into()),
- false,
- None,
- );
+ let substs =
+ self.substs_from_path_segment(segment, Some(associated_ty.into()), false, None);
let len_self =
crate::generics::generics(self.db.upcast(), associated_ty.into()).len_self();
@@ -999,6 +1096,23 @@ impl<'a> TyLoweringContext<'a> {
TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs }
}
+ fn prohibit_generics(
+ &mut self,
+ path_id: PathId,
+ idx: u32,
+ segments: PathSegments<'_>,
+ reason: GenericArgsProhibitedReason,
+ ) {
+ segments.iter().zip(idx..).for_each(|(segment, idx)| {
+ if segment.args_and_bindings.is_some() {
+ self.push_diagnostic(
+ path_id.type_ref(),
+ TyLoweringDiagnosticKind::GenericArgsProhibited { segment: idx, reason },
+ );
+ }
+ });
+ }
+
fn lower_trait_ref_from_path(
&mut self,
path_id: PathId,
@@ -1010,6 +1124,13 @@ impl<'a> TyLoweringContext<'a> {
TypeNs::TraitId(tr) => tr,
_ => return None,
};
+ // Do this after we verify it's indeed a trait to not confuse the user if they're not modules.
+ self.prohibit_generics(
+ path_id,
+ 0,
+ path.segments().strip_last(),
+ GenericArgsProhibitedReason::Module,
+ );
let segment = path.segments().last().expect("path should have at least one segment");
Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty))
}
@@ -1233,7 +1354,9 @@ impl<'a> TyLoweringContext<'a> {
}
_ => unreachable!(),
}
- ext.lower_ty(type_ref)
+ let ty = ext.lower_ty(type_ref);
+ self.diagnostics.extend(ext.diagnostics);
+ ty
} else {
self.lower_ty(type_ref)
};