mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
Merge pull request #18743 from ChayimFriedman2/e0107
feat: Unify handling of path diagnostics in hir-ty
This commit is contained in:
commit
493ac4d4df
15 changed files with 881 additions and 275 deletions
|
@ -85,6 +85,8 @@ use crate::{
|
|||
FxIndexMap, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId,
|
||||
};
|
||||
|
||||
pub use self::path_resolution::ResolvePathResultPrefixInfo;
|
||||
|
||||
const PREDEFINED_TOOLS: &[SmolStr] = &[
|
||||
SmolStr::new_static("clippy"),
|
||||
SmolStr::new_static("rustfmt"),
|
||||
|
@ -615,13 +617,15 @@ impl DefMap {
|
|||
(res.resolved_def, res.segment_index)
|
||||
}
|
||||
|
||||
/// The first `Option<usize>` points at the `Enum` segment in case of `Enum::Variant`, the second
|
||||
/// points at the unresolved segments.
|
||||
pub(crate) fn resolve_path_locally(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
original_module: LocalModuleId,
|
||||
path: &ModPath,
|
||||
shadow: BuiltinShadowMode,
|
||||
) -> (PerNs, Option<usize>) {
|
||||
) -> (PerNs, Option<usize>, ResolvePathResultPrefixInfo) {
|
||||
let res = self.resolve_path_fp_with_macro_single(
|
||||
db,
|
||||
ResolveMode::Other,
|
||||
|
@ -630,7 +634,7 @@ impl DefMap {
|
|||
shadow,
|
||||
None, // Currently this function isn't used for macro resolution.
|
||||
);
|
||||
(res.resolved_def, res.segment_index)
|
||||
(res.resolved_def, res.segment_index, res.prefix_info)
|
||||
}
|
||||
|
||||
/// Ascends the `DefMap` hierarchy and calls `f` with every `DefMap` and containing module.
|
||||
|
|
|
@ -38,7 +38,7 @@ use crate::{
|
|||
attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id},
|
||||
diagnostics::DefDiagnostic,
|
||||
mod_resolution::ModDir,
|
||||
path_resolution::ReachedFixedPoint,
|
||||
path_resolution::{ReachedFixedPoint, ResolvePathResultPrefixInfo},
|
||||
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
|
||||
sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin,
|
||||
ResolveMode,
|
||||
|
@ -797,7 +797,7 @@ impl DefCollector<'_> {
|
|||
return PartialResolvedImport::Unresolved;
|
||||
}
|
||||
|
||||
if res.from_differing_crate {
|
||||
if let ResolvePathResultPrefixInfo::DifferingCrate = res.prefix_info {
|
||||
return PartialResolvedImport::Resolved(
|
||||
def.filter_visibility(|v| matches!(v, Visibility::Public)),
|
||||
);
|
||||
|
|
|
@ -43,21 +43,34 @@ pub(super) struct ResolvePathResult {
|
|||
pub(super) resolved_def: PerNs,
|
||||
pub(super) segment_index: Option<usize>,
|
||||
pub(super) reached_fixedpoint: ReachedFixedPoint,
|
||||
pub(super) from_differing_crate: bool,
|
||||
pub(super) prefix_info: ResolvePathResultPrefixInfo,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ResolvePathResultPrefixInfo {
|
||||
None,
|
||||
DifferingCrate,
|
||||
/// Path of the form `Enum::Variant` (and not `Variant` alone).
|
||||
Enum,
|
||||
}
|
||||
|
||||
impl ResolvePathResult {
|
||||
fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
|
||||
ResolvePathResult::new(PerNs::none(), reached_fixedpoint, None, false)
|
||||
ResolvePathResult::new(
|
||||
PerNs::none(),
|
||||
reached_fixedpoint,
|
||||
None,
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
)
|
||||
}
|
||||
|
||||
fn new(
|
||||
resolved_def: PerNs,
|
||||
reached_fixedpoint: ReachedFixedPoint,
|
||||
segment_index: Option<usize>,
|
||||
from_differing_crate: bool,
|
||||
prefix_info: ResolvePathResultPrefixInfo,
|
||||
) -> ResolvePathResult {
|
||||
ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, from_differing_crate }
|
||||
ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, prefix_info }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,7 +170,17 @@ impl DefMap {
|
|||
if result.reached_fixedpoint == ReachedFixedPoint::No {
|
||||
result.reached_fixedpoint = new.reached_fixedpoint;
|
||||
}
|
||||
result.from_differing_crate |= new.from_differing_crate;
|
||||
result.prefix_info = match (result.prefix_info, new.prefix_info) {
|
||||
(ResolvePathResultPrefixInfo::None, it) => it,
|
||||
(ResolvePathResultPrefixInfo::DifferingCrate, _) => {
|
||||
ResolvePathResultPrefixInfo::DifferingCrate
|
||||
}
|
||||
(
|
||||
ResolvePathResultPrefixInfo::Enum,
|
||||
ResolvePathResultPrefixInfo::DifferingCrate,
|
||||
) => ResolvePathResultPrefixInfo::DifferingCrate,
|
||||
(ResolvePathResultPrefixInfo::Enum, _) => ResolvePathResultPrefixInfo::Enum,
|
||||
};
|
||||
result.segment_index = match (result.segment_index, new.segment_index) {
|
||||
(Some(idx), None) => Some(idx),
|
||||
(Some(old), Some(new)) => Some(old.max(new)),
|
||||
|
@ -403,14 +426,14 @@ impl DefMap {
|
|||
|
||||
fn resolve_remaining_segments<'a>(
|
||||
&self,
|
||||
segments: impl Iterator<Item = (usize, &'a Name)>,
|
||||
mut segments: impl Iterator<Item = (usize, &'a Name)>,
|
||||
mut curr_per_ns: PerNs,
|
||||
path: &ModPath,
|
||||
db: &dyn DefDatabase,
|
||||
shadow: BuiltinShadowMode,
|
||||
original_module: LocalModuleId,
|
||||
) -> ResolvePathResult {
|
||||
for (i, segment) in segments {
|
||||
while let Some((i, segment)) = segments.next() {
|
||||
let curr = match curr_per_ns.take_types_full() {
|
||||
Some(r) => r,
|
||||
None => {
|
||||
|
@ -443,7 +466,7 @@ impl DefMap {
|
|||
def,
|
||||
ReachedFixedPoint::Yes,
|
||||
s.map(|s| s + i),
|
||||
true,
|
||||
ResolvePathResultPrefixInfo::DifferingCrate,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -488,17 +511,28 @@ impl DefMap {
|
|||
),
|
||||
})
|
||||
});
|
||||
match res {
|
||||
Some(res) => res,
|
||||
None => {
|
||||
return ResolvePathResult::new(
|
||||
PerNs::types(e.into(), curr.vis, curr.import),
|
||||
ReachedFixedPoint::Yes,
|
||||
Some(i),
|
||||
false,
|
||||
)
|
||||
// FIXME: Need to filter visibility here and below? Not sure.
|
||||
return match res {
|
||||
Some(res) => {
|
||||
if segments.next().is_some() {
|
||||
// Enum variants are in value namespace, segments left => no resolution.
|
||||
ResolvePathResult::empty(ReachedFixedPoint::No)
|
||||
} else {
|
||||
ResolvePathResult::new(
|
||||
res,
|
||||
ReachedFixedPoint::Yes,
|
||||
None,
|
||||
ResolvePathResultPrefixInfo::Enum,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
None => ResolvePathResult::new(
|
||||
PerNs::types(e.into(), curr.vis, curr.import),
|
||||
ReachedFixedPoint::Yes,
|
||||
Some(i),
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
),
|
||||
};
|
||||
}
|
||||
s => {
|
||||
// could be an inherent method call in UFCS form
|
||||
|
@ -513,7 +547,7 @@ impl DefMap {
|
|||
PerNs::types(s, curr.vis, curr.import),
|
||||
ReachedFixedPoint::Yes,
|
||||
Some(i),
|
||||
false,
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -522,7 +556,12 @@ impl DefMap {
|
|||
.filter_visibility(|vis| vis.is_visible_from_def_map(db, self, original_module));
|
||||
}
|
||||
|
||||
ResolvePathResult::new(curr_per_ns, ReachedFixedPoint::Yes, None, false)
|
||||
ResolvePathResult::new(
|
||||
curr_per_ns,
|
||||
ReachedFixedPoint::Yes,
|
||||
None,
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
)
|
||||
}
|
||||
|
||||
fn resolve_name_in_module(
|
||||
|
|
|
@ -240,6 +240,7 @@ pub struct PathSegment<'a> {
|
|||
pub args_and_bindings: Option<&'a GenericArgs>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PathSegments<'a> {
|
||||
segments: &'a [Name],
|
||||
generic_args: Option<&'a [Option<GenericArgs>]>,
|
||||
|
@ -259,6 +260,7 @@ impl<'a> PathSegments<'a> {
|
|||
pub fn last(&self) -> Option<PathSegment<'a>> {
|
||||
self.get(self.len().checked_sub(1)?)
|
||||
}
|
||||
|
||||
pub fn get(&self, idx: usize) -> Option<PathSegment<'a>> {
|
||||
let res = PathSegment {
|
||||
name: self.segments.get(idx)?,
|
||||
|
@ -266,24 +268,37 @@ impl<'a> PathSegments<'a> {
|
|||
};
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn skip(&self, len: usize) -> PathSegments<'a> {
|
||||
PathSegments {
|
||||
segments: self.segments.get(len..).unwrap_or(&[]),
|
||||
generic_args: self.generic_args.and_then(|it| it.get(len..)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take(&self, len: usize) -> PathSegments<'a> {
|
||||
PathSegments {
|
||||
segments: self.segments.get(..len).unwrap_or(self.segments),
|
||||
generic_args: self.generic_args.map(|it| it.get(..len).unwrap_or(it)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn strip_last(&self) -> PathSegments<'a> {
|
||||
PathSegments {
|
||||
segments: self.segments.split_last().map_or(&[], |it| it.1),
|
||||
generic_args: self.generic_args.map(|it| it.split_last().map_or(&[][..], |it| it.1)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn strip_last_two(&self) -> PathSegments<'a> {
|
||||
PathSegments {
|
||||
segments: self.segments.get(..self.segments.len().saturating_sub(2)).unwrap_or(&[]),
|
||||
generic_args: self
|
||||
.generic_args
|
||||
.map(|it| it.get(..it.len().saturating_sub(2)).unwrap_or(&[])),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = PathSegment<'a>> {
|
||||
self.segments
|
||||
.iter()
|
||||
|
|
|
@ -21,7 +21,7 @@ use crate::{
|
|||
hir::{BindingId, ExprId, LabelId},
|
||||
item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE},
|
||||
lang_item::LangItemTarget,
|
||||
nameres::{DefMap, MacroSubNs},
|
||||
nameres::{DefMap, MacroSubNs, ResolvePathResultPrefixInfo},
|
||||
path::{ModPath, Path, PathKind},
|
||||
per_ns::PerNs,
|
||||
type_ref::{LifetimeRef, TypesMap},
|
||||
|
@ -263,25 +263,37 @@ impl Resolver {
|
|||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &Path,
|
||||
mut hygiene_id: HygieneId,
|
||||
hygiene_id: HygieneId,
|
||||
) -> Option<ResolveValueResult> {
|
||||
self.resolve_path_in_value_ns_with_prefix_info(db, path, hygiene_id).map(|(it, _)| it)
|
||||
}
|
||||
|
||||
pub fn resolve_path_in_value_ns_with_prefix_info(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &Path,
|
||||
mut hygiene_id: HygieneId,
|
||||
) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> {
|
||||
let path = match path {
|
||||
Path::BarePath(mod_path) => mod_path,
|
||||
Path::Normal(it) => it.mod_path(),
|
||||
Path::LangItem(l, None) => {
|
||||
return Some(ResolveValueResult::ValueNs(
|
||||
match *l {
|
||||
LangItemTarget::Function(it) => ValueNs::FunctionId(it),
|
||||
LangItemTarget::Static(it) => ValueNs::StaticId(it),
|
||||
LangItemTarget::Struct(it) => ValueNs::StructId(it),
|
||||
LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it),
|
||||
LangItemTarget::Union(_)
|
||||
| LangItemTarget::ImplDef(_)
|
||||
| LangItemTarget::TypeAlias(_)
|
||||
| LangItemTarget::Trait(_)
|
||||
| LangItemTarget::EnumId(_) => return None,
|
||||
},
|
||||
None,
|
||||
return Some((
|
||||
ResolveValueResult::ValueNs(
|
||||
match *l {
|
||||
LangItemTarget::Function(it) => ValueNs::FunctionId(it),
|
||||
LangItemTarget::Static(it) => ValueNs::StaticId(it),
|
||||
LangItemTarget::Struct(it) => ValueNs::StructId(it),
|
||||
LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it),
|
||||
LangItemTarget::Union(_)
|
||||
| LangItemTarget::ImplDef(_)
|
||||
| LangItemTarget::TypeAlias(_)
|
||||
| LangItemTarget::Trait(_)
|
||||
| LangItemTarget::EnumId(_) => return None,
|
||||
},
|
||||
None,
|
||||
),
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
))
|
||||
}
|
||||
Path::LangItem(l, Some(_)) => {
|
||||
|
@ -296,7 +308,10 @@ impl Resolver {
|
|||
| LangItemTarget::ImplDef(_)
|
||||
| LangItemTarget::Static(_) => return None,
|
||||
};
|
||||
return Some(ResolveValueResult::Partial(type_ns, 1, None));
|
||||
return Some((
|
||||
ResolveValueResult::Partial(type_ns, 1, None),
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
));
|
||||
}
|
||||
};
|
||||
let n_segments = path.segments().len();
|
||||
|
@ -326,9 +341,12 @@ impl Resolver {
|
|||
});
|
||||
|
||||
if let Some(e) = entry {
|
||||
return Some(ResolveValueResult::ValueNs(
|
||||
ValueNs::LocalBinding(e.binding()),
|
||||
None,
|
||||
return Some((
|
||||
ResolveValueResult::ValueNs(
|
||||
ValueNs::LocalBinding(e.binding()),
|
||||
None,
|
||||
),
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -350,14 +368,17 @@ impl Resolver {
|
|||
Scope::GenericParams { params, def } => {
|
||||
if let Some(id) = params.find_const_by_name(first_name, *def) {
|
||||
let val = ValueNs::GenericParam(id);
|
||||
return Some(ResolveValueResult::ValueNs(val, None));
|
||||
return Some((
|
||||
ResolveValueResult::ValueNs(val, None),
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
&Scope::ImplDefScope(impl_) => {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
return Some(ResolveValueResult::ValueNs(
|
||||
ValueNs::ImplSelf(impl_),
|
||||
None,
|
||||
return Some((
|
||||
ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_), None),
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -377,22 +398,27 @@ impl Resolver {
|
|||
Scope::GenericParams { params, def } => {
|
||||
if let Some(id) = params.find_type_by_name(first_name, *def) {
|
||||
let ty = TypeNs::GenericParam(id);
|
||||
return Some(ResolveValueResult::Partial(ty, 1, None));
|
||||
return Some((
|
||||
ResolveValueResult::Partial(ty, 1, None),
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
&Scope::ImplDefScope(impl_) => {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
return Some(ResolveValueResult::Partial(
|
||||
TypeNs::SelfType(impl_),
|
||||
1,
|
||||
None,
|
||||
return Some((
|
||||
ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1, None),
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
Scope::AdtScope(adt) => {
|
||||
if *first_name == sym::Self_.clone() {
|
||||
let ty = TypeNs::AdtSelfType(*adt);
|
||||
return Some(ResolveValueResult::Partial(ty, 1, None));
|
||||
return Some((
|
||||
ResolveValueResult::Partial(ty, 1, None),
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
Scope::BlockScope(m) => {
|
||||
|
@ -413,7 +439,10 @@ impl Resolver {
|
|||
// `use core::u16;`.
|
||||
if path.kind == PathKind::Plain && n_segments > 1 {
|
||||
if let Some(builtin) = BuiltinType::by_name(first_name) {
|
||||
return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None));
|
||||
return Some((
|
||||
ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None),
|
||||
ResolvePathResultPrefixInfo::None,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -924,15 +953,15 @@ impl ModuleItemMap {
|
|||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
) -> Option<ResolveValueResult> {
|
||||
let (module_def, idx) =
|
||||
) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> {
|
||||
let (module_def, unresolved_idx, prefix_info) =
|
||||
self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
|
||||
match idx {
|
||||
match unresolved_idx {
|
||||
None => {
|
||||
let (value, import) = to_value_ns(module_def)?;
|
||||
Some(ResolveValueResult::ValueNs(value, import))
|
||||
Some((ResolveValueResult::ValueNs(value, import), prefix_info))
|
||||
}
|
||||
Some(idx) => {
|
||||
Some(unresolved_idx) => {
|
||||
let def = module_def.take_types_full()?;
|
||||
let ty = match def.def {
|
||||
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
|
||||
|
@ -948,7 +977,7 @@ impl ModuleItemMap {
|
|||
| ModuleDefId::MacroId(_)
|
||||
| ModuleDefId::StaticId(_) => return None,
|
||||
};
|
||||
Some(ResolveValueResult::Partial(ty, idx, def.import))
|
||||
Some((ResolveValueResult::Partial(ty, unresolved_idx, def.import), prefix_info))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -958,7 +987,7 @@ impl ModuleItemMap {
|
|||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
|
||||
let (module_def, idx) =
|
||||
let (module_def, idx, _) =
|
||||
self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
|
||||
let (res, import) = to_type_ns(module_def)?;
|
||||
Some((res, idx, import))
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
pub(crate) mod cast;
|
||||
pub(crate) mod closure;
|
||||
mod coerce;
|
||||
mod diagnostics;
|
||||
mod expr;
|
||||
mod mutability;
|
||||
mod pat;
|
||||
|
@ -57,15 +58,20 @@ use crate::{
|
|||
db::HirDatabase,
|
||||
fold_tys,
|
||||
generics::Generics,
|
||||
infer::{coerce::CoerceMany, expr::ExprIsRead, unify::InferenceTable},
|
||||
lower::{ImplTraitLoweringMode, TyLoweringDiagnostic},
|
||||
infer::{
|
||||
coerce::CoerceMany,
|
||||
diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext},
|
||||
expr::ExprIsRead,
|
||||
unify::InferenceTable,
|
||||
},
|
||||
lower::{diagnostics::TyLoweringDiagnostic, ImplTraitLoweringMode},
|
||||
mir::MirSpan,
|
||||
to_assoc_type_id,
|
||||
traits::FnTrait,
|
||||
utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
|
||||
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
|
||||
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, ProjectionTy,
|
||||
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode,
|
||||
PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
};
|
||||
|
||||
// This lint has a false positive here. See the link below for details.
|
||||
|
@ -276,6 +282,10 @@ pub enum InferenceDiagnostic {
|
|||
source: InferenceTyDiagnosticSource,
|
||||
diag: TyLoweringDiagnostic,
|
||||
},
|
||||
PathDiagnostic {
|
||||
node: ExprOrPatId,
|
||||
diag: PathLoweringDiagnostic,
|
||||
},
|
||||
}
|
||||
|
||||
/// A mismatch between an expected and an inferred type.
|
||||
|
@ -442,6 +452,7 @@ pub struct InferenceResult {
|
|||
/// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of
|
||||
/// that which allows us to resolve a [`TupleFieldId`]s type.
|
||||
pub tuple_field_access_types: FxHashMap<TupleId, Substitution>,
|
||||
/// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead.
|
||||
pub diagnostics: Vec<InferenceDiagnostic>,
|
||||
pub type_of_expr: ArenaMap<ExprId, Ty>,
|
||||
/// For each pattern record the type it resolves to.
|
||||
|
@ -579,6 +590,8 @@ pub(crate) struct InferenceContext<'a> {
|
|||
pub(crate) db: &'a dyn HirDatabase,
|
||||
pub(crate) owner: DefWithBodyId,
|
||||
pub(crate) body: &'a Body,
|
||||
/// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext
|
||||
/// and resolve the path via its methods. This will ensure proper error reporting.
|
||||
pub(crate) resolver: Resolver,
|
||||
generics: OnceCell<Option<Generics>>,
|
||||
table: unify::InferenceTable<'a>,
|
||||
|
@ -620,6 +633,8 @@ pub(crate) struct InferenceContext<'a> {
|
|||
/// comment on `InferenceContext::sort_closures`
|
||||
closure_dependencies: FxHashMap<ClosureId, Vec<ClosureId>>,
|
||||
deferred_closures: FxHashMap<ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>,
|
||||
|
||||
diagnostics: Diagnostics,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -701,6 +716,7 @@ impl<'a> InferenceContext<'a> {
|
|||
deferred_closures: FxHashMap::default(),
|
||||
closure_dependencies: FxHashMap::default(),
|
||||
inside_assignment: false,
|
||||
diagnostics: Diagnostics::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -724,8 +740,10 @@ impl<'a> InferenceContext<'a> {
|
|||
mut result,
|
||||
mut deferred_cast_checks,
|
||||
tuple_field_accesses_rev,
|
||||
diagnostics,
|
||||
..
|
||||
} = self;
|
||||
let mut diagnostics = diagnostics.finish();
|
||||
// Destructure every single field so whenever new fields are added to `InferenceResult` we
|
||||
// don't forget to handle them here.
|
||||
let InferenceResult {
|
||||
|
@ -733,7 +751,6 @@ impl<'a> InferenceContext<'a> {
|
|||
field_resolutions: _,
|
||||
variant_resolutions: _,
|
||||
assoc_resolutions,
|
||||
diagnostics,
|
||||
type_of_expr,
|
||||
type_of_pat,
|
||||
type_of_binding,
|
||||
|
@ -752,6 +769,7 @@ impl<'a> InferenceContext<'a> {
|
|||
mutated_bindings_in_closure: _,
|
||||
tuple_field_access_types: _,
|
||||
coercion_casts,
|
||||
diagnostics: _,
|
||||
} = &mut result;
|
||||
table.fallback_if_possible();
|
||||
|
||||
|
@ -866,6 +884,9 @@ impl<'a> InferenceContext<'a> {
|
|||
*has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown());
|
||||
})
|
||||
.collect();
|
||||
|
||||
result.diagnostics = diagnostics;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -1238,41 +1259,28 @@ impl<'a> InferenceContext<'a> {
|
|||
self.result.type_of_binding.insert(id, ty);
|
||||
}
|
||||
|
||||
fn push_diagnostic(&mut self, diagnostic: InferenceDiagnostic) {
|
||||
self.result.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
fn push_ty_diagnostics(
|
||||
&mut self,
|
||||
source: InferenceTyDiagnosticSource,
|
||||
diagnostics: Vec<TyLoweringDiagnostic>,
|
||||
) {
|
||||
self.result.diagnostics.extend(
|
||||
diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }),
|
||||
);
|
||||
fn push_diagnostic(&self, diagnostic: InferenceDiagnostic) {
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
fn with_ty_lowering<R>(
|
||||
&mut self,
|
||||
types_map: &TypesMap,
|
||||
types_source: InferenceTyDiagnosticSource,
|
||||
f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
|
||||
f: impl FnOnce(&mut TyLoweringContext<'_>) -> R,
|
||||
) -> R {
|
||||
let mut ctx = crate::lower::TyLoweringContext::new(
|
||||
let mut ctx = TyLoweringContext::new(
|
||||
self.db,
|
||||
&self.resolver,
|
||||
types_map,
|
||||
self.owner.into(),
|
||||
&self.diagnostics,
|
||||
types_source,
|
||||
);
|
||||
let result = f(&mut ctx);
|
||||
self.push_ty_diagnostics(types_source, ctx.diagnostics);
|
||||
result
|
||||
f(&mut ctx)
|
||||
}
|
||||
|
||||
fn with_body_ty_lowering<R>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
|
||||
) -> R {
|
||||
fn with_body_ty_lowering<R>(&mut self, f: impl FnOnce(&mut TyLoweringContext<'_>) -> R) -> R {
|
||||
self.with_ty_lowering(&self.body.types, InferenceTyDiagnosticSource::Body, f)
|
||||
}
|
||||
|
||||
|
@ -1451,51 +1459,55 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_variant(&mut self, path: Option<&Path>, value_ns: bool) -> (Ty, Option<VariantId>) {
|
||||
fn resolve_variant(
|
||||
&mut self,
|
||||
node: ExprOrPatId,
|
||||
path: Option<&Path>,
|
||||
value_ns: bool,
|
||||
) -> (Ty, Option<VariantId>) {
|
||||
let path = match path {
|
||||
Some(path) => path,
|
||||
None => return (self.err_ty(), None),
|
||||
};
|
||||
let mut ctx = crate::lower::TyLoweringContext::new(
|
||||
let mut ctx = TyLoweringContext::new(
|
||||
self.db,
|
||||
&self.resolver,
|
||||
&self.body.types,
|
||||
self.owner.into(),
|
||||
&self.diagnostics,
|
||||
InferenceTyDiagnosticSource::Body,
|
||||
);
|
||||
let (resolution, unresolved) = if value_ns {
|
||||
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, HygieneId::ROOT) {
|
||||
Some(ResolveValueResult::ValueNs(value, _)) => match value {
|
||||
let Some(res) = ctx.resolve_path_in_value_ns(path, node, HygieneId::ROOT) else {
|
||||
return (self.err_ty(), None);
|
||||
};
|
||||
match res {
|
||||
ResolveValueResult::ValueNs(value, _) => match value {
|
||||
ValueNs::EnumVariantId(var) => {
|
||||
let substs = ctx.substs_from_path(path, var.into(), true);
|
||||
self.push_ty_diagnostics(
|
||||
InferenceTyDiagnosticSource::Body,
|
||||
ctx.diagnostics,
|
||||
);
|
||||
drop(ctx);
|
||||
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
|
||||
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
|
||||
return (ty, Some(var.into()));
|
||||
}
|
||||
ValueNs::StructId(strukt) => {
|
||||
let substs = ctx.substs_from_path(path, strukt.into(), true);
|
||||
self.push_ty_diagnostics(
|
||||
InferenceTyDiagnosticSource::Body,
|
||||
ctx.diagnostics,
|
||||
);
|
||||
drop(ctx);
|
||||
let ty = self.db.ty(strukt.into());
|
||||
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
|
||||
return (ty, Some(strukt.into()));
|
||||
}
|
||||
ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None),
|
||||
_ => return (self.err_ty(), None),
|
||||
_ => {
|
||||
drop(ctx);
|
||||
return (self.err_ty(), None);
|
||||
}
|
||||
},
|
||||
Some(ResolveValueResult::Partial(typens, unresolved, _)) => {
|
||||
(typens, Some(unresolved))
|
||||
}
|
||||
None => return (self.err_ty(), None),
|
||||
ResolveValueResult::Partial(typens, unresolved, _) => (typens, Some(unresolved)),
|
||||
}
|
||||
} else {
|
||||
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
|
||||
Some((it, idx, _)) => (it, idx),
|
||||
match ctx.resolve_path_in_type_ns(path, node) {
|
||||
Some((it, idx)) => (it, idx),
|
||||
None => return (self.err_ty(), None),
|
||||
}
|
||||
};
|
||||
|
@ -1506,21 +1518,21 @@ impl<'a> InferenceContext<'a> {
|
|||
return match resolution {
|
||||
TypeNs::AdtId(AdtId::StructId(strukt)) => {
|
||||
let substs = ctx.substs_from_path(path, strukt.into(), true);
|
||||
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
|
||||
drop(ctx);
|
||||
let ty = self.db.ty(strukt.into());
|
||||
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
|
||||
forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
|
||||
}
|
||||
TypeNs::AdtId(AdtId::UnionId(u)) => {
|
||||
let substs = ctx.substs_from_path(path, u.into(), true);
|
||||
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
|
||||
drop(ctx);
|
||||
let ty = self.db.ty(u.into());
|
||||
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
|
||||
forbid_unresolved_segments((ty, Some(u.into())), unresolved)
|
||||
}
|
||||
TypeNs::EnumVariantId(var) => {
|
||||
let substs = ctx.substs_from_path(path, var.into(), true);
|
||||
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
|
||||
drop(ctx);
|
||||
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
|
||||
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
|
||||
forbid_unresolved_segments((ty, Some(var.into())), unresolved)
|
||||
|
@ -1531,6 +1543,7 @@ impl<'a> InferenceContext<'a> {
|
|||
let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
|
||||
|
||||
let Some(mut remaining_idx) = unresolved else {
|
||||
drop(ctx);
|
||||
return self.resolve_variant_on_alias(ty, None, mod_path);
|
||||
};
|
||||
|
||||
|
@ -1538,6 +1551,7 @@ impl<'a> InferenceContext<'a> {
|
|||
|
||||
// We need to try resolving unresolved segments one by one because each may resolve
|
||||
// to a projection, which `TyLoweringContext` cannot handle on its own.
|
||||
let mut tried_resolving_once = false;
|
||||
while !remaining_segments.is_empty() {
|
||||
let resolved_segment = path.segments().get(remaining_idx - 1).unwrap();
|
||||
let current_segment = remaining_segments.take(1);
|
||||
|
@ -1558,18 +1572,27 @@ impl<'a> InferenceContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
if tried_resolving_once {
|
||||
// FIXME: with `inherent_associated_types` this is allowed, but our `lower_partly_resolved_path()`
|
||||
// will need to be updated to err at the correct segment.
|
||||
//
|
||||
// We need to stop here because otherwise the segment index passed to `lower_partly_resolved_path()`
|
||||
// will be incorrect, and that can mess up error reporting.
|
||||
break;
|
||||
}
|
||||
|
||||
// `lower_partly_resolved_path()` returns `None` as type namespace unless
|
||||
// `remaining_segments` is empty, which is never the case here. We don't know
|
||||
// which namespace the new `ty` is in until normalized anyway.
|
||||
(ty, _) = ctx.lower_partly_resolved_path(
|
||||
node,
|
||||
resolution,
|
||||
resolved_segment,
|
||||
current_segment,
|
||||
(remaining_idx - 1) as u32,
|
||||
false,
|
||||
&mut |_, _reason| {
|
||||
// FIXME: Report an error.
|
||||
},
|
||||
);
|
||||
tried_resolving_once = true;
|
||||
|
||||
ty = self.table.insert_type_vars(ty);
|
||||
ty = self.table.normalize_associated_types_in(ty);
|
||||
|
@ -1582,7 +1605,7 @@ impl<'a> InferenceContext<'a> {
|
|||
remaining_idx += 1;
|
||||
remaining_segments = remaining_segments.skip(1);
|
||||
}
|
||||
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
|
||||
drop(ctx);
|
||||
|
||||
let variant = ty.as_adt().and_then(|(id, _)| match id {
|
||||
AdtId::StructId(s) => Some(VariantId::StructId(s)),
|
||||
|
@ -1601,7 +1624,7 @@ impl<'a> InferenceContext<'a> {
|
|||
};
|
||||
let substs =
|
||||
ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None);
|
||||
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
|
||||
drop(ctx);
|
||||
let ty = self.db.ty(it.into());
|
||||
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
|
||||
|
||||
|
|
128
crates/hir-ty/src/infer/diagnostics.rs
Normal file
128
crates/hir-ty/src/infer/diagnostics.rs
Normal file
|
@ -0,0 +1,128 @@
|
|||
//! This file contains the [`Diagnostics`] type used during inference,
|
||||
//! and a wrapper around [`TyLoweringContext`] ([`InferenceTyLoweringContext`]) that replaces
|
||||
//! it and takes care of diagnostics in inference.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use hir_def::body::HygieneId;
|
||||
use hir_def::hir::ExprOrPatId;
|
||||
use hir_def::path::{Path, PathSegment, PathSegments};
|
||||
use hir_def::resolver::{ResolveValueResult, Resolver, TypeNs};
|
||||
use hir_def::type_ref::TypesMap;
|
||||
use hir_def::TypeOwnerId;
|
||||
|
||||
use crate::db::HirDatabase;
|
||||
use crate::{
|
||||
InferenceDiagnostic, InferenceTyDiagnosticSource, Ty, TyLoweringContext, TyLoweringDiagnostic,
|
||||
};
|
||||
|
||||
// Unfortunately, this struct needs to use interior mutability (but we encapsulate it)
|
||||
// because when lowering types and paths we hold a `TyLoweringContext` that holds a reference
|
||||
// to our resolver and so we cannot have mutable reference, but we really want to have
|
||||
// ability to dispatch diagnostics during this work otherwise the code becomes a complete mess.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub(super) struct Diagnostics(RefCell<Vec<InferenceDiagnostic>>);
|
||||
|
||||
impl Diagnostics {
|
||||
pub(super) fn push(&self, diagnostic: InferenceDiagnostic) {
|
||||
self.0.borrow_mut().push(diagnostic);
|
||||
}
|
||||
|
||||
fn push_ty_diagnostics(
|
||||
&self,
|
||||
source: InferenceTyDiagnosticSource,
|
||||
diagnostics: Vec<TyLoweringDiagnostic>,
|
||||
) {
|
||||
self.0.borrow_mut().extend(
|
||||
diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn finish(self) -> Vec<InferenceDiagnostic> {
|
||||
self.0.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct InferenceTyLoweringContext<'a> {
|
||||
ctx: TyLoweringContext<'a>,
|
||||
diagnostics: &'a Diagnostics,
|
||||
source: InferenceTyDiagnosticSource,
|
||||
}
|
||||
|
||||
impl<'a> InferenceTyLoweringContext<'a> {
|
||||
pub(super) fn new(
|
||||
db: &'a dyn HirDatabase,
|
||||
resolver: &'a Resolver,
|
||||
types_map: &'a TypesMap,
|
||||
owner: TypeOwnerId,
|
||||
diagnostics: &'a Diagnostics,
|
||||
source: InferenceTyDiagnosticSource,
|
||||
) -> Self {
|
||||
Self { ctx: TyLoweringContext::new(db, resolver, types_map, owner), diagnostics, source }
|
||||
}
|
||||
|
||||
pub(super) fn resolve_path_in_type_ns(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
node: ExprOrPatId,
|
||||
) -> Option<(TypeNs, Option<usize>)> {
|
||||
let diagnostics = self.diagnostics;
|
||||
self.ctx.resolve_path_in_type_ns(path, &mut |_, diag| {
|
||||
diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag })
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn resolve_path_in_value_ns(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
node: ExprOrPatId,
|
||||
hygiene_id: HygieneId,
|
||||
) -> Option<ResolveValueResult> {
|
||||
let diagnostics = self.diagnostics;
|
||||
self.ctx.resolve_path_in_value_ns(path, hygiene_id, &mut |_, diag| {
|
||||
diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag })
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn lower_partly_resolved_path(
|
||||
&mut self,
|
||||
node: ExprOrPatId,
|
||||
resolution: TypeNs,
|
||||
resolved_segment: PathSegment<'_>,
|
||||
remaining_segments: PathSegments<'_>,
|
||||
resolved_segment_idx: u32,
|
||||
infer_args: bool,
|
||||
) -> (Ty, Option<TypeNs>) {
|
||||
let diagnostics = self.diagnostics;
|
||||
self.ctx.lower_partly_resolved_path(
|
||||
resolution,
|
||||
resolved_segment,
|
||||
remaining_segments,
|
||||
resolved_segment_idx,
|
||||
infer_args,
|
||||
&mut |_, diag| diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag }),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for InferenceTyLoweringContext<'a> {
|
||||
type Target = TyLoweringContext<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.ctx
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for InferenceTyLoweringContext<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.ctx
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InferenceTyLoweringContext<'_> {
|
||||
fn drop(&mut self) {
|
||||
self.diagnostics
|
||||
.push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics));
|
||||
}
|
||||
}
|
|
@ -531,7 +531,7 @@ impl InferenceContext<'_> {
|
|||
(params, ret_ty)
|
||||
}
|
||||
None => {
|
||||
self.result.diagnostics.push(InferenceDiagnostic::ExpectedFunction {
|
||||
self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
|
||||
call_expr: tgt_expr,
|
||||
found: callee_ty.clone(),
|
||||
});
|
||||
|
@ -707,7 +707,7 @@ impl InferenceContext<'_> {
|
|||
self.result.standard_types.never.clone()
|
||||
}
|
||||
Expr::RecordLit { path, fields, spread, .. } => {
|
||||
let (ty, def_id) = self.resolve_variant(path.as_deref(), false);
|
||||
let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path.as_deref(), false);
|
||||
|
||||
if let Some(t) = expected.only_has_type(&mut self.table) {
|
||||
self.unify(&ty, &t);
|
||||
|
@ -1816,9 +1816,10 @@ impl InferenceContext<'_> {
|
|||
if !is_public {
|
||||
if let Either::Left(field) = field_id {
|
||||
// FIXME: Merge this diagnostic into UnresolvedField?
|
||||
self.result
|
||||
.diagnostics
|
||||
.push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field });
|
||||
self.push_diagnostic(InferenceDiagnostic::PrivateField {
|
||||
expr: tgt_expr,
|
||||
field,
|
||||
});
|
||||
}
|
||||
}
|
||||
ty
|
||||
|
@ -1835,7 +1836,7 @@ impl InferenceContext<'_> {
|
|||
VisibleFromModule::Filter(self.resolver.module()),
|
||||
name,
|
||||
);
|
||||
self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField {
|
||||
self.push_diagnostic(InferenceDiagnostic::UnresolvedField {
|
||||
expr: tgt_expr,
|
||||
receiver: receiver_ty.clone(),
|
||||
name: name.clone(),
|
||||
|
@ -1927,7 +1928,7 @@ impl InferenceContext<'_> {
|
|||
},
|
||||
);
|
||||
|
||||
self.result.diagnostics.push(InferenceDiagnostic::UnresolvedMethodCall {
|
||||
self.push_diagnostic(InferenceDiagnostic::UnresolvedMethodCall {
|
||||
expr: tgt_expr,
|
||||
receiver: receiver_ty.clone(),
|
||||
name: method_name.clone(),
|
||||
|
|
|
@ -35,7 +35,7 @@ impl InferenceContext<'_> {
|
|||
ellipsis: Option<u32>,
|
||||
subs: &[PatId],
|
||||
) -> Ty {
|
||||
let (ty, def) = self.resolve_variant(path, true);
|
||||
let (ty, def) = self.resolve_variant(id.into(), path, true);
|
||||
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
|
||||
if let Some(variant) = def {
|
||||
self.write_variant_resolution(id.into(), variant);
|
||||
|
@ -115,7 +115,7 @@ impl InferenceContext<'_> {
|
|||
id: PatId,
|
||||
subs: impl ExactSizeIterator<Item = (Name, PatId)>,
|
||||
) -> Ty {
|
||||
let (ty, def) = self.resolve_variant(path, false);
|
||||
let (ty, def) = self.resolve_variant(id.into(), path, false);
|
||||
if let Some(variant) = def {
|
||||
self.write_variant_resolution(id.into(), variant);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ use crate::{
|
|||
builder::ParamKind,
|
||||
consteval, error_lifetime,
|
||||
generics::generics,
|
||||
infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext,
|
||||
method_resolution::{self, VisibleFromModule},
|
||||
to_chalk_trait_id, InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty,
|
||||
TyBuilder, TyExt, TyKind, ValueTyDefId,
|
||||
|
@ -147,36 +148,38 @@ impl InferenceContext<'_> {
|
|||
path: &Path,
|
||||
id: ExprOrPatId,
|
||||
) -> Option<(ValueNs, Option<chalk_ir::Substitution<Interner>>)> {
|
||||
// Don't use `self.make_ty()` here as we need `orig_ns`.
|
||||
let mut ctx = TyLoweringContext::new(
|
||||
self.db,
|
||||
&self.resolver,
|
||||
&self.body.types,
|
||||
self.owner.into(),
|
||||
&self.diagnostics,
|
||||
InferenceTyDiagnosticSource::Body,
|
||||
);
|
||||
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
|
||||
let last = path.segments().last()?;
|
||||
|
||||
// Don't use `self.make_ty()` here as we need `orig_ns`.
|
||||
let mut ctx = crate::lower::TyLoweringContext::new(
|
||||
self.db,
|
||||
&self.resolver,
|
||||
&self.body.types,
|
||||
self.owner.into(),
|
||||
);
|
||||
let (ty, orig_ns) = ctx.lower_ty_ext(type_ref);
|
||||
let ty = self.table.insert_type_vars(ty);
|
||||
let ty = self.table.normalize_associated_types_in(ty);
|
||||
|
||||
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
|
||||
let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty);
|
||||
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
|
||||
drop(ctx);
|
||||
let ty = self.table.insert_type_vars(ty);
|
||||
let ty = self.table.normalize_associated_types_in(ty);
|
||||
self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
|
||||
} else {
|
||||
let hygiene = self.body.expr_or_pat_path_hygiene(id);
|
||||
// FIXME: report error, unresolved first path segment
|
||||
let value_or_partial =
|
||||
self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, hygiene)?;
|
||||
let value_or_partial = ctx.resolve_path_in_value_ns(path, id, hygiene)?;
|
||||
drop(ctx);
|
||||
|
||||
match value_or_partial {
|
||||
ResolveValueResult::ValueNs(it, _) => (it, None),
|
||||
ResolveValueResult::Partial(def, remaining_index, _) => self
|
||||
.resolve_assoc_item(def, path, remaining_index, id)
|
||||
.resolve_assoc_item(id, def, path, remaining_index, id)
|
||||
.map(|(it, substs)| (it, Some(substs)))?,
|
||||
}
|
||||
};
|
||||
|
@ -212,6 +215,7 @@ impl InferenceContext<'_> {
|
|||
|
||||
fn resolve_assoc_item(
|
||||
&mut self,
|
||||
node: ExprOrPatId,
|
||||
def: TypeNs,
|
||||
path: &Path,
|
||||
remaining_index: usize,
|
||||
|
@ -260,17 +264,23 @@ impl InferenceContext<'_> {
|
|||
// as Iterator>::Item::default`)
|
||||
let remaining_segments_for_ty =
|
||||
remaining_segments.take(remaining_segments.len() - 1);
|
||||
let (ty, _) = self.with_body_ty_lowering(|ctx| {
|
||||
ctx.lower_partly_resolved_path(
|
||||
def,
|
||||
resolved_segment,
|
||||
remaining_segments_for_ty,
|
||||
true,
|
||||
&mut |_, _reason| {
|
||||
// FIXME: Report an error.
|
||||
},
|
||||
)
|
||||
});
|
||||
let mut ctx = TyLoweringContext::new(
|
||||
self.db,
|
||||
&self.resolver,
|
||||
&self.body.types,
|
||||
self.owner.into(),
|
||||
&self.diagnostics,
|
||||
InferenceTyDiagnosticSource::Body,
|
||||
);
|
||||
let (ty, _) = ctx.lower_partly_resolved_path(
|
||||
node,
|
||||
def,
|
||||
resolved_segment,
|
||||
remaining_segments_for_ty,
|
||||
(remaining_index - 1) as u32,
|
||||
true,
|
||||
);
|
||||
drop(ctx);
|
||||
if ty.is_unknown() {
|
||||
return None;
|
||||
}
|
||||
|
|
|
@ -88,10 +88,10 @@ pub use infer::{
|
|||
PointerCast,
|
||||
};
|
||||
pub use interner::Interner;
|
||||
pub use lower::diagnostics::*;
|
||||
pub use lower::{
|
||||
associated_type_shorthand_candidates, GenericArgsProhibitedReason, ImplTraitLoweringMode,
|
||||
ParamLoweringMode, TyDefId, TyLoweringContext, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
|
||||
ValueTyDefId,
|
||||
associated_type_shorthand_candidates, ImplTraitLoweringMode, ParamLoweringMode, TyDefId,
|
||||
TyLoweringContext, ValueTyDefId,
|
||||
};
|
||||
pub use mapping::{
|
||||
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
//! - Building the type for an item: This happens through the `ty` query.
|
||||
//!
|
||||
//! This usually involves resolving names, collecting generic arguments etc.
|
||||
pub(crate) mod diagnostics;
|
||||
|
||||
use std::{
|
||||
cell::OnceCell,
|
||||
iter, mem,
|
||||
|
@ -21,6 +23,7 @@ use chalk_ir::{
|
|||
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
body::HygieneId,
|
||||
builtin_type::BuiltinType,
|
||||
data::adt::StructKind,
|
||||
expander::Expander,
|
||||
|
@ -29,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,
|
||||
|
@ -59,6 +62,7 @@ use crate::{
|
|||
db::HirDatabase,
|
||||
error_lifetime,
|
||||
generics::{generics, trait_self_param_idx, Generics},
|
||||
lower::diagnostics::*,
|
||||
make_binders,
|
||||
mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk},
|
||||
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
|
||||
|
@ -102,31 +106,6 @@ 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,
|
||||
|
@ -536,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,
|
||||
|
@ -548,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 {
|
||||
|
@ -584,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_) => {
|
||||
|
@ -655,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 {
|
||||
|
@ -718,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),
|
||||
|
@ -737,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) => {
|
||||
|
@ -754,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() {
|
||||
|
@ -761,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
|
||||
|
@ -774,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()),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1107,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 },
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -1119,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,
|
||||
|
|
36
crates/hir-ty/src/lower/diagnostics.rs
Normal file
36
crates/hir-ty/src/lower/diagnostics.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
//! This files contains the declaration of diagnostics kinds for ty and path lowering.
|
||||
|
||||
use either::Either;
|
||||
use hir_def::type_ref::TypeRefId;
|
||||
|
||||
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 {
|
||||
PathDiagnostic(PathLoweringDiagnostic),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GenericArgsProhibitedReason {
|
||||
Module,
|
||||
TyParam,
|
||||
SelfTy,
|
||||
PrimitiveTy,
|
||||
Const,
|
||||
Static,
|
||||
/// 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, PartialEq, Eq, Clone)]
|
||||
pub enum PathLoweringDiagnostic {
|
||||
GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason },
|
||||
}
|
|
@ -15,12 +15,12 @@ use hir_expand::{name::Name, HirFileId, InFile};
|
|||
use hir_ty::{
|
||||
db::HirDatabase,
|
||||
diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
|
||||
CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic,
|
||||
TyLoweringDiagnosticKind,
|
||||
CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathLoweringDiagnostic,
|
||||
TyLoweringDiagnostic, TyLoweringDiagnosticKind,
|
||||
};
|
||||
use syntax::{
|
||||
ast::{self, HasGenericArgs},
|
||||
AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
|
||||
match_ast, AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
|
||||
};
|
||||
use triomphe::Arc;
|
||||
|
||||
|
@ -674,6 +674,39 @@ impl AnyDiagnostic {
|
|||
};
|
||||
Self::ty_diagnostic(diag, source_map, db)?
|
||||
}
|
||||
InferenceDiagnostic::PathDiagnostic { node, diag } => {
|
||||
let source = expr_or_pat_syntax(*node)?;
|
||||
let syntax = source.value.to_node(&db.parse_or_expand(source.file_id));
|
||||
let path = match_ast! {
|
||||
match (syntax.syntax()) {
|
||||
ast::RecordExpr(it) => it.path()?,
|
||||
ast::RecordPat(it) => it.path()?,
|
||||
ast::TupleStructPat(it) => it.path()?,
|
||||
ast::PathExpr(it) => it.path()?,
|
||||
ast::PathPat(it) => it.path()?,
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
Self::path_diagnostic(diag, source.with_value(path))?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn path_diagnostic(
|
||||
diag: &PathLoweringDiagnostic,
|
||||
path: InFile<ast::Path>,
|
||||
) -> Option<AnyDiagnostic> {
|
||||
Some(match diag {
|
||||
&PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
|
||||
let segment = hir_segment_to_ast_segment(&path.value, 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 = path.with_value(args);
|
||||
GenericArgsProhibited { args, reason }.into()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -693,17 +726,10 @@ impl AnyDiagnostic {
|
|||
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 } => {
|
||||
Some(match &diag.kind {
|
||||
TyLoweringDiagnosticKind::PathDiagnostic(diag) => {
|
||||
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()
|
||||
Self::path_diagnostic(diag, source.with_value(syntax.path()?))?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ fn describe_reason(reason: GenericArgsProhibitedReason) -> String {
|
|||
return "you can specify generic arguments on either the enum or the variant, but not both"
|
||||
.to_owned();
|
||||
}
|
||||
GenericArgsProhibitedReason::Const => "constants",
|
||||
GenericArgsProhibitedReason::Static => "statics",
|
||||
};
|
||||
format!("generic arguments are not allowed on {kind}")
|
||||
}
|
||||
|
@ -435,6 +437,144 @@ type T = bool<i32>;
|
|||
impl Trait for () {
|
||||
type Assoc = i32<bool>;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_record_expr() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar { pub field: i32 }
|
||||
}
|
||||
fn baz() {
|
||||
let _ = foo::<()>::Bar { field: 0 };
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_record_pat() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar { field: i32 }
|
||||
}
|
||||
fn baz(v: foo::Bar) {
|
||||
let foo::<()>::Bar { .. } = v;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_tuple_struct_pat() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar(i32);
|
||||
}
|
||||
fn baz(v: foo::Bar) {
|
||||
let foo::<()>::Bar(..) = v;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_path_pat() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar;
|
||||
}
|
||||
fn baz(v: foo::Bar) {
|
||||
let foo::<()>::Bar = v;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_path_expr() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
mod foo {
|
||||
pub struct Bar;
|
||||
}
|
||||
fn baz() {
|
||||
let _ = foo::<()>::Bar;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn const_and_static() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
const CONST: i32 = 0;
|
||||
static STATIC: i32 = 0;
|
||||
fn baz() {
|
||||
let _ = CONST::<()>;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on constants
|
||||
let _ = STATIC::<()>;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on statics
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_variant() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
enum Enum<A> {
|
||||
Variant(A),
|
||||
}
|
||||
mod enum_ {
|
||||
pub(super) use super::Enum::Variant as V;
|
||||
}
|
||||
fn baz() {
|
||||
let v = Enum::<()>::Variant::<()>(());
|
||||
// ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both
|
||||
let Enum::<()>::Variant::<()>(..) = v;
|
||||
// ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both
|
||||
let _ = Enum::<()>::Variant(());
|
||||
let _ = Enum::Variant::<()>(());
|
||||
}
|
||||
fn foo() {
|
||||
use Enum::Variant;
|
||||
let _ = Variant::<()>(());
|
||||
let _ = enum_::V::<()>(());
|
||||
let _ = enum_::<()>::V::<()>(());
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dyn_trait() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
mod foo {
|
||||
pub trait Trait {}
|
||||
}
|
||||
|
||||
fn bar() {
|
||||
let _: &dyn foo::<()>::Trait;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
|
||||
let _: &foo::<()>::Trait;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue