Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-def/src/nameres.rs8
-rw-r--r--crates/hir-def/src/nameres/collector.rs4
-rw-r--r--crates/hir-def/src/nameres/path_resolution.rs79
-rw-r--r--crates/hir-def/src/path.rs15
-rw-r--r--crates/hir-def/src/resolver.rs103
-rw-r--r--crates/hir-ty/src/infer.rs129
-rw-r--r--crates/hir-ty/src/infer/diagnostics.rs128
-rw-r--r--crates/hir-ty/src/infer/expr.rs15
-rw-r--r--crates/hir-ty/src/infer/pat.rs4
-rw-r--r--crates/hir-ty/src/infer/path.rs54
-rw-r--r--crates/hir-ty/src/lib.rs6
-rw-r--r--crates/hir-ty/src/lower.rs383
-rw-r--r--crates/hir-ty/src/lower/diagnostics.rs36
-rw-r--r--crates/hir/src/diagnostics.rs52
-rw-r--r--crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs140
15 files changed, 881 insertions, 275 deletions
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index 9bd7d38f0a..39d383f015 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -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.
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index f391cc41c1..318a8d5aa1 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -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)),
);
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index 8eb195680d..9573697a87 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -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(
diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs
index 44e132061a..e59c37104d 100644
--- a/crates/hir-def/src/path.rs
+++ b/crates/hir-def/src/path.rs
@@ -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()
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index f4dfd42a30..4f15c8091c 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -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))
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index dbee5a1a91..25bb3a76de 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -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));
diff --git a/crates/hir-ty/src/infer/diagnostics.rs b/crates/hir-ty/src/infer/diagnostics.rs
new file mode 100644
index 0000000000..032dc37899
--- /dev/null
+++ b/crates/hir-ty/src/infer/diagnostics.rs
@@ -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));
+ }
+}
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index a13541be69..3dccd536be 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -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(),
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 50e761196e..00398f019d 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -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);
}
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index a6296c3af2..d79bf9a05e 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -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;
}
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 8bb90ca31e..224fcf313a 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -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,
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index b23f2749ab..4bfa51c849 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -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,
diff --git a/crates/hir-ty/src/lower/diagnostics.rs b/crates/hir-ty/src/lower/diagnostics.rs
new file mode 100644
index 0000000000..7fe196cdbb
--- /dev/null
+++ b/crates/hir-ty/src/lower/diagnostics.rs
@@ -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 },
+}
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index cbb1ed95ed..fc77d1889c 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -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()?))?
}
})
}
diff --git a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
index a319a0bcf6..a6d2ed3223 100644
--- a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
+++ b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
@@ -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}")
}
@@ -439,4 +441,142 @@ impl Trait for () {
"#,
);
}
+
+ #[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
+}
+ "#,
+ );
+ }
}