mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-11-13 09:11:51 +00:00
Unify handling of path diagnostics in hir-ty
Because it was a mess. Previously, pretty much you had to handle all path diagnostics manually: remember to check for them and handle them. Now, we wrap the resolver in `TyLoweringContext` and ensure proper error reporting. This means that you don't have to worry about them: most of the things are handled automatically, and things that cannot will create a compile-time error (forcing you top `drop(ty_lowering_context);`) if forgotten, instead of silently dropping the diagnostics. The real place for error reporting is in the hir-def resolver, because there are other things resolving, both in hir-ty and in hir-def, and they all need to ensure proper diagnostics. But this is a good start, and future compatible. This commit also ensures proper path diagnostics for value/pattern paths, which is why it's marked "feat".
This commit is contained in:
parent
82896b2cc4
commit
cc11e1a796
14 changed files with 848 additions and 247 deletions
|
|
@ -23,6 +23,7 @@ use chalk_ir::{
|
|||
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
body::HygieneId,
|
||||
builtin_type::BuiltinType,
|
||||
data::adt::StructKind,
|
||||
expander::Expander,
|
||||
|
|
@ -31,9 +32,9 @@ use hir_def::{
|
|||
WherePredicateTypeTarget,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
nameres::MacroSubNs,
|
||||
nameres::{MacroSubNs, ResolvePathResultPrefixInfo},
|
||||
path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
|
||||
resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
|
||||
resolver::{HasResolver, LifetimeNs, ResolveValueResult, Resolver, TypeNs, ValueNs},
|
||||
type_ref::{
|
||||
ConstRef, LifetimeRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound,
|
||||
TypeRef, TypeRefId, TypesMap, TypesSourceMap,
|
||||
|
|
@ -514,8 +515,8 @@ impl<'a> TyLoweringContext<'a> {
|
|||
/// This is only for `generic_predicates_for_param`, where we can't just
|
||||
/// lower the self types of the predicates since that could lead to cycles.
|
||||
/// So we just check here if the `type_ref` resolves to a generic param, and which.
|
||||
fn lower_ty_only_param(&self, type_ref: TypeRefId) -> Option<TypeOrConstParamId> {
|
||||
let type_ref = &self.types_map[type_ref];
|
||||
fn lower_ty_only_param(&mut self, type_ref_id: TypeRefId) -> Option<TypeOrConstParamId> {
|
||||
let type_ref = &self.types_map[type_ref_id];
|
||||
let path = match type_ref {
|
||||
TypeRef::Path(path) => path,
|
||||
_ => return None,
|
||||
|
|
@ -526,8 +527,10 @@ impl<'a> TyLoweringContext<'a> {
|
|||
if path.segments().len() > 1 {
|
||||
return None;
|
||||
}
|
||||
let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
|
||||
Some((it, None, _)) => it,
|
||||
let resolution = match self
|
||||
.resolve_path_in_type_ns(path, &mut Self::on_path_diagnostic_callback(type_ref_id))
|
||||
{
|
||||
Some((it, None)) => it,
|
||||
_ => return None,
|
||||
};
|
||||
match resolution {
|
||||
|
|
@ -562,11 +565,9 @@ impl<'a> TyLoweringContext<'a> {
|
|||
resolution: TypeNs,
|
||||
resolved_segment: PathSegment<'_>,
|
||||
remaining_segments: PathSegments<'_>,
|
||||
_resolved_segment_idx: u32,
|
||||
infer_args: bool,
|
||||
on_prohibited_generics_for_resolved_segment: &mut dyn FnMut(
|
||||
&mut Self,
|
||||
GenericArgsProhibitedReason,
|
||||
),
|
||||
_on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
|
||||
) -> (Ty, Option<TypeNs>) {
|
||||
let ty = match resolution {
|
||||
TypeNs::TraitId(trait_) => {
|
||||
|
|
@ -633,44 +634,28 @@ impl<'a> TyLoweringContext<'a> {
|
|||
// FIXME(trait_alias): Implement trait alias.
|
||||
return (TyKind::Error.intern(Interner), None);
|
||||
}
|
||||
TypeNs::GenericParam(param_id) => {
|
||||
if resolved_segment.args_and_bindings.is_some() {
|
||||
on_prohibited_generics_for_resolved_segment(
|
||||
self,
|
||||
GenericArgsProhibitedReason::TyParam,
|
||||
);
|
||||
TypeNs::GenericParam(param_id) => 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,
|
||||
};
|
||||
|
||||
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))
|
||||
}
|
||||
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 {
|
||||
|
|
@ -696,13 +681,6 @@ 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),
|
||||
|
|
@ -715,12 +693,6 @@ 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) => {
|
||||
|
|
@ -732,6 +704,220 @@ impl<'a> TyLoweringContext<'a> {
|
|||
self.lower_ty_relative_path(ty, Some(resolution), remaining_segments)
|
||||
}
|
||||
|
||||
fn handle_type_ns_resolution(
|
||||
&mut self,
|
||||
resolution: &TypeNs,
|
||||
resolved_segment: PathSegment<'_>,
|
||||
resolved_segment_idx: usize,
|
||||
on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
|
||||
) {
|
||||
let mut prohibit_generics_on_resolved = |reason| {
|
||||
if resolved_segment.args_and_bindings.is_some() {
|
||||
on_diagnostic(
|
||||
self,
|
||||
PathLoweringDiagnostic::GenericArgsProhibited {
|
||||
segment: resolved_segment_idx as u32,
|
||||
reason,
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
match resolution {
|
||||
TypeNs::SelfType(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
|
||||
}
|
||||
TypeNs::GenericParam(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam)
|
||||
}
|
||||
TypeNs::AdtSelfType(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
|
||||
}
|
||||
TypeNs::BuiltinType(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy)
|
||||
}
|
||||
TypeNs::AdtId(_)
|
||||
| TypeNs::EnumVariantId(_)
|
||||
| TypeNs::TypeAliasId(_)
|
||||
| TypeNs::TraitId(_)
|
||||
| TypeNs::TraitAliasId(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_path_in_type_ns_fully(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
|
||||
) -> Option<TypeNs> {
|
||||
let (res, unresolved) = self.resolve_path_in_type_ns(path, on_diagnostic)?;
|
||||
if unresolved.is_some() {
|
||||
return None;
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_path_in_type_ns(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
|
||||
) -> Option<(TypeNs, Option<usize>)> {
|
||||
let (resolution, remaining_index, _) =
|
||||
self.resolver.resolve_path_in_type_ns(self.db.upcast(), path)?;
|
||||
let segments = path.segments();
|
||||
|
||||
match path {
|
||||
// `segments.is_empty()` can occur with `self`.
|
||||
Path::Normal(..) if !segments.is_empty() => (),
|
||||
_ => return Some((resolution, remaining_index)),
|
||||
};
|
||||
|
||||
let (module_segments, resolved_segment_idx, resolved_segment) = match remaining_index {
|
||||
None => (
|
||||
segments.strip_last(),
|
||||
segments.len() - 1,
|
||||
segments.last().expect("resolved path has at least one element"),
|
||||
),
|
||||
Some(i) => (segments.take(i - 1), i - 1, segments.get(i - 1).unwrap()),
|
||||
};
|
||||
|
||||
for (i, mod_segment) in module_segments.iter().enumerate() {
|
||||
if mod_segment.args_and_bindings.is_some() {
|
||||
on_diagnostic(
|
||||
self,
|
||||
PathLoweringDiagnostic::GenericArgsProhibited {
|
||||
segment: i as u32,
|
||||
reason: GenericArgsProhibitedReason::Module,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.handle_type_ns_resolution(
|
||||
&resolution,
|
||||
resolved_segment,
|
||||
resolved_segment_idx,
|
||||
on_diagnostic,
|
||||
);
|
||||
|
||||
Some((resolution, remaining_index))
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_path_in_value_ns(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
hygiene_id: HygieneId,
|
||||
on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
|
||||
) -> Option<ResolveValueResult> {
|
||||
let (res, prefix_info) = self.resolver.resolve_path_in_value_ns_with_prefix_info(
|
||||
self.db.upcast(),
|
||||
path,
|
||||
hygiene_id,
|
||||
)?;
|
||||
|
||||
let segments = path.segments();
|
||||
match path {
|
||||
// `segments.is_empty()` can occur with `self`.
|
||||
Path::Normal(..) if !segments.is_empty() => (),
|
||||
_ => return Some(res),
|
||||
};
|
||||
|
||||
let (mod_segments, enum_segment) = match res {
|
||||
ResolveValueResult::Partial(_, unresolved_segment, _) => {
|
||||
(segments.take(unresolved_segment - 1), None)
|
||||
}
|
||||
ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _)
|
||||
if prefix_info == ResolvePathResultPrefixInfo::Enum =>
|
||||
{
|
||||
(segments.strip_last_two(), segments.len().checked_sub(2))
|
||||
}
|
||||
ResolveValueResult::ValueNs(..) => (segments.strip_last(), None),
|
||||
};
|
||||
for (i, mod_segment) in mod_segments.iter().enumerate() {
|
||||
if mod_segment.args_and_bindings.is_some() {
|
||||
on_diagnostic(
|
||||
self,
|
||||
PathLoweringDiagnostic::GenericArgsProhibited {
|
||||
segment: i as u32,
|
||||
reason: GenericArgsProhibitedReason::Module,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(enum_segment) = enum_segment {
|
||||
if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
|
||||
&& segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
|
||||
{
|
||||
on_diagnostic(
|
||||
self,
|
||||
PathLoweringDiagnostic::GenericArgsProhibited {
|
||||
segment: (enum_segment + 1) as u32,
|
||||
reason: GenericArgsProhibitedReason::EnumVariant,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
match &res {
|
||||
ResolveValueResult::ValueNs(resolution, _) => {
|
||||
let resolved_segment_idx =
|
||||
segments.len().checked_sub(1).unwrap_or_else(|| panic!("{path:?}"));
|
||||
let resolved_segment = segments.last().unwrap();
|
||||
|
||||
let mut prohibit_generics_on_resolved = |reason| {
|
||||
if resolved_segment.args_and_bindings.is_some() {
|
||||
on_diagnostic(
|
||||
self,
|
||||
PathLoweringDiagnostic::GenericArgsProhibited {
|
||||
segment: resolved_segment_idx as u32,
|
||||
reason,
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
match resolution {
|
||||
ValueNs::ImplSelf(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
|
||||
}
|
||||
// FIXME: rustc generates E0107 (incorrect number of generic arguments) and not
|
||||
// E0109 (generic arguments provided for a type that doesn't accept them) for
|
||||
// consts and statics, presumably as a defense against future in which consts
|
||||
// and statics can be generic, or just because it was easier for rustc implementors.
|
||||
// That means we'll show the wrong error code. Because of us it's easier to do it
|
||||
// this way :)
|
||||
ValueNs::GenericParam(_) | ValueNs::ConstId(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const)
|
||||
}
|
||||
ValueNs::StaticId(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static)
|
||||
}
|
||||
ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {}
|
||||
ValueNs::LocalBinding(_) => {}
|
||||
}
|
||||
}
|
||||
ResolveValueResult::Partial(resolution, unresolved_idx, _) => {
|
||||
let resolved_segment_idx = unresolved_idx - 1;
|
||||
let resolved_segment = segments.get(resolved_segment_idx).unwrap();
|
||||
self.handle_type_ns_resolution(
|
||||
resolution,
|
||||
resolved_segment,
|
||||
resolved_segment_idx,
|
||||
on_diagnostic,
|
||||
);
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
fn on_path_diagnostic_callback(
|
||||
type_ref: TypeRefId,
|
||||
) -> impl FnMut(&mut Self, PathLoweringDiagnostic) {
|
||||
move |this, diag| {
|
||||
this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty, Option<TypeNs>) {
|
||||
// Resolve the path (in type namespace)
|
||||
if let Some(type_ref) = path.type_anchor() {
|
||||
|
|
@ -739,11 +925,13 @@ impl<'a> TyLoweringContext<'a> {
|
|||
return self.lower_ty_relative_path(ty, res, path.segments());
|
||||
}
|
||||
|
||||
let (resolution, remaining_index, _) =
|
||||
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
|
||||
Some(it) => it,
|
||||
None => return (TyKind::Error.intern(Interner), None),
|
||||
};
|
||||
let (resolution, remaining_index) = match self.resolve_path_in_type_ns(
|
||||
path,
|
||||
&mut Self::on_path_diagnostic_callback(path_id.type_ref()),
|
||||
) {
|
||||
Some(it) => it,
|
||||
None => return (TyKind::Error.intern(Interner), None),
|
||||
};
|
||||
|
||||
if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() {
|
||||
// trait object type without dyn
|
||||
|
|
@ -752,38 +940,22 @@ impl<'a> TyLoweringContext<'a> {
|
|||
return (ty, None);
|
||||
}
|
||||
|
||||
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);
|
||||
let (resolved_segment_idx, resolved_segment, remaining_segments) = match remaining_index {
|
||||
None => (
|
||||
path.segments().len() - 1,
|
||||
path.segments().last().expect("resolved path has at least one element"),
|
||||
PathSegments::EMPTY,
|
||||
),
|
||||
Some(i) => (i - 1, path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
|
||||
};
|
||||
|
||||
self.lower_partly_resolved_path(
|
||||
resolution,
|
||||
resolved_segment,
|
||||
remaining_segments,
|
||||
resolved_segment_idx as u32,
|
||||
false,
|
||||
&mut |this, reason| {
|
||||
this.push_diagnostic(
|
||||
path_id.type_ref(),
|
||||
TyLoweringDiagnosticKind::GenericArgsProhibited {
|
||||
segment: resolved_segment_idx as u32,
|
||||
reason,
|
||||
},
|
||||
)
|
||||
},
|
||||
&mut Self::on_path_diagnostic_callback(path_id.type_ref()),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1085,7 +1257,9 @@ impl<'a> TyLoweringContext<'a> {
|
|||
if segment.args_and_bindings.is_some() {
|
||||
self.push_diagnostic(
|
||||
path_id.type_ref(),
|
||||
TyLoweringDiagnosticKind::GenericArgsProhibited { segment: idx, reason },
|
||||
TyLoweringDiagnosticKind::PathDiagnostic(
|
||||
PathLoweringDiagnostic::GenericArgsProhibited { segment: idx, reason },
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -1097,7 +1271,10 @@ impl<'a> TyLoweringContext<'a> {
|
|||
explicit_self_ty: Ty,
|
||||
) -> Option<TraitRef> {
|
||||
let path = &self.types_map[path_id];
|
||||
let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? {
|
||||
let resolved = match self.resolve_path_in_type_ns_fully(
|
||||
path,
|
||||
&mut Self::on_path_diagnostic_callback(path_id.type_ref()),
|
||||
)? {
|
||||
// FIXME(trait_alias): We need to handle trait alias here.
|
||||
TypeNs::TraitId(tr) => tr,
|
||||
_ => return None,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue