Unnamed repository; edit this file 'description' to name the repository.
Complete diagnostics in ty lowering groundwork
Implement diagnostics in all places left: generics (predicates, defaults, const params' types), fields, and type aliases.
Unfortunately this results in a 20mb addition in `analysis-stats .` due to many type methods returning an addition diagnostics result now (even if it's `None` in most cases). I'm not sure if this can be improved.
An alternative strategy that can prevent the memory usage growth is to never produce diagnostics in hir-ty methods. Instead, lower all types in the hir crate when computing diagnostics from scratch (with diagnostics this time). But this has two serious disadvantages:
1. This can cause code duplication (although it can probably be not that bad, it will still mean a lot more code).
2. I believe we eventually want to compute diagnostics for the *entire* workspace (either on-type or on-save or something alike), so users can know when they have diagnostics even in inactive files. Choosing this approach will mean we lose all precomputed salsa queries. For one file this is fine, for the whole workspace this will be very slow.
| -rw-r--r-- | crates/hir-def/src/generics.rs | 5 | ||||
| -rw-r--r-- | crates/hir-ty/src/db.rs | 30 | ||||
| -rw-r--r-- | crates/hir-ty/src/generics.rs | 16 | ||||
| -rw-r--r-- | crates/hir-ty/src/lib.rs | 3 | ||||
| -rw-r--r-- | crates/hir-ty/src/lower.rs | 213 | ||||
| -rw-r--r-- | crates/hir/src/diagnostics.rs | 59 | ||||
| -rw-r--r-- | crates/hir/src/lib.rs | 198 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs | 200 |
8 files changed, 621 insertions, 103 deletions
diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs index fdcb10e998..7b3f1d06d2 100644 --- a/crates/hir-def/src/generics.rs +++ b/crates/hir-def/src/generics.rs @@ -225,6 +225,11 @@ impl GenericParams { } #[inline] + pub fn no_predicates(&self) -> bool { + self.where_predicates.is_empty() + } + + #[inline] pub fn where_predicates(&self) -> std::slice::Iter<'_, WherePredicate> { self.where_predicates.iter() } diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 3a3a05c369..6856eaa3e0 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -22,7 +22,7 @@ use crate::{ consteval::ConstEvalError, dyn_compatibility::DynCompatibilityViolation, layout::{Layout, LayoutError}, - lower::{GenericDefaults, GenericPredicates}, + lower::{Diagnostics, GenericDefaults, GenericPredicates}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner, @@ -115,21 +115,35 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { #[ra_salsa::cycle(crate::lower::ty_recover)] fn ty(&self, def: TyDefId) -> Binders<Ty>; + #[ra_salsa::invoke(crate::lower::type_for_type_alias_with_diagnostics_query)] + fn type_for_type_alias_with_diagnostics(&self, def: TypeAliasId) -> (Binders<Ty>, Diagnostics); + /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is /// a `StructId` or `EnumVariantId` with a record constructor. #[ra_salsa::invoke(crate::lower::value_ty_query)] fn value_ty(&self, def: ValueTyDefId) -> Option<Binders<Ty>>; + #[ra_salsa::invoke(crate::lower::impl_self_ty_with_diagnostics_query)] + #[ra_salsa::cycle(crate::lower::impl_self_ty_with_diagnostics_recover)] + fn impl_self_ty_with_diagnostics(&self, def: ImplId) -> (Binders<Ty>, Diagnostics); #[ra_salsa::invoke(crate::lower::impl_self_ty_query)] - #[ra_salsa::cycle(crate::lower::impl_self_ty_recover)] fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>; + #[ra_salsa::invoke(crate::lower::const_param_ty_with_diagnostics_query)] + fn const_param_ty_with_diagnostics(&self, def: ConstParamId) -> (Ty, Diagnostics); #[ra_salsa::invoke(crate::lower::const_param_ty_query)] fn const_param_ty(&self, def: ConstParamId) -> Ty; + #[ra_salsa::invoke(crate::lower::impl_trait_with_diagnostics_query)] + fn impl_trait_with_diagnostics(&self, def: ImplId) -> Option<(Binders<TraitRef>, Diagnostics)>; #[ra_salsa::invoke(crate::lower::impl_trait_query)] fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>; + #[ra_salsa::invoke(crate::lower::field_types_with_diagnostics_query)] + fn field_types_with_diagnostics( + &self, + var: VariantId, + ) -> (Arc<ArenaMap<LocalFieldId, Binders<Ty>>>, Diagnostics); #[ra_salsa::invoke(crate::lower::field_types_query)] fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>; @@ -154,6 +168,11 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { #[ra_salsa::invoke(crate::lower::generic_predicates_query)] fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates; + #[ra_salsa::invoke(crate::lower::generic_predicates_without_parent_with_diagnostics_query)] + fn generic_predicates_without_parent_with_diagnostics( + &self, + def: GenericDefId, + ) -> (GenericPredicates, Diagnostics); #[ra_salsa::invoke(crate::lower::generic_predicates_without_parent_query)] fn generic_predicates_without_parent(&self, def: GenericDefId) -> GenericPredicates; @@ -164,8 +183,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { #[ra_salsa::invoke(crate::lower::trait_environment_query)] fn trait_environment(&self, def: GenericDefId) -> Arc<TraitEnvironment>; + #[ra_salsa::invoke(crate::lower::generic_defaults_with_diagnostics_query)] + #[ra_salsa::cycle(crate::lower::generic_defaults_with_diagnostics_recover)] + fn generic_defaults_with_diagnostics( + &self, + def: GenericDefId, + ) -> (GenericDefaults, Diagnostics); #[ra_salsa::invoke(crate::lower::generic_defaults_query)] - #[ra_salsa::cycle(crate::lower::generic_defaults_recover)] fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults; #[ra_salsa::invoke(InherentImpls::inherent_impls_in_crate_query)] diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs index c094bc3951..fe7541d237 100644 --- a/crates/hir-ty/src/generics.rs +++ b/crates/hir-ty/src/generics.rs @@ -55,6 +55,10 @@ impl Generics { self.def } + pub(crate) fn self_types_map(&self) -> &TypesMap { + &self.params.types_map + } + pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> + '_ { self.iter_self_id().chain(self.iter_parent_id()) } @@ -86,15 +90,13 @@ impl Generics { self.iter_self().chain(self.iter_parent()) } - pub(crate) fn iter_with_types_map( + pub(crate) fn iter_parents_with_types_map( &self, ) -> impl Iterator<Item = ((GenericParamId, GenericParamDataRef<'_>), &TypesMap)> + '_ { - self.iter_self().zip(std::iter::repeat(&self.params.types_map)).chain( - self.iter_parent().zip( - self.parent_generics() - .into_iter() - .flat_map(|it| std::iter::repeat(&it.params.types_map)), - ), + self.iter_parent().zip( + self.parent_generics() + .into_iter() + .flat_map(|it| std::iter::repeat(&it.params.types_map)), ) } diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 77ae295cee..8bb90ca31e 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -90,7 +90,8 @@ pub use infer::{ pub use interner::Interner; pub use lower::{ associated_type_shorthand_candidates, GenericArgsProhibitedReason, ImplTraitLoweringMode, - ParamLoweringMode, TyDefId, TyLoweringContext, TyLoweringDiagnosticKind, ValueTyDefId, + ParamLoweringMode, TyDefId, TyLoweringContext, TyLoweringDiagnostic, TyLoweringDiagnosticKind, + ValueTyDefId, }; pub use mapping::{ from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index b73967be71..b23f2749ab 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -48,7 +48,7 @@ use rustc_pattern_analysis::Captures; use smallvec::SmallVec; use stdx::{impl_from, never}; use syntax::ast; -use triomphe::Arc; +use triomphe::{Arc, ThinArc}; use crate::{ all_super_traits, @@ -1652,11 +1652,24 @@ fn named_associated_type_shorthand_candidates<R>( } } -/// Build the type of all specific fields of a struct or enum variant. +pub(crate) type Diagnostics = Option<ThinArc<(), TyLoweringDiagnostic>>; + +fn create_diagnostics(diagnostics: Vec<TyLoweringDiagnostic>) -> Diagnostics { + (!diagnostics.is_empty()).then(|| ThinArc::from_header_and_iter((), diagnostics.into_iter())) +} + pub(crate) fn field_types_query( db: &dyn HirDatabase, variant_id: VariantId, ) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>> { + db.field_types_with_diagnostics(variant_id).0 +} + +/// Build the type of all specific fields of a struct or enum variant. +pub(crate) fn field_types_with_diagnostics_query( + db: &dyn HirDatabase, + variant_id: VariantId, +) -> (Arc<ArenaMap<LocalFieldId, Binders<Ty>>>, Diagnostics) { let var_data = variant_id.variant_data(db.upcast()); let (resolver, def): (_, GenericDefId) = match variant_id { VariantId::StructId(it) => (it.resolver(db.upcast()), it.into()), @@ -1672,7 +1685,7 @@ pub(crate) fn field_types_query( for (field_id, field_data) in var_data.fields().iter() { res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(field_data.type_ref))); } - Arc::new(res) + (Arc::new(res), create_diagnostics(ctx.diagnostics)) } /// This query exists only to be used when resolving short-hand associated types @@ -1873,15 +1886,22 @@ pub(crate) fn generic_predicates_query( db: &dyn HirDatabase, def: GenericDefId, ) -> GenericPredicates { - generic_predicates_filtered_by(db, def, |_, _| true) + generic_predicates_filtered_by(db, def, |_, _| true).0 } -/// Resolve the where clause(s) of an item with generics, -/// except the ones inherited from the parent pub(crate) fn generic_predicates_without_parent_query( db: &dyn HirDatabase, def: GenericDefId, ) -> GenericPredicates { + db.generic_predicates_without_parent_with_diagnostics(def).0 +} + +/// Resolve the where clause(s) of an item with generics, +/// except the ones inherited from the parent +pub(crate) fn generic_predicates_without_parent_with_diagnostics_query( + db: &dyn HirDatabase, + def: GenericDefId, +) -> (GenericPredicates, Diagnostics) { generic_predicates_filtered_by(db, def, |_, d| *d == def) } @@ -1891,7 +1911,7 @@ fn generic_predicates_filtered_by<F>( db: &dyn HirDatabase, def: GenericDefId, filter: F, -) -> GenericPredicates +) -> (GenericPredicates, Diagnostics) where F: Fn(&WherePredicate, &GenericDefId) -> bool, { @@ -1932,7 +1952,10 @@ where ); }; } - GenericPredicates(predicates.is_empty().not().then(|| predicates.into())) + ( + GenericPredicates(predicates.is_empty().not().then(|| predicates.into())), + create_diagnostics(ctx.diagnostics), + ) } /// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound. @@ -1985,75 +2008,110 @@ impl ops::Deref for GenericDefaults { } } -/// Resolve the default type params from generics pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) -> GenericDefaults { + db.generic_defaults_with_diagnostics(def).0 +} + +/// Resolve the default type params from generics. +/// +/// Diagnostics are only returned for this `GenericDefId` (returned defaults include parents). +pub(crate) fn generic_defaults_with_diagnostics_query( + db: &dyn HirDatabase, + def: GenericDefId, +) -> (GenericDefaults, Diagnostics) { let generic_params = generics(db.upcast(), def); if generic_params.len() == 0 { - return GenericDefaults(None); + return (GenericDefaults(None), None); } let resolver = def.resolver(db.upcast()); let parent_start_idx = generic_params.len_self(); - let mut ctx = TyLoweringContext::new(db, &resolver, TypesMap::EMPTY, def.into()) - .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed) - .with_type_param_mode(ParamLoweringMode::Variable); - GenericDefaults(Some(Arc::from_iter(generic_params.iter_with_types_map().enumerate().map( - |(idx, ((id, p), types_map))| { - ctx.types_map = types_map; - match p { - GenericParamDataRef::TypeParamData(p) => { - let ty = p.default.as_ref().map_or(TyKind::Error.intern(Interner), |ty| { - // Each default can only refer to previous parameters. - // Type variable default referring to parameter coming - // after it is forbidden (FIXME: report diagnostic) - fallback_bound_vars(ctx.lower_ty(*ty), idx, parent_start_idx) - }); - crate::make_binders(db, &generic_params, ty.cast(Interner)) - } - GenericParamDataRef::ConstParamData(p) => { - let GenericParamId::ConstParamId(id) = id else { - unreachable!("Unexpected lifetime or type argument") - }; + let mut ctx = + TyLoweringContext::new(db, &resolver, generic_params.self_types_map(), def.into()) + .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed) + .with_type_param_mode(ParamLoweringMode::Variable); + let mut idx = 0; + let mut defaults = generic_params + .iter_self() + .map(|(id, p)| { + let result = + handle_generic_param(&mut ctx, idx, id, p, parent_start_idx, &generic_params); + idx += 1; + result + }) + .collect::<Vec<_>>(); + let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics)); + defaults.extend(generic_params.iter_parents_with_types_map().map(|((id, p), types_map)| { + ctx.types_map = types_map; + let result = handle_generic_param(&mut ctx, idx, id, p, parent_start_idx, &generic_params); + idx += 1; + result + })); + let defaults = GenericDefaults(Some(Arc::from_iter(defaults))); + return (defaults, diagnostics); + + fn handle_generic_param( + ctx: &mut TyLoweringContext<'_>, + idx: usize, + id: GenericParamId, + p: GenericParamDataRef<'_>, + parent_start_idx: usize, + generic_params: &Generics, + ) -> Binders<crate::GenericArg> { + match p { + GenericParamDataRef::TypeParamData(p) => { + let ty = p.default.as_ref().map_or(TyKind::Error.intern(Interner), |ty| { + // Each default can only refer to previous parameters. + // Type variable default referring to parameter coming + // after it is forbidden (FIXME: report diagnostic) + fallback_bound_vars(ctx.lower_ty(*ty), idx, parent_start_idx) + }); + crate::make_binders(ctx.db, generic_params, ty.cast(Interner)) + } + GenericParamDataRef::ConstParamData(p) => { + let GenericParamId::ConstParamId(id) = id else { + unreachable!("Unexpected lifetime or type argument") + }; - let mut val = p.default.as_ref().map_or_else( - || unknown_const_as_generic(db.const_param_ty(id)), - |c| { - let param_ty = ctx.lower_ty(p.ty); - let c = ctx.lower_const(c, param_ty); - c.cast(Interner) - }, - ); - // Each default can only refer to previous parameters, see above. - val = fallback_bound_vars(val, idx, parent_start_idx); - make_binders(db, &generic_params, val) - } - GenericParamDataRef::LifetimeParamData(_) => { - make_binders(db, &generic_params, error_lifetime().cast(Interner)) - } + let mut val = p.default.as_ref().map_or_else( + || unknown_const_as_generic(ctx.db.const_param_ty(id)), + |c| { + let param_ty = ctx.lower_ty(p.ty); + let c = ctx.lower_const(c, param_ty); + c.cast(Interner) + }, + ); + // Each default can only refer to previous parameters, see above. + val = fallback_bound_vars(val, idx, parent_start_idx); + make_binders(ctx.db, generic_params, val) } - }, - )))) + GenericParamDataRef::LifetimeParamData(_) => { + make_binders(ctx.db, generic_params, error_lifetime().cast(Interner)) + } + } + } } -pub(crate) fn generic_defaults_recover( +pub(crate) fn generic_defaults_with_diagnostics_recover( db: &dyn HirDatabase, _cycle: &Cycle, def: &GenericDefId, -) -> GenericDefaults { +) -> (GenericDefaults, Diagnostics) { let generic_params = generics(db.upcast(), *def); if generic_params.len() == 0 { - return GenericDefaults(None); + return (GenericDefaults(None), None); } // FIXME: this code is not covered in tests. // we still need one default per parameter - GenericDefaults(Some(Arc::from_iter(generic_params.iter_id().map(|id| { + let defaults = GenericDefaults(Some(Arc::from_iter(generic_params.iter_id().map(|id| { let val = match id { GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)), GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), }; crate::make_binders(db, &generic_params, val) - })))) + })))); + (defaults, None) } fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { @@ -2196,7 +2254,10 @@ fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> { make_binders(db, &generics, ty) } -fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { +pub(crate) fn type_for_type_alias_with_diagnostics_query( + db: &dyn HirDatabase, + t: TypeAliasId, +) -> (Binders<Ty>, Diagnostics) { let generics = generics(db.upcast(), t.into()); let resolver = t.resolver(db.upcast()); let type_alias_data = db.type_alias_data(t); @@ -2211,7 +2272,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { .map(|type_ref| ctx.lower_ty(type_ref)) .unwrap_or_else(|| TyKind::Error.intern(Interner)) }; - make_binders(db, &generics, inner) + (make_binders(db, &generics, inner), create_diagnostics(ctx.diagnostics)) } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -2254,7 +2315,7 @@ pub(crate) fn ty_query(db: &dyn HirDatabase, def: TyDefId) -> Binders<Ty> { match def { TyDefId::BuiltinType(it) => Binders::empty(Interner, TyBuilder::builtin(it)), TyDefId::AdtId(it) => type_for_adt(db, it), - TyDefId::TypeAliasId(it) => type_for_type_alias(db, it), + TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).0, } } @@ -2279,47 +2340,73 @@ pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Option< } pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binders<Ty> { + db.impl_self_ty_with_diagnostics(impl_id).0 +} + +pub(crate) fn impl_self_ty_with_diagnostics_query( + db: &dyn HirDatabase, + impl_id: ImplId, +) -> (Binders<Ty>, Diagnostics) { let impl_data = db.impl_data(impl_id); let resolver = impl_id.resolver(db.upcast()); let generics = generics(db.upcast(), impl_id.into()); let mut ctx = TyLoweringContext::new(db, &resolver, &impl_data.types_map, impl_id.into()) .with_type_param_mode(ParamLoweringMode::Variable); - make_binders(db, &generics, ctx.lower_ty(impl_data.self_ty)) + ( + make_binders(db, &generics, ctx.lower_ty(impl_data.self_ty)), + create_diagnostics(ctx.diagnostics), + ) } -// returns None if def is a type arg pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty { + db.const_param_ty_with_diagnostics(def).0 +} + +// returns None if def is a type arg +pub(crate) fn const_param_ty_with_diagnostics_query( + db: &dyn HirDatabase, + def: ConstParamId, +) -> (Ty, Diagnostics) { let parent_data = db.generic_params(def.parent()); let data = &parent_data[def.local_id()]; let resolver = def.parent().resolver(db.upcast()); let mut ctx = TyLoweringContext::new(db, &resolver, &parent_data.types_map, def.parent().into()); - match data { + let ty = match data { TypeOrConstParamData::TypeParamData(_) => { never!(); Ty::new(Interner, TyKind::Error) } TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(d.ty), - } + }; + (ty, create_diagnostics(ctx.diagnostics)) } -pub(crate) fn impl_self_ty_recover( +pub(crate) fn impl_self_ty_with_diagnostics_recover( db: &dyn HirDatabase, _cycle: &Cycle, impl_id: &ImplId, -) -> Binders<Ty> { +) -> (Binders<Ty>, Diagnostics) { let generics = generics(db.upcast(), (*impl_id).into()); - make_binders(db, &generics, TyKind::Error.intern(Interner)) + (make_binders(db, &generics, TyKind::Error.intern(Interner)), None) } pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> { + db.impl_trait_with_diagnostics(impl_id).map(|it| it.0) +} + +pub(crate) fn impl_trait_with_diagnostics_query( + db: &dyn HirDatabase, + impl_id: ImplId, +) -> Option<(Binders<TraitRef>, Diagnostics)> { let impl_data = db.impl_data(impl_id); let resolver = impl_id.resolver(db.upcast()); let mut ctx = TyLoweringContext::new(db, &resolver, &impl_data.types_map, impl_id.into()) .with_type_param_mode(ParamLoweringMode::Variable); let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders(); let target_trait = impl_data.target_trait.as_ref()?; - Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?)) + let trait_ref = Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?); + Some((trait_ref, create_diagnostics(ctx.diagnostics))) } pub(crate) fn return_type_impl_traits( diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 3657cf5e16..612c6adb20 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -15,7 +15,8 @@ use hir_expand::{name::Name, HirFileId, InFile}; use hir_ty::{ db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, - CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnosticKind, + CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic, + TyLoweringDiagnosticKind, }; use syntax::{ ast::{self, HasGenericArgs}, @@ -402,7 +403,7 @@ pub struct InvalidCast { #[derive(Debug)] pub struct GenericArgsProhibited { - pub args: InFile<AstPtr<Either<ast::GenericArgList, ast::ParamList>>>, + pub args: InFile<AstPtr<Either<ast::GenericArgList, ast::ParenthesizedArgList>>>, pub reason: GenericArgsProhibitedReason, } @@ -664,30 +665,38 @@ impl AnyDiagnostic { InferenceTyDiagnosticSource::Body => &source_map.types, InferenceTyDiagnosticSource::Signature => outer_types_source_map, }; - let source = match diag.source { - Either::Left(type_ref_id) => { - let Ok(source) = source_map.type_syntax(type_ref_id) else { - stdx::never!("error on synthetic type syntax"); - return None; - }; - source - } - Either::Right(source) => source, + Self::ty_diagnostic(diag, source_map, db)? + } + }) + } + + pub(crate) fn ty_diagnostic( + diag: &TyLoweringDiagnostic, + source_map: &TypesSourceMap, + db: &dyn HirDatabase, + ) -> Option<AnyDiagnostic> { + let source = match diag.source { + Either::Left(type_ref_id) => { + let Ok(source) = source_map.type_syntax(type_ref_id) else { + stdx::never!("error on synthetic type syntax"); + return None; }; - let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id)); - match diag.kind { - TyLoweringDiagnosticKind::GenericArgsProhibited { segment, reason } => { - let ast::Type::PathType(syntax) = syntax() else { return None }; - let segment = hir_segment_to_ast_segment(&syntax.path()?, segment)?; - let args = if let Some(generics) = segment.generic_arg_list() { - AstPtr::new(&generics).wrap_left() - } else { - AstPtr::new(&segment.param_list()?).wrap_right() - }; - let args = source.with_value(args); - GenericArgsProhibited { args, reason }.into() - } - } + source + } + Either::Right(source) => source, + }; + let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id)); + Some(match diag.kind { + TyLoweringDiagnosticKind::GenericArgsProhibited { segment, reason } => { + let ast::Type::PathType(syntax) = syntax() else { return None }; + let segment = hir_segment_to_ast_segment(&syntax.path()?, segment)?; + let args = if let Some(generics) = segment.generic_arg_list() { + AstPtr::new(&generics).wrap_left() + } else { + AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right() + }; + let args = source.with_value(args); + GenericArgsProhibited { args, reason }.into() } }) } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index cd46276747..10365ba002 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -76,8 +76,8 @@ use hir_ty::{ traits::FnTrait, AliasTy, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg, GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution, - TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId, - WhereClause, + TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, TyLoweringDiagnostic, + ValueTyDefId, WhereClause, }; use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; @@ -89,7 +89,7 @@ use syntax::{ ast::{self, HasAttrs as _, HasGenericParams, HasName}, format_smolstr, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ToSmolStr, T, }; -use triomphe::Arc; +use triomphe::{Arc, ThinArc}; use crate::db::{DefDatabase, HirDatabase}; @@ -411,6 +411,10 @@ impl ModuleDef { } } + if let Some(def) = self.as_self_generic_def() { + def.diagnostics(db, &mut acc); + } + acc } @@ -431,6 +435,23 @@ impl ModuleDef { } } + /// Returns only defs that have generics from themselves, not their parent. + pub fn as_self_generic_def(self) -> Option<GenericDef> { + match self { + ModuleDef::Function(it) => Some(it.into()), + ModuleDef::Adt(it) => Some(it.into()), + ModuleDef::Trait(it) => Some(it.into()), + ModuleDef::TraitAlias(it) => Some(it.into()), + ModuleDef::TypeAlias(it) => Some(it.into()), + ModuleDef::Module(_) + | ModuleDef::Variant(_) + | ModuleDef::Static(_) + | ModuleDef::Const(_) + | ModuleDef::BuiltinType(_) + | ModuleDef::Macro(_) => None, + } + } + pub fn attrs(&self, db: &dyn HirDatabase) -> Option<AttrsWithOwner> { Some(match self { ModuleDef::Module(it) => it.attrs(db), @@ -605,17 +626,42 @@ impl Module { ModuleDef::Adt(adt) => { match adt { Adt::Struct(s) => { + let tree_id = s.id.lookup(db.upcast()).id; + let tree_source_maps = tree_id.item_tree_with_source_map(db.upcast()).1; + push_ty_diagnostics( + db, + acc, + db.field_types_with_diagnostics(s.id.into()).1, + tree_source_maps.strukt(tree_id.value).item(), + ); for diag in db.struct_data_with_diagnostics(s.id).1.iter() { emit_def_diagnostic(db, acc, diag, edition); } } Adt::Union(u) => { + let tree_id = u.id.lookup(db.upcast()).id; + let tree_source_maps = tree_id.item_tree_with_source_map(db.upcast()).1; + push_ty_diagnostics( + db, + acc, + db.field_types_with_diagnostics(u.id.into()).1, + tree_source_maps.union(tree_id.value).item(), + ); for diag in db.union_data_with_diagnostics(u.id).1.iter() { emit_def_diagnostic(db, acc, diag, edition); } } Adt::Enum(e) => { for v in e.variants(db) { + let tree_id = v.id.lookup(db.upcast()).id; + let tree_source_maps = + tree_id.item_tree_with_source_map(db.upcast()).1; + push_ty_diagnostics( + db, + acc, + db.field_types_with_diagnostics(v.id.into()).1, + tree_source_maps.variant(tree_id.value), + ); acc.extend(ModuleDef::Variant(v).diagnostics(db, style_lints)); for diag in db.enum_variant_data_with_diagnostics(v.id).1.iter() { emit_def_diagnostic(db, acc, diag, edition); @@ -626,6 +672,17 @@ impl Module { acc.extend(def.diagnostics(db, style_lints)) } ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m), + ModuleDef::TypeAlias(type_alias) => { + let tree_id = type_alias.id.lookup(db.upcast()).id; + let tree_source_maps = tree_id.item_tree_with_source_map(db.upcast()).1; + push_ty_diagnostics( + db, + acc, + db.type_for_type_alias_with_diagnostics(type_alias.id).1, + tree_source_maps.type_alias(tree_id.value).item(), + ); + acc.extend(def.diagnostics(db, style_lints)); + } _ => acc.extend(def.diagnostics(db, style_lints)), } } @@ -635,8 +692,11 @@ impl Module { let mut impl_assoc_items_scratch = vec![]; for impl_def in self.impl_defs(db) { + GenericDef::Impl(impl_def).diagnostics(db, acc); + let loc = impl_def.id.lookup(db.upcast()); - let tree = loc.id.item_tree(db.upcast()); + let (tree, tree_source_maps) = loc.id.item_tree_with_source_map(db.upcast()); + let source_map = tree_source_maps.impl_(loc.id.value).item(); let node = &tree[loc.id.value]; let file_id = loc.id.file_id(); if file_id.macro_file().map_or(false, |it| it.is_builtin_derive(db.upcast())) { @@ -771,6 +831,19 @@ impl Module { impl_assoc_items_scratch.clear(); } + push_ty_diagnostics( + db, + acc, + db.impl_self_ty_with_diagnostics(impl_def.id).1, + source_map, + ); + push_ty_diagnostics( + db, + acc, + db.impl_trait_with_diagnostics(impl_def.id).and_then(|it| it.1), + source_map, + ); + for &item in db.impl_data(impl_def.id).items.iter() { AssocItem::from(item).diagnostics(db, acc, style_lints); } @@ -3350,12 +3423,22 @@ impl AssocItem { ) { match self { AssocItem::Function(func) => { + GenericDef::Function(func).diagnostics(db, acc); DefWithBody::from(func).diagnostics(db, acc, style_lints); } AssocItem::Const(const_) => { DefWithBody::from(const_).diagnostics(db, acc, style_lints); } AssocItem::TypeAlias(type_alias) => { + GenericDef::TypeAlias(type_alias).diagnostics(db, acc); + let tree_id = type_alias.id.lookup(db.upcast()).id; + let tree_source_maps = tree_id.item_tree_with_source_map(db.upcast()).1; + push_ty_diagnostics( + db, + acc, + db.type_for_type_alias_with_diagnostics(type_alias.id).1, + tree_source_maps.type_alias(tree_id.value).item(), + ); for diag in hir_ty::diagnostics::incorrect_case(db, type_alias.id.into()) { acc.push(diag.into()); } @@ -3442,6 +3525,97 @@ impl GenericDef { }) .collect() } + + fn id(self) -> GenericDefId { + match self { + GenericDef::Function(it) => it.id.into(), + GenericDef::Adt(it) => it.into(), + GenericDef::Trait(it) => it.id.into(), + GenericDef::TraitAlias(it) => it.id.into(), + GenericDef::TypeAlias(it) => it.id.into(), + GenericDef::Impl(it) => it.id.into(), + GenericDef::Const(it) => it.id.into(), + } + } + + pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) { + let def = self.id(); + + let item_tree_source_maps; + let (generics, generics_source_map) = db.generic_params_with_source_map(def); + + if generics.is_empty() && generics.no_predicates() { + return; + } + + let source_map = match &generics_source_map { + Some(it) => it, + None => match def { + GenericDefId::FunctionId(it) => { + let id = it.lookup(db.upcast()).id; + item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1; + item_tree_source_maps.function(id.value).generics() + } + GenericDefId::AdtId(AdtId::EnumId(it)) => { + let id = it.lookup(db.upcast()).id; + item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1; + item_tree_source_maps.enum_generic(id.value) + } + GenericDefId::AdtId(AdtId::StructId(it)) => { + let id = it.lookup(db.upcast()).id; + item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1; + item_tree_source_maps.strukt(id.value).generics() + } + GenericDefId::AdtId(AdtId::UnionId(it)) => { + let id = it.lookup(db.upcast()).id; + item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1; + item_tree_source_maps.union(id.value).generics() + } + GenericDefId::TraitId(it) => { + let id = it.lookup(db.upcast()).id; + item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1; + item_tree_source_maps.trait_generic(id.value) + } + GenericDefId::TraitAliasId(it) => { + let id = it.lookup(db.upcast()).id; + item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1; + item_tree_source_maps.trait_alias_generic(id.value) + } + GenericDefId::TypeAliasId(it) => { + let id = it.lookup(db.upcast()).id; + item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1; + item_tree_source_maps.type_alias(id.value).generics() + } + GenericDefId::ImplId(it) => { + let id = it.lookup(db.upcast()).id; + item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1; + item_tree_source_maps.impl_(id.value).generics() + } + GenericDefId::ConstId(_) => return, + }, + }; + + push_ty_diagnostics(db, acc, db.generic_defaults_with_diagnostics(def).1, source_map); + push_ty_diagnostics( + db, + acc, + db.generic_predicates_without_parent_with_diagnostics(def).1, + source_map, + ); + for (param_id, param) in generics.iter_type_or_consts() { + if let TypeOrConstParamData::ConstParamData(_) = param { + push_ty_diagnostics( + db, + acc, + db.const_param_ty_with_diagnostics(ConstParamId::from_unchecked( + TypeOrConstParamId { parent: def, local_id: param_id }, + )) + .1, + source_map, + ); + } + } + } } /// A single local definition. @@ -5825,3 +5999,19 @@ pub enum DocLinkDef { Field(Field), SelfType(Trait), } + +fn push_ty_diagnostics( + db: &dyn HirDatabase, + acc: &mut Vec<AnyDiagnostic>, + diagnostics: Option<ThinArc<(), TyLoweringDiagnostic>>, + source_map: &TypesSourceMap, +) { + if let Some(diagnostics) = diagnostics { + acc.extend( + diagnostics + .slice + .iter() + .filter_map(|diagnostic| AnyDiagnostic::ty_diagnostic(diagnostic, source_map, db)), + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs index c3ad6704b6..a319a0bcf6 100644 --- a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs +++ b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs @@ -68,6 +68,9 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::GenericArgsProhibited) -> Option #[cfg(test)] mod tests { + // This diagnostic was the first to be emitted in ty lowering, so the tests here also test + // diagnostics in ty lowering in general (which is why there are so many of them). + use crate::tests::{check_diagnostics, check_fix}; #[test] @@ -239,4 +242,201 @@ fn foo() { }"#, ); } + + #[test] + fn in_fields() { + check_diagnostics( + r#" +struct A(bool<i32>); + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types +struct B { v: bool<(), 1> } + // ^^^^^^^ 💡 error: generic arguments are not allowed on builtin types +union C { + a: bool<i32>, + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types + b: i32<bool>, + // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types + } +enum D { + A(bool<i32>), + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types + B { v: i32<bool> }, + // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types +} + "#, + ); + } + + #[test] + fn in_generics() { + check_diagnostics( + r#" +mod foo { + pub trait Trait {} +} + +struct A<A: foo::<()>::Trait>(A) + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + where bool<i32>: foo::Trait; + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types +union B<A: foo::<()>::Trait> + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + where bool<i32>: foo::Trait + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types +{ a: A } +enum C<A: foo::<()>::Trait> + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + where bool<i32>: foo::Trait + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types +{} + +fn f<A: foo::<()>::Trait>() + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + where bool<i32>: foo::Trait + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types +{} + +type D<A: foo::<()>::Trait> = A + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + where bool<i32>: foo::Trait; + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types + +trait E<A: foo::<()>::Trait> + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + where bool<i32>: foo::Trait + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types +{ + fn f<B: foo::<()>::Trait>() + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + where bool<i32>: foo::Trait + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types + {} + + type D<B: foo::<()>::Trait> = A + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + where bool<i32>: foo::Trait; + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types +} + +impl<A: foo::<()>::Trait> E for () + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + where bool<i32>: foo::Trait + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types +{ + fn f<B: foo::<()>::Trait>() + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + where bool<i32>: foo::Trait + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types + {} + + type D<B: foo::<()>::Trait> = A + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + where bool<i32>: foo::Trait; + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types +} + "#, + ); + } + + #[test] + fn assoc_items() { + check_diagnostics( + r#" +struct Foo; + +trait Trait { + fn f() -> bool<i32> { true } + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types + type T = i32<bool>; + // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types +} + +impl Trait for Foo { + fn f() -> bool<i32> { true } + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types + type T = i32<bool>; + // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types +} + +impl Foo { + fn f() -> bool<i32> { true } + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types + type T = i32<bool>; + // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types +} + "#, + ); + } + + #[test] + fn const_param_ty() { + check_diagnostics( + r#" +fn foo< + const A: bool<i32>, + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types + B, + C, + const D: bool<i32>, + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types + const E: bool<i32>, + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types +>() {} + "#, + ); + } + + #[test] + fn generic_defaults() { + check_diagnostics( + r#" +struct Foo<A = bool<i32>>(A); + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types + "#, + ); + } + + #[test] + fn impl_self_ty() { + check_diagnostics( + r#" +struct Foo<A>(A); +trait Trait {} +impl Foo<bool<i32>> {} + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types +impl Trait for Foo<bool<i32>> {} + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types + "#, + ); + } + + #[test] + fn impl_trait() { + check_diagnostics( + r#" +mod foo { + pub trait Trait {} +} +impl foo::<()>::Trait for () {} + // ^^^^^^ 💡 error: generic arguments are not allowed on modules + "#, + ); + } + + #[test] + fn type_alias() { + check_diagnostics( + r#" +pub trait Trait { + type Assoc; +} +type T = bool<i32>; + // ^^^^^ 💡 error: generic arguments are not allowed on builtin types +impl Trait for () { + type Assoc = i32<bool>; + // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types +} + "#, + ); + } } |