mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 11:20:54 +00:00
Merge pull request #19479 from ChayimFriedman2/generic-mismatch
feat: Add two new diagnostics: one for mismatch in generic arguments count, and another for mismatch in their kind
This commit is contained in:
commit
9632b386d1
@ -35,7 +35,9 @@ use crate::{
|
||||
};
|
||||
|
||||
pub use self::body::{Body, BodySourceMap};
|
||||
pub use self::lower::hir_segment_to_ast_segment;
|
||||
pub use self::lower::{
|
||||
hir_assoc_type_binding_to_ast, hir_generic_arg_to_ast, hir_segment_to_ast_segment,
|
||||
};
|
||||
|
||||
/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -788,6 +788,7 @@ impl ExprCollector<'_> {
|
||||
node: ast::GenericArgList,
|
||||
impl_trait_lower_fn: &mut impl FnMut(ThinVec<TypeBound>) -> TypeRef,
|
||||
) -> Option<GenericArgs> {
|
||||
// This needs to be kept in sync with `hir_generic_arg_to_ast()`.
|
||||
let mut args = Vec::new();
|
||||
let mut bindings = Vec::new();
|
||||
for generic_arg in node.generic_args() {
|
||||
@ -797,6 +798,7 @@ impl ExprCollector<'_> {
|
||||
args.push(GenericArg::Type(type_ref));
|
||||
}
|
||||
ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
|
||||
// This needs to be kept in sync with `hir_assoc_type_binding_to_ast()`.
|
||||
if assoc_type_arg.param_list().is_some() {
|
||||
// We currently ignore associated return type bounds.
|
||||
continue;
|
||||
@ -3228,3 +3230,33 @@ enum ArgumentType {
|
||||
Format(FormatTrait),
|
||||
Usize,
|
||||
}
|
||||
|
||||
/// This function find the AST fragment that corresponds to an `AssociatedTypeBinding` in the HIR.
|
||||
pub fn hir_assoc_type_binding_to_ast(
|
||||
segment_args: &ast::GenericArgList,
|
||||
binding_idx: u32,
|
||||
) -> Option<ast::AssocTypeArg> {
|
||||
segment_args
|
||||
.generic_args()
|
||||
.filter_map(|arg| match arg {
|
||||
ast::GenericArg::AssocTypeArg(it) => Some(it),
|
||||
_ => None,
|
||||
})
|
||||
.filter(|binding| binding.param_list().is_none() && binding.name_ref().is_some())
|
||||
.nth(binding_idx as usize)
|
||||
}
|
||||
|
||||
/// This function find the AST generic argument from the one in the HIR. Does not support the `Self` argument.
|
||||
pub fn hir_generic_arg_to_ast(
|
||||
args: &ast::GenericArgList,
|
||||
arg_idx: u32,
|
||||
has_self_arg: bool,
|
||||
) -> Option<ast::GenericArg> {
|
||||
args.generic_args()
|
||||
.filter(|arg| match arg {
|
||||
ast::GenericArg::AssocTypeArg(_) => false,
|
||||
ast::GenericArg::LifetimeArg(arg) => arg.lifetime().is_some(),
|
||||
ast::GenericArg::ConstArg(_) | ast::GenericArg::TypeArg(_) => true,
|
||||
})
|
||||
.nth(arg_idx as usize - has_self_arg as usize)
|
||||
}
|
||||
|
@ -152,10 +152,8 @@ pub(super) fn lower_path(
|
||||
args: iter::once(self_type)
|
||||
.chain(it.args.iter().cloned())
|
||||
.collect(),
|
||||
|
||||
has_self_type: true,
|
||||
bindings: it.bindings.clone(),
|
||||
parenthesized: it.parenthesized,
|
||||
..it
|
||||
},
|
||||
None => GenericArgs {
|
||||
args: Box::new([self_type]),
|
||||
|
@ -138,6 +138,7 @@ impl GenericParamData {
|
||||
|
||||
impl_from!(TypeParamData, ConstParamData, LifetimeParamData for GenericParamData);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum GenericParamDataRef<'a> {
|
||||
TypeParamData(&'a TypeParamData),
|
||||
ConstParamData(&'a ConstParamData),
|
||||
|
@ -306,29 +306,28 @@ impl TyBuilder<hir_def::AdtId> {
|
||||
// Note that we're building ADT, so we never have parent generic parameters.
|
||||
let defaults = db.generic_defaults(self.data.into());
|
||||
|
||||
for default_ty in &defaults[self.vec.len()..] {
|
||||
// NOTE(skip_binders): we only check if the arg type is error type.
|
||||
if let Some(x) = default_ty.skip_binders().ty(Interner) {
|
||||
if x.is_unknown() {
|
||||
self.vec.push(fallback().cast(Interner));
|
||||
continue;
|
||||
if let Some(defaults) = defaults.get(self.vec.len()..) {
|
||||
for default_ty in defaults {
|
||||
// NOTE(skip_binders): we only check if the arg type is error type.
|
||||
if let Some(x) = default_ty.skip_binders().ty(Interner) {
|
||||
if x.is_unknown() {
|
||||
self.vec.push(fallback().cast(Interner));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Each default can only depend on the previous parameters.
|
||||
self.vec.push(default_ty.clone().substitute(Interner, &*self.vec).cast(Interner));
|
||||
}
|
||||
// Each default can only depend on the previous parameters.
|
||||
let subst_so_far = Substitution::from_iter(
|
||||
Interner,
|
||||
self.vec
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(self.param_kinds[self.vec.len()..].iter().map(|it| match it {
|
||||
ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
|
||||
}))
|
||||
.take(self.param_kinds.len()),
|
||||
);
|
||||
self.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner));
|
||||
}
|
||||
|
||||
// The defaults may be missing if no param has default, so fill that.
|
||||
let filler = self.param_kinds[self.vec.len()..].iter().map(|x| match x {
|
||||
ParamKind::Type => fallback().cast(Interner),
|
||||
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
});
|
||||
self.vec.extend(filler.casted(Interner));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -200,6 +200,9 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
|
||||
def: GenericDefId,
|
||||
) -> (GenericDefaults, Diagnostics);
|
||||
|
||||
/// This returns an empty list if no parameter has default.
|
||||
///
|
||||
/// The binders of the returned defaults are only up to (not including) this parameter.
|
||||
#[salsa::invoke(crate::lower::generic_defaults_query)]
|
||||
#[salsa::transparent]
|
||||
fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults;
|
||||
|
@ -1640,7 +1640,7 @@ fn generic_args_sans_defaults<'ga>(
|
||||
Some(default_parameter) => {
|
||||
// !is_err(default_parameter.skip_binders())
|
||||
// &&
|
||||
arg != &default_parameter.clone().substitute(Interner, ¶meters)
|
||||
arg != &default_parameter.clone().substitute(Interner, ¶meters[..i])
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -21,7 +21,6 @@ use hir_def::{
|
||||
},
|
||||
};
|
||||
use itertools::chain;
|
||||
use stdx::TupleExt;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{Interner, Substitution, db::HirDatabase, lt_to_placeholder_idx, to_placeholder_idx};
|
||||
@ -76,10 +75,13 @@ impl Generics {
|
||||
self.iter_parent().map(|(id, _)| id)
|
||||
}
|
||||
|
||||
pub(crate) fn iter_self_type_or_consts_id(
|
||||
pub(crate) fn iter_self_type_or_consts(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = GenericParamId> + '_ {
|
||||
self.params.iter_type_or_consts().map(from_toc_id(self)).map(TupleExt::head)
|
||||
) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> + '_
|
||||
{
|
||||
let mut toc = self.params.iter_type_or_consts();
|
||||
let trait_self_param = self.has_trait_self_param.then(|| toc.next()).flatten();
|
||||
chain!(trait_self_param, toc)
|
||||
}
|
||||
|
||||
/// Iterate over the parent params followed by self params.
|
||||
@ -107,7 +109,7 @@ impl Generics {
|
||||
}
|
||||
|
||||
/// Iterator over types and const params of parent.
|
||||
fn iter_parent(
|
||||
pub(crate) fn iter_parent(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
|
||||
self.parent_generics().into_iter().flat_map(|it| {
|
||||
@ -129,6 +131,10 @@ impl Generics {
|
||||
self.params.len()
|
||||
}
|
||||
|
||||
pub(crate) fn len_lifetimes_self(&self) -> usize {
|
||||
self.params.len_lifetimes()
|
||||
}
|
||||
|
||||
/// (parent total, self param, type params, const params, impl trait list, lifetimes)
|
||||
pub(crate) fn provenance_split(&self) -> (usize, bool, usize, usize, usize, usize) {
|
||||
let mut self_param = false;
|
||||
@ -144,7 +150,7 @@ impl Generics {
|
||||
TypeOrConstParamData::ConstParamData(_) => const_params += 1,
|
||||
});
|
||||
|
||||
let lifetime_params = self.params.iter_lt().count();
|
||||
let lifetime_params = self.params.len_lifetimes();
|
||||
|
||||
let parent_len = self.parent_generics().map_or(0, Generics::len);
|
||||
(parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params)
|
||||
|
@ -34,8 +34,8 @@ use chalk_ir::{
|
||||
};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, GenericDefId, ImplId, ItemContainerId,
|
||||
Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
|
||||
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId,
|
||||
ItemContainerId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
|
||||
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||
expr_store::{Body, ExpressionStore, HygieneId, path::Path},
|
||||
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
|
||||
@ -55,8 +55,9 @@ use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
|
||||
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode,
|
||||
PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
ImplTraitIdx, InEnvironment, IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId,
|
||||
ParamLoweringMode, PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty,
|
||||
TyBuilder, TyExt,
|
||||
db::HirDatabase,
|
||||
fold_tys,
|
||||
generics::Generics,
|
||||
@ -66,7 +67,7 @@ use crate::{
|
||||
expr::ExprIsRead,
|
||||
unify::InferenceTable,
|
||||
},
|
||||
lower::{ImplTraitLoweringMode, diagnostics::TyLoweringDiagnostic},
|
||||
lower::{GenericArgsPosition, ImplTraitLoweringMode, diagnostics::TyLoweringDiagnostic},
|
||||
mir::MirSpan,
|
||||
to_assoc_type_id,
|
||||
traits::FnTrait,
|
||||
@ -275,6 +276,20 @@ pub enum InferenceDiagnostic {
|
||||
node: ExprOrPatId,
|
||||
diag: PathLoweringDiagnostic,
|
||||
},
|
||||
MethodCallIncorrectGenericsLen {
|
||||
expr: ExprId,
|
||||
provided_count: u32,
|
||||
expected_count: u32,
|
||||
kind: IncorrectGenericsLenKind,
|
||||
def: GenericDefId,
|
||||
},
|
||||
MethodCallIncorrectGenericsOrder {
|
||||
expr: ExprId,
|
||||
param_id: GenericParamId,
|
||||
arg_idx: u32,
|
||||
/// Whether the `GenericArgs` contains a `Self` arg.
|
||||
has_self_arg: bool,
|
||||
},
|
||||
}
|
||||
|
||||
/// A mismatch between an expected and an inferred type.
|
||||
@ -909,6 +924,7 @@ impl<'a> InferenceContext<'a> {
|
||||
let mut param_tys =
|
||||
self.with_ty_lowering(&data.store, InferenceTyDiagnosticSource::Signature, |ctx| {
|
||||
ctx.type_param_mode(ParamLoweringMode::Placeholder);
|
||||
ctx.in_fn_signature = true;
|
||||
data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
@ -953,8 +969,9 @@ impl<'a> InferenceContext<'a> {
|
||||
InferenceTyDiagnosticSource::Signature,
|
||||
|ctx| {
|
||||
ctx.type_param_mode(ParamLoweringMode::Placeholder)
|
||||
.impl_trait_mode(ImplTraitLoweringMode::Opaque)
|
||||
.lower_ty(return_ty)
|
||||
.impl_trait_mode(ImplTraitLoweringMode::Opaque);
|
||||
ctx.in_fn_signature = true;
|
||||
ctx.lower_ty(return_ty)
|
||||
},
|
||||
);
|
||||
let return_ty = self.insert_type_vars(return_ty);
|
||||
@ -1513,7 +1530,7 @@ impl<'a> InferenceContext<'a> {
|
||||
InferenceTyDiagnosticSource::Body,
|
||||
self.generic_def,
|
||||
);
|
||||
let mut path_ctx = ctx.at_path(path, node);
|
||||
let mut path_ctx = ctx.at_path(path, node, GenericArgsPosition::Value);
|
||||
let (resolution, unresolved) = if value_ns {
|
||||
let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else {
|
||||
return (self.err_ty(), None);
|
||||
|
@ -12,6 +12,7 @@ use hir_def::expr_store::path::Path;
|
||||
use hir_def::{hir::ExprOrPatId, resolver::Resolver};
|
||||
use la_arena::{Idx, RawIdx};
|
||||
|
||||
use crate::lower::GenericArgsPosition;
|
||||
use crate::{
|
||||
InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringContext, TyLoweringDiagnostic,
|
||||
db::HirDatabase,
|
||||
@ -74,6 +75,7 @@ impl<'a> InferenceTyLoweringContext<'a> {
|
||||
&'b mut self,
|
||||
path: &'b Path,
|
||||
node: ExprOrPatId,
|
||||
position: GenericArgsPosition,
|
||||
) -> PathLoweringContext<'b, 'a> {
|
||||
let on_diagnostic = PathDiagnosticCallback {
|
||||
data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }),
|
||||
@ -83,13 +85,14 @@ impl<'a> InferenceTyLoweringContext<'a> {
|
||||
.push(InferenceDiagnostic::PathDiagnostic { node: data.node, diag });
|
||||
},
|
||||
};
|
||||
PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
|
||||
PathLoweringContext::new(&mut self.ctx, on_diagnostic, path, position)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn at_path_forget_diagnostics<'b>(
|
||||
&'b mut self,
|
||||
path: &'b Path,
|
||||
position: GenericArgsPosition,
|
||||
) -> PathLoweringContext<'b, 'a> {
|
||||
let on_diagnostic = PathDiagnosticCallback {
|
||||
data: Either::Right(PathDiagnosticCallbackData {
|
||||
@ -98,7 +101,7 @@ impl<'a> InferenceTyLoweringContext<'a> {
|
||||
}),
|
||||
callback: |_data, _, _diag| {},
|
||||
};
|
||||
PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
|
||||
PathLoweringContext::new(&mut self.ctx, on_diagnostic, path, position)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -12,7 +12,7 @@ use hir_def::{
|
||||
expr_store::path::{GenericArg, GenericArgs, Path},
|
||||
hir::{
|
||||
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, Expr, ExprId, ExprOrPatId, LabelId,
|
||||
Literal, Pat, PatId, Statement, UnaryOp,
|
||||
Literal, Pat, PatId, Statement, UnaryOp, generics::GenericParamDataRef,
|
||||
},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
resolver::ValueNs,
|
||||
@ -24,11 +24,11 @@ use syntax::ast::RangeOp;
|
||||
|
||||
use crate::{
|
||||
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext,
|
||||
DeclOrigin, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder,
|
||||
TyExt, TyKind,
|
||||
DeclOrigin, IncorrectGenericsLenKind, Interner, Rawness, Scalar, Substitution,
|
||||
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||
autoderef::{Autoderef, builtin_deref, deref_by_trait},
|
||||
consteval, error_lifetime,
|
||||
generics::{Generics, generics},
|
||||
consteval,
|
||||
generics::generics,
|
||||
infer::{
|
||||
BreakableKind,
|
||||
coerce::{CoerceMany, CoerceNever, CoercionCause},
|
||||
@ -36,7 +36,10 @@ use crate::{
|
||||
pat::contains_explicit_ref_binding,
|
||||
},
|
||||
lang_items::lang_items_for_bin_op,
|
||||
lower::{ParamLoweringMode, generic_arg_to_chalk, lower_to_chalk_mutability},
|
||||
lower::{
|
||||
GenericArgsPosition, ParamLoweringMode, lower_to_chalk_mutability,
|
||||
path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings},
|
||||
},
|
||||
mapping::{ToChalk, from_chalk},
|
||||
method_resolution::{self, VisibleFromModule},
|
||||
primitive::{self, UintTy},
|
||||
@ -1658,8 +1661,7 @@ impl InferenceContext<'_> {
|
||||
match resolved {
|
||||
Some((adjust, func, _)) => {
|
||||
let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
|
||||
let generics = generics(self.db, func.into());
|
||||
let substs = self.substs_for_method_call(generics, None);
|
||||
let substs = self.substs_for_method_call(tgt_expr, func.into(), None);
|
||||
self.write_expr_adj(receiver, adjustments);
|
||||
self.write_method_resolution(tgt_expr, func, substs.clone());
|
||||
|
||||
@ -1809,8 +1811,7 @@ impl InferenceContext<'_> {
|
||||
let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
|
||||
self.write_expr_adj(receiver, adjustments);
|
||||
|
||||
let generics = generics(self.db, func.into());
|
||||
let substs = self.substs_for_method_call(generics, generic_args);
|
||||
let substs = self.substs_for_method_call(tgt_expr, func.into(), generic_args);
|
||||
self.write_method_resolution(tgt_expr, func, substs.clone());
|
||||
self.check_method_call(
|
||||
tgt_expr,
|
||||
@ -1860,8 +1861,7 @@ impl InferenceContext<'_> {
|
||||
|
||||
let recovered = match assoc_func_with_same_name {
|
||||
Some(f) => {
|
||||
let generics = generics(self.db, f.into());
|
||||
let substs = self.substs_for_method_call(generics, generic_args);
|
||||
let substs = self.substs_for_method_call(tgt_expr, f.into(), generic_args);
|
||||
let f = self
|
||||
.db
|
||||
.value_ty(f.into())
|
||||
@ -2051,83 +2051,129 @@ impl InferenceContext<'_> {
|
||||
|
||||
fn substs_for_method_call(
|
||||
&mut self,
|
||||
def_generics: Generics,
|
||||
expr: ExprId,
|
||||
def: GenericDefId,
|
||||
generic_args: Option<&GenericArgs>,
|
||||
) -> Substitution {
|
||||
let (
|
||||
parent_params,
|
||||
has_self_param,
|
||||
type_params,
|
||||
const_params,
|
||||
impl_trait_params,
|
||||
lifetime_params,
|
||||
) = def_generics.provenance_split();
|
||||
assert!(!has_self_param); // method shouldn't have another Self param
|
||||
let total_len =
|
||||
parent_params + type_params + const_params + impl_trait_params + lifetime_params;
|
||||
|
||||
let param_to_var = |id| match id {
|
||||
GenericParamId::TypeParamId(_) => self.table.new_type_var().cast(Interner),
|
||||
GenericParamId::ConstParamId(id) => {
|
||||
self.table.new_const_var(self.db.const_param_ty(id)).cast(Interner)
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => self.table.new_lifetime_var().cast(Interner),
|
||||
};
|
||||
|
||||
let mut substs: Vec<_> = def_generics.iter_parent_id().map(param_to_var).collect();
|
||||
|
||||
// handle provided arguments
|
||||
if let Some(generic_args) = generic_args {
|
||||
// if args are provided, it should be all of them, but we can't rely on that
|
||||
let self_params = type_params + const_params + lifetime_params;
|
||||
|
||||
let mut args = generic_args.args.iter().peekable();
|
||||
for kind_id in def_generics.iter_self_id().take(self_params) {
|
||||
let arg = args.peek();
|
||||
let arg = match (kind_id, arg) {
|
||||
// Lifetimes can be inferred.
|
||||
// Once we have implemented lifetime inference correctly,
|
||||
// this should be handled in a proper way.
|
||||
(
|
||||
GenericParamId::LifetimeParamId(_),
|
||||
None | Some(GenericArg::Type(_) | GenericArg::Const(_)),
|
||||
) => error_lifetime().cast(Interner),
|
||||
|
||||
// If we run out of `generic_args`, stop pushing substs
|
||||
(_, None) => break,
|
||||
|
||||
// Normal cases
|
||||
(_, Some(_)) => generic_arg_to_chalk(
|
||||
self.db,
|
||||
kind_id,
|
||||
args.next().unwrap(), // `peek()` is `Some(_)`, so guaranteed no panic
|
||||
self,
|
||||
&self.body.store,
|
||||
|this, type_ref| this.make_body_ty(type_ref),
|
||||
|this, c, ty| this.make_body_const(*c, ty),
|
||||
|this, path, ty| this.make_path_as_body_const(path, ty),
|
||||
|this, lt_ref| this.make_body_lifetime(lt_ref),
|
||||
),
|
||||
};
|
||||
|
||||
substs.push(arg);
|
||||
}
|
||||
};
|
||||
|
||||
let mut param_to_var = |id| match id {
|
||||
GenericParamId::TypeParamId(_) => self.table.new_type_var().cast(Interner),
|
||||
GenericParamId::ConstParamId(id) => {
|
||||
self.table.new_const_var(self.db.const_param_ty(id)).cast(Interner)
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => self.table.new_lifetime_var().cast(Interner),
|
||||
};
|
||||
|
||||
// Handle everything else as unknown.
|
||||
for (id, _data) in def_generics.iter().skip(substs.len()) {
|
||||
substs.push(param_to_var(id));
|
||||
struct LowererCtx<'a, 'b> {
|
||||
ctx: &'a mut InferenceContext<'b>,
|
||||
expr: ExprId,
|
||||
}
|
||||
assert_eq!(substs.len(), total_len);
|
||||
Substitution::from_iter(Interner, substs)
|
||||
|
||||
impl GenericArgsLowerer for LowererCtx<'_, '_> {
|
||||
fn report_len_mismatch(
|
||||
&mut self,
|
||||
def: GenericDefId,
|
||||
provided_count: u32,
|
||||
expected_count: u32,
|
||||
kind: IncorrectGenericsLenKind,
|
||||
) {
|
||||
self.ctx.push_diagnostic(InferenceDiagnostic::MethodCallIncorrectGenericsLen {
|
||||
expr: self.expr,
|
||||
provided_count,
|
||||
expected_count,
|
||||
kind,
|
||||
def,
|
||||
});
|
||||
}
|
||||
|
||||
fn report_arg_mismatch(
|
||||
&mut self,
|
||||
param_id: GenericParamId,
|
||||
arg_idx: u32,
|
||||
has_self_arg: bool,
|
||||
) {
|
||||
self.ctx.push_diagnostic(InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
|
||||
expr: self.expr,
|
||||
param_id,
|
||||
arg_idx,
|
||||
has_self_arg,
|
||||
});
|
||||
}
|
||||
|
||||
fn provided_kind(
|
||||
&mut self,
|
||||
param_id: GenericParamId,
|
||||
param: GenericParamDataRef<'_>,
|
||||
arg: &GenericArg,
|
||||
) -> crate::GenericArg {
|
||||
match (param, arg) {
|
||||
(GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => {
|
||||
self.ctx.make_body_lifetime(lifetime).cast(Interner)
|
||||
}
|
||||
(GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => {
|
||||
self.ctx.make_body_ty(*type_ref).cast(Interner)
|
||||
}
|
||||
(GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => {
|
||||
let GenericParamId::ConstParamId(const_id) = param_id else {
|
||||
unreachable!("non-const param ID for const param");
|
||||
};
|
||||
let const_ty = self.ctx.db.const_param_ty(const_id);
|
||||
self.ctx.make_body_const(*konst, const_ty).cast(Interner)
|
||||
}
|
||||
_ => unreachable!("unmatching param kinds were passed to `provided_kind()`"),
|
||||
}
|
||||
}
|
||||
|
||||
fn provided_type_like_const(
|
||||
&mut self,
|
||||
const_ty: Ty,
|
||||
arg: TypeLikeConst<'_>,
|
||||
) -> crate::Const {
|
||||
match arg {
|
||||
TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path, const_ty),
|
||||
TypeLikeConst::Infer => self.ctx.table.new_const_var(const_ty),
|
||||
}
|
||||
}
|
||||
|
||||
fn inferred_kind(
|
||||
&mut self,
|
||||
_def: GenericDefId,
|
||||
param_id: GenericParamId,
|
||||
_param: GenericParamDataRef<'_>,
|
||||
_infer_args: bool,
|
||||
_preceding_args: &[crate::GenericArg],
|
||||
) -> crate::GenericArg {
|
||||
// Always create an inference var, even when `infer_args == false`. This helps with diagnostics,
|
||||
// and I think it's also required in the presence of `impl Trait` (that must be inferred).
|
||||
match param_id {
|
||||
GenericParamId::TypeParamId(_) => self.ctx.table.new_type_var().cast(Interner),
|
||||
GenericParamId::ConstParamId(const_id) => self
|
||||
.ctx
|
||||
.table
|
||||
.new_const_var(self.ctx.db.const_param_ty(const_id))
|
||||
.cast(Interner),
|
||||
GenericParamId::LifetimeParamId(_) => {
|
||||
self.ctx.table.new_lifetime_var().cast(Interner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg {
|
||||
match param_id {
|
||||
GenericParamId::TypeParamId(_) => self.ctx.table.new_type_var().cast(Interner),
|
||||
GenericParamId::ConstParamId(const_id) => self
|
||||
.ctx
|
||||
.table
|
||||
.new_const_var(self.ctx.db.const_param_ty(const_id))
|
||||
.cast(Interner),
|
||||
GenericParamId::LifetimeParamId(_) => {
|
||||
self.ctx.table.new_lifetime_var().cast(Interner)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
substs_from_args_and_bindings(
|
||||
self.db,
|
||||
self.body,
|
||||
generic_args,
|
||||
def,
|
||||
true,
|
||||
GenericArgsPosition::MethodCall,
|
||||
None,
|
||||
&mut LowererCtx { ctx: self, expr },
|
||||
)
|
||||
}
|
||||
|
||||
fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
|
||||
|
@ -16,6 +16,7 @@ use crate::{
|
||||
consteval, error_lifetime,
|
||||
generics::generics,
|
||||
infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext,
|
||||
lower::GenericArgsPosition,
|
||||
method_resolution::{self, VisibleFromModule},
|
||||
to_chalk_trait_id,
|
||||
};
|
||||
@ -95,7 +96,7 @@ impl InferenceContext<'_> {
|
||||
};
|
||||
|
||||
let substs = self.with_body_ty_lowering(|ctx| {
|
||||
let mut path_ctx = ctx.at_path(path, id);
|
||||
let mut path_ctx = ctx.at_path(path, id, GenericArgsPosition::Value);
|
||||
let last_segment = path.segments().len().checked_sub(1);
|
||||
if let Some(last_segment) = last_segment {
|
||||
path_ctx.set_current_segment(last_segment)
|
||||
@ -163,9 +164,9 @@ impl InferenceContext<'_> {
|
||||
self.generic_def,
|
||||
);
|
||||
let mut path_ctx = if no_diagnostics {
|
||||
ctx.at_path_forget_diagnostics(path)
|
||||
ctx.at_path_forget_diagnostics(path, GenericArgsPosition::Value)
|
||||
} else {
|
||||
ctx.at_path(path, id)
|
||||
ctx.at_path(path, id, GenericArgsPosition::Value)
|
||||
};
|
||||
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
|
||||
let last = path.segments().last()?;
|
||||
|
@ -347,20 +347,24 @@ pub(crate) fn make_binders<T: HasInterner<Interner = Interner>>(
|
||||
generics: &Generics,
|
||||
value: T,
|
||||
) -> Binders<T> {
|
||||
Binders::new(
|
||||
VariableKinds::from_iter(
|
||||
Interner,
|
||||
generics.iter_id().map(|x| match x {
|
||||
hir_def::GenericParamId::ConstParamId(id) => {
|
||||
chalk_ir::VariableKind::Const(db.const_param_ty(id))
|
||||
}
|
||||
hir_def::GenericParamId::TypeParamId(_) => {
|
||||
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
|
||||
}
|
||||
hir_def::GenericParamId::LifetimeParamId(_) => chalk_ir::VariableKind::Lifetime,
|
||||
}),
|
||||
),
|
||||
value,
|
||||
Binders::new(variable_kinds_from_iter(db, generics.iter_id()), value)
|
||||
}
|
||||
|
||||
pub(crate) fn variable_kinds_from_iter(
|
||||
db: &dyn HirDatabase,
|
||||
iter: impl Iterator<Item = hir_def::GenericParamId>,
|
||||
) -> VariableKinds {
|
||||
VariableKinds::from_iter(
|
||||
Interner,
|
||||
iter.map(|x| match x {
|
||||
hir_def::GenericParamId::ConstParamId(id) => {
|
||||
chalk_ir::VariableKind::Const(db.const_param_ty(id))
|
||||
}
|
||||
hir_def::GenericParamId::TypeParamId(_) => {
|
||||
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
|
||||
}
|
||||
hir_def::GenericParamId::LifetimeParamId(_) => chalk_ir::VariableKind::Lifetime,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -28,10 +28,7 @@ use hir_def::{
|
||||
FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LocalFieldId, Lookup, StaticId,
|
||||
StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId,
|
||||
builtin_type::BuiltinType,
|
||||
expr_store::{
|
||||
ExpressionStore,
|
||||
path::{GenericArg, Path},
|
||||
},
|
||||
expr_store::{ExpressionStore, path::Path},
|
||||
hir::generics::{
|
||||
GenericParamDataRef, TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget,
|
||||
},
|
||||
@ -55,9 +52,9 @@ use triomphe::{Arc, ThinArc};
|
||||
use crate::{
|
||||
AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DynTy, FnAbi, FnPointer, FnSig,
|
||||
FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, LifetimeData,
|
||||
LifetimeOutlives, ParamKind, PolyFnSig, ProgramClause, QuantifiedWhereClause,
|
||||
QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
|
||||
TyKind, WhereClause, all_super_traits,
|
||||
LifetimeOutlives, PolyFnSig, ProgramClause, QuantifiedWhereClause, QuantifiedWhereClauses,
|
||||
Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
|
||||
all_super_traits,
|
||||
consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic},
|
||||
db::HirDatabase,
|
||||
error_lifetime,
|
||||
@ -70,6 +67,7 @@ use crate::{
|
||||
mapping::{ToChalk, from_chalk_trait_id, lt_to_placeholder_idx},
|
||||
static_lifetime, to_chalk_trait_id, to_placeholder_idx,
|
||||
utils::all_super_trait_refs,
|
||||
variable_kinds_from_iter,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -89,6 +87,22 @@ impl ImplTraitLoweringState {
|
||||
|
||||
pub(crate) struct PathDiagnosticCallbackData(TypeRefId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum GenericArgsPosition {
|
||||
Type,
|
||||
/// E.g. functions.
|
||||
Value,
|
||||
MethodCall,
|
||||
// FIXME: This is a temporary variant we need to work around the lack of lifetime elision.
|
||||
// The reason for its existence is that in `check_generic_args_len()`, without this, we will
|
||||
// not infer elide lifetimes.
|
||||
// They indeed should not be inferred - they should be elided - but we won't elide them either,
|
||||
// emitting an error instead. rustc elides them in late resolve, and the generics it passes
|
||||
// to lowering already include them. We probably can't do that, but we will still need to
|
||||
// account for them when we properly implement lifetime elision.
|
||||
FnSignature,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TyLoweringContext<'a> {
|
||||
pub db: &'a dyn HirDatabase,
|
||||
@ -106,6 +120,7 @@ pub struct TyLoweringContext<'a> {
|
||||
/// Tracks types with explicit `?Sized` bounds.
|
||||
pub(crate) unsized_types: FxHashSet<Ty>,
|
||||
pub(crate) diagnostics: Vec<TyLoweringDiagnostic>,
|
||||
pub(crate) in_fn_signature: bool,
|
||||
}
|
||||
|
||||
impl<'a> TyLoweringContext<'a> {
|
||||
@ -129,6 +144,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
type_param_mode,
|
||||
unsized_types: FxHashSet::default(),
|
||||
diagnostics: Vec::new(),
|
||||
in_fn_signature: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -415,6 +431,11 @@ impl<'a> TyLoweringContext<'a> {
|
||||
self,
|
||||
Self::on_path_diagnostic_callback(path_id.type_ref()),
|
||||
&self.store[path_id],
|
||||
if self.in_fn_signature {
|
||||
GenericArgsPosition::FnSignature
|
||||
} else {
|
||||
GenericArgsPosition::Type
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ -1172,22 +1193,30 @@ pub(crate) fn generic_defaults_with_diagnostics_query(
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Disallowed)
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
let mut idx = 0;
|
||||
let mut has_any_default = false;
|
||||
let mut defaults = generic_params
|
||||
.iter_self()
|
||||
.map(|(id, p)| {
|
||||
let result = handle_generic_param(&mut ctx, idx, id, p, &generic_params);
|
||||
.iter_parents_with_store()
|
||||
.map(|((id, p), store)| {
|
||||
ctx.store = store;
|
||||
let (result, has_default) = handle_generic_param(&mut ctx, idx, id, p, &generic_params);
|
||||
has_any_default |= has_default;
|
||||
idx += 1;
|
||||
result
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics));
|
||||
defaults.extend(generic_params.iter_parents_with_store().map(|((id, p), store)| {
|
||||
ctx.store = store;
|
||||
let result = handle_generic_param(&mut ctx, idx, id, p, &generic_params);
|
||||
ctx.diagnostics.clear(); // Don't include diagnostics from the parent.
|
||||
defaults.extend(generic_params.iter_self().map(|(id, p)| {
|
||||
let (result, has_default) = handle_generic_param(&mut ctx, idx, id, p, &generic_params);
|
||||
has_any_default |= has_default;
|
||||
idx += 1;
|
||||
result
|
||||
}));
|
||||
let defaults = GenericDefaults(Some(Arc::from_iter(defaults)));
|
||||
let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics));
|
||||
let defaults = if has_any_default {
|
||||
GenericDefaults(Some(Arc::from_iter(defaults)))
|
||||
} else {
|
||||
GenericDefaults(None)
|
||||
};
|
||||
return (defaults, diagnostics);
|
||||
|
||||
fn handle_generic_param(
|
||||
@ -1196,16 +1225,20 @@ pub(crate) fn generic_defaults_with_diagnostics_query(
|
||||
id: GenericParamId,
|
||||
p: GenericParamDataRef<'_>,
|
||||
generic_params: &Generics,
|
||||
) -> Binders<crate::GenericArg> {
|
||||
) -> (Binders<crate::GenericArg>, bool) {
|
||||
let binders = variable_kinds_from_iter(ctx.db, generic_params.iter_id().take(idx));
|
||||
match p {
|
||||
GenericParamDataRef::TypeParamData(p) => {
|
||||
let ty = p.default.as_ref().map_or(TyKind::Error.intern(Interner), |ty| {
|
||||
// Each default can only refer to previous parameters.
|
||||
// Type variable default referring to parameter coming
|
||||
// after it is forbidden (FIXME: report diagnostic)
|
||||
fallback_bound_vars(ctx.lower_ty(*ty), idx, 0)
|
||||
});
|
||||
crate::make_binders(ctx.db, generic_params, ty.cast(Interner))
|
||||
let ty = p.default.as_ref().map_or_else(
|
||||
|| TyKind::Error.intern(Interner),
|
||||
|ty| {
|
||||
// Each default can only refer to previous parameters.
|
||||
// Type variable default referring to parameter coming
|
||||
// after it is forbidden (FIXME: report diagnostic)
|
||||
fallback_bound_vars(ctx.lower_ty(*ty), idx)
|
||||
},
|
||||
);
|
||||
(Binders::new(binders, ty.cast(Interner)), p.default.is_some())
|
||||
}
|
||||
GenericParamDataRef::ConstParamData(p) => {
|
||||
let GenericParamId::ConstParamId(id) = id else {
|
||||
@ -1221,36 +1254,22 @@ pub(crate) fn generic_defaults_with_diagnostics_query(
|
||||
},
|
||||
);
|
||||
// Each default can only refer to previous parameters, see above.
|
||||
val = fallback_bound_vars(val, idx, 0);
|
||||
make_binders(ctx.db, generic_params, val)
|
||||
val = fallback_bound_vars(val, idx);
|
||||
(Binders::new(binders, val), p.default.is_some())
|
||||
}
|
||||
GenericParamDataRef::LifetimeParamData(_) => {
|
||||
make_binders(ctx.db, generic_params, error_lifetime().cast(Interner))
|
||||
(Binders::new(binders, error_lifetime().cast(Interner)), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn generic_defaults_with_diagnostics_recover(
|
||||
db: &dyn HirDatabase,
|
||||
_db: &dyn HirDatabase,
|
||||
_cycle: &Cycle,
|
||||
def: GenericDefId,
|
||||
_def: GenericDefId,
|
||||
) -> (GenericDefaults, Diagnostics) {
|
||||
let generic_params = generics(db, def);
|
||||
if generic_params.len() == 0 {
|
||||
return (GenericDefaults(None), None);
|
||||
}
|
||||
// FIXME: this code is not covered in tests.
|
||||
// we still need one default per parameter
|
||||
let defaults = GenericDefaults(Some(Arc::from_iter(generic_params.iter_id().map(|id| {
|
||||
let val = match id {
|
||||
GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)),
|
||||
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
|
||||
};
|
||||
crate::make_binders(db, &generic_params, val)
|
||||
}))));
|
||||
(defaults, None)
|
||||
(GenericDefaults(None), None)
|
||||
}
|
||||
|
||||
fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
|
||||
@ -1258,6 +1277,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
|
||||
let resolver = def.resolver(db);
|
||||
let mut ctx_params = TyLoweringContext::new(db, &resolver, &data.store, def.into())
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
ctx_params.in_fn_signature = true;
|
||||
let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr));
|
||||
|
||||
let ret = match data.ret_type {
|
||||
@ -1265,6 +1285,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
|
||||
let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.store, def.into())
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
ctx_ret.in_fn_signature = true;
|
||||
ctx_ret.lower_ty(ret_type)
|
||||
}
|
||||
None => TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner),
|
||||
@ -1612,73 +1633,14 @@ pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mut
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the provided generic arg matches its expected kind, then lower them via
|
||||
/// provided closures. Use unknown if there was kind mismatch.
|
||||
///
|
||||
pub(crate) fn generic_arg_to_chalk<'a, T>(
|
||||
db: &dyn HirDatabase,
|
||||
kind_id: GenericParamId,
|
||||
arg: &'a GenericArg,
|
||||
this: &mut T,
|
||||
store: &ExpressionStore,
|
||||
for_type: impl FnOnce(&mut T, TypeRefId) -> Ty + 'a,
|
||||
for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a,
|
||||
for_const_ty_path_fallback: impl FnOnce(&mut T, &Path, Ty) -> Const + 'a,
|
||||
for_lifetime: impl FnOnce(&mut T, &LifetimeRef) -> Lifetime + 'a,
|
||||
) -> crate::GenericArg {
|
||||
let kind = match kind_id {
|
||||
GenericParamId::TypeParamId(_) => ParamKind::Type,
|
||||
GenericParamId::ConstParamId(id) => {
|
||||
let ty = db.const_param_ty(id);
|
||||
ParamKind::Const(ty)
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime,
|
||||
};
|
||||
match (arg, kind) {
|
||||
(GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, *type_ref).cast(Interner),
|
||||
(GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner),
|
||||
(GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => {
|
||||
for_lifetime(this, lifetime_ref).cast(Interner)
|
||||
}
|
||||
(GenericArg::Const(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
(GenericArg::Lifetime(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
(GenericArg::Type(t), ParamKind::Const(c_ty)) => match &store[*t] {
|
||||
TypeRef::Path(p) => for_const_ty_path_fallback(this, p, c_ty).cast(Interner),
|
||||
_ => unknown_const_as_generic(c_ty),
|
||||
},
|
||||
(GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => unknown_const_as_generic(c_ty),
|
||||
(GenericArg::Type(_), ParamKind::Lifetime) => error_lifetime().cast(Interner),
|
||||
(GenericArg::Const(_), ParamKind::Lifetime) => error_lifetime().cast(Interner),
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces any 'free' `BoundVar`s in `s` by `TyKind::Error` from the perspective of generic
|
||||
/// parameter whose index is `param_index`. A `BoundVar` is free when it is or (syntactically)
|
||||
/// appears after the generic parameter of `param_index`.
|
||||
/// parameter whose index is `param_index`. A `BoundVar` is free when it appears after the
|
||||
/// generic parameter of `param_index`.
|
||||
fn fallback_bound_vars<T: TypeFoldable<Interner> + HasInterner<Interner = Interner>>(
|
||||
s: T,
|
||||
param_index: usize,
|
||||
parent_start: usize,
|
||||
) -> T {
|
||||
// Keep in mind that parent generic parameters, if any, come *after* those of the item in
|
||||
// question. In the diagrams below, `c*` and `p*` represent generic parameters of the item and
|
||||
// its parent respectively.
|
||||
let is_allowed = |index| {
|
||||
if param_index < parent_start {
|
||||
// The parameter of `param_index` is one from the item in question. Any parent generic
|
||||
// parameters or the item's generic parameters that come before `param_index` is
|
||||
// allowed.
|
||||
// [c1, .., cj, .., ck, p1, .., pl] where cj is `param_index`
|
||||
// ^^^^^^ ^^^^^^^^^^ these are allowed
|
||||
!(param_index..parent_start).contains(&index)
|
||||
} else {
|
||||
// The parameter of `param_index` is one from the parent generics. Only parent generic
|
||||
// parameters that come before `param_index` are allowed.
|
||||
// [c1, .., ck, p1, .., pj, .., pl] where pj is `param_index`
|
||||
// ^^^^^^ these are allowed
|
||||
(parent_start..param_index).contains(&index)
|
||||
}
|
||||
};
|
||||
let is_allowed = |index| (0..param_index).contains(&index);
|
||||
|
||||
crate::fold_free_vars(
|
||||
s,
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! This files contains the declaration of diagnostics kinds for ty and path lowering.
|
||||
|
||||
use hir_def::type_ref::TypeRefId;
|
||||
use hir_def::{GenericDefId, GenericParamId};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct TyLoweringDiagnostic {
|
||||
@ -21,13 +22,51 @@ pub enum GenericArgsProhibitedReason {
|
||||
PrimitiveTy,
|
||||
Const,
|
||||
Static,
|
||||
LocalVariable,
|
||||
/// When there is a generic enum, within the expression `Enum::Variant`,
|
||||
/// either `Enum` or `Variant` are allowed to have generic arguments, but not both.
|
||||
EnumVariant,
|
||||
}
|
||||
|
||||
/// A path can have many generic arguments: each segment may have one associated with the
|
||||
/// segment, and in addition, each associated type binding may have generic arguments. This
|
||||
/// enum abstracts over both.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PathGenericsSource {
|
||||
/// Generic arguments directly on the segment.
|
||||
Segment(u32),
|
||||
/// Generic arguments on an associated type, e.g. `Foo<Assoc<A, B> = C>` or `Foo<Assoc<A, B>: Bound>`.
|
||||
AssocType { segment: u32, assoc_type: u32 },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum PathLoweringDiagnostic {
|
||||
GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason },
|
||||
ParenthesizedGenericArgsWithoutFnTrait { segment: u32 },
|
||||
GenericArgsProhibited {
|
||||
segment: u32,
|
||||
reason: GenericArgsProhibitedReason,
|
||||
},
|
||||
ParenthesizedGenericArgsWithoutFnTrait {
|
||||
segment: u32,
|
||||
},
|
||||
/// The expected lifetimes & types and consts counts can be found by inspecting the `GenericDefId`.
|
||||
IncorrectGenericsLen {
|
||||
generics_source: PathGenericsSource,
|
||||
provided_count: u32,
|
||||
expected_count: u32,
|
||||
kind: IncorrectGenericsLenKind,
|
||||
def: GenericDefId,
|
||||
},
|
||||
IncorrectGenericsOrder {
|
||||
generics_source: PathGenericsSource,
|
||||
param_id: GenericParamId,
|
||||
arg_idx: u32,
|
||||
/// Whether the `GenericArgs` contains a `Self` arg.
|
||||
has_self_arg: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum IncorrectGenericsLenKind {
|
||||
Lifetimes,
|
||||
TypesAndConsts,
|
||||
}
|
||||
|
@ -1,30 +1,33 @@
|
||||
//! A wrapper around [`TyLoweringContext`] specifically for lowering paths.
|
||||
|
||||
use std::iter;
|
||||
|
||||
use chalk_ir::{BoundVar, cast::Cast, fold::Shift};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
GenericDefId, GenericParamId, ItemContainerId, Lookup, TraitId,
|
||||
GenericDefId, GenericParamId, Lookup, TraitId,
|
||||
expr_store::{
|
||||
HygieneId,
|
||||
ExpressionStore, HygieneId,
|
||||
path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments},
|
||||
},
|
||||
hir::generics::{
|
||||
GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance,
|
||||
},
|
||||
resolver::{ResolveValueResult, TypeNs, ValueNs},
|
||||
signatures::TraitFlags,
|
||||
type_ref::TypeRef,
|
||||
type_ref::{TypeRef, TypeRefId},
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use stdx::never;
|
||||
|
||||
use crate::{
|
||||
AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, Interner,
|
||||
ParamLoweringMode, PathLoweringDiagnostic, ProjectionTy, QuantifiedWhereClause, Substitution,
|
||||
TraitRef, Ty, TyBuilder, TyDefId, TyKind, TyLoweringContext, ValueTyDefId, WhereClause,
|
||||
consteval::unknown_const_as_generic,
|
||||
AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, IncorrectGenericsLenKind,
|
||||
Interner, ParamLoweringMode, PathGenericsSource, PathLoweringDiagnostic, ProjectionTy,
|
||||
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyDefId, TyKind,
|
||||
TyLoweringContext, ValueTyDefId, WhereClause,
|
||||
consteval::{unknown_const, unknown_const_as_generic},
|
||||
db::HirDatabase,
|
||||
error_lifetime,
|
||||
generics::generics,
|
||||
lower::{generic_arg_to_chalk, named_associated_type_shorthand_candidates},
|
||||
generics::{Generics, generics},
|
||||
lower::{GenericArgsPosition, named_associated_type_shorthand_candidates},
|
||||
to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
|
||||
utils::associated_type_by_name_including_super_traits,
|
||||
};
|
||||
@ -49,6 +52,7 @@ pub(crate) struct PathLoweringContext<'a, 'b> {
|
||||
current_segment_idx: usize,
|
||||
/// Contains the previous segment if `current_segment_idx == segments.len()`
|
||||
current_or_prev_segment: PathSegment<'a>,
|
||||
position: GenericArgsPosition,
|
||||
}
|
||||
|
||||
impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
@ -57,6 +61,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
ctx: &'a mut TyLoweringContext<'b>,
|
||||
on_diagnostic: PathDiagnosticCallback<'a>,
|
||||
path: &'a Path,
|
||||
position: GenericArgsPosition,
|
||||
) -> Self {
|
||||
let segments = path.segments();
|
||||
let first_segment = segments.first().unwrap_or(PathSegment::MISSING);
|
||||
@ -67,6 +72,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
segments,
|
||||
current_segment_idx: 0,
|
||||
current_or_prev_segment: first_segment,
|
||||
position,
|
||||
}
|
||||
}
|
||||
|
||||
@ -449,14 +455,19 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
// 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(_) => {
|
||||
ValueNs::GenericParam(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const)
|
||||
}
|
||||
ValueNs::StaticId(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static)
|
||||
}
|
||||
ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {}
|
||||
ValueNs::LocalBinding(_) => {}
|
||||
ValueNs::LocalBinding(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::LocalVariable)
|
||||
}
|
||||
ValueNs::FunctionId(_)
|
||||
| ValueNs::StructId(_)
|
||||
| ValueNs::EnumVariantId(_)
|
||||
| ValueNs::ConstId(_) => {}
|
||||
}
|
||||
}
|
||||
ResolveValueResult::Partial(resolution, _, _) => {
|
||||
@ -615,6 +626,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
def,
|
||||
infer_args,
|
||||
explicit_self_ty,
|
||||
PathGenericsSource::Segment(self.current_segment_u32()),
|
||||
)
|
||||
}
|
||||
|
||||
@ -624,144 +636,143 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
def: GenericDefId,
|
||||
infer_args: bool,
|
||||
explicit_self_ty: Option<Ty>,
|
||||
generics_source: PathGenericsSource,
|
||||
) -> Substitution {
|
||||
// Order is
|
||||
// - Parent parameters
|
||||
// - Optional Self parameter
|
||||
// - Lifetime parameters
|
||||
// - Type or Const parameters
|
||||
let def_generics = generics(self.ctx.db, def);
|
||||
let (
|
||||
parent_params,
|
||||
self_param,
|
||||
type_params,
|
||||
const_params,
|
||||
impl_trait_params,
|
||||
lifetime_params,
|
||||
) = def_generics.provenance_split();
|
||||
let item_len =
|
||||
self_param as usize + type_params + const_params + impl_trait_params + lifetime_params;
|
||||
let total_len = parent_params + item_len;
|
||||
struct LowererCtx<'a, 'b, 'c> {
|
||||
ctx: &'a mut PathLoweringContext<'b, 'c>,
|
||||
generics_source: PathGenericsSource,
|
||||
}
|
||||
|
||||
let ty_error = || TyKind::Error.intern(Interner).cast(Interner);
|
||||
let param_to_err = |id| match id {
|
||||
GenericParamId::ConstParamId(x) => {
|
||||
unknown_const_as_generic(self.ctx.db.const_param_ty(x))
|
||||
impl GenericArgsLowerer for LowererCtx<'_, '_, '_> {
|
||||
fn report_len_mismatch(
|
||||
&mut self,
|
||||
def: GenericDefId,
|
||||
provided_count: u32,
|
||||
expected_count: u32,
|
||||
kind: IncorrectGenericsLenKind,
|
||||
) {
|
||||
self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsLen {
|
||||
generics_source: self.generics_source,
|
||||
provided_count,
|
||||
expected_count,
|
||||
kind,
|
||||
def,
|
||||
});
|
||||
}
|
||||
GenericParamId::TypeParamId(_) => ty_error(),
|
||||
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
|
||||
};
|
||||
|
||||
let mut substs: Vec<_> = def_generics.iter_parent_id().map(param_to_err).collect();
|
||||
fn report_arg_mismatch(
|
||||
&mut self,
|
||||
param_id: GenericParamId,
|
||||
arg_idx: u32,
|
||||
has_self_arg: bool,
|
||||
) {
|
||||
self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsOrder {
|
||||
generics_source: self.generics_source,
|
||||
param_id,
|
||||
arg_idx,
|
||||
has_self_arg,
|
||||
});
|
||||
}
|
||||
|
||||
tracing::debug!(?substs, ?parent_params);
|
||||
|
||||
// we need to iterate the lifetime and type/const params separately as our order of them
|
||||
// differs from the supplied syntax
|
||||
|
||||
let mut def_toc_iter = def_generics.iter_self_type_or_consts_id();
|
||||
let fill_self_param = || {
|
||||
if self_param {
|
||||
let self_ty = explicit_self_ty.map(|x| x.cast(Interner)).unwrap_or_else(ty_error);
|
||||
|
||||
if let Some(id) = def_toc_iter.next() {
|
||||
assert!(matches!(id, GenericParamId::TypeParamId(_)));
|
||||
substs.push(self_ty);
|
||||
fn provided_kind(
|
||||
&mut self,
|
||||
param_id: GenericParamId,
|
||||
param: GenericParamDataRef<'_>,
|
||||
arg: &GenericArg,
|
||||
) -> crate::GenericArg {
|
||||
match (param, arg) {
|
||||
(GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => {
|
||||
self.ctx.ctx.lower_lifetime(lifetime).cast(Interner)
|
||||
}
|
||||
(GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => {
|
||||
self.ctx.ctx.lower_ty(*type_ref).cast(Interner)
|
||||
}
|
||||
(GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => {
|
||||
let GenericParamId::ConstParamId(const_id) = param_id else {
|
||||
unreachable!("non-const param ID for const param");
|
||||
};
|
||||
self.ctx
|
||||
.ctx
|
||||
.lower_const(konst, self.ctx.ctx.db.const_param_ty(const_id))
|
||||
.cast(Interner)
|
||||
}
|
||||
_ => unreachable!("unmatching param kinds were passed to `provided_kind()`"),
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut had_explicit_args = false;
|
||||
|
||||
if let Some(&GenericArgs { ref args, has_self_type, .. }) = args_and_bindings {
|
||||
// Fill in the self param first
|
||||
if has_self_type && self_param {
|
||||
had_explicit_args = true;
|
||||
if let Some(id) = def_toc_iter.next() {
|
||||
assert!(matches!(id, GenericParamId::TypeParamId(_)));
|
||||
had_explicit_args = true;
|
||||
if let GenericArg::Type(ty) = &args[0] {
|
||||
substs.push(self.ctx.lower_ty(*ty).cast(Interner));
|
||||
fn provided_type_like_const(
|
||||
&mut self,
|
||||
const_ty: Ty,
|
||||
arg: TypeLikeConst<'_>,
|
||||
) -> crate::Const {
|
||||
match arg {
|
||||
TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty),
|
||||
TypeLikeConst::Infer => unknown_const(const_ty),
|
||||
}
|
||||
}
|
||||
|
||||
fn inferred_kind(
|
||||
&mut self,
|
||||
def: GenericDefId,
|
||||
param_id: GenericParamId,
|
||||
param: GenericParamDataRef<'_>,
|
||||
infer_args: bool,
|
||||
preceding_args: &[crate::GenericArg],
|
||||
) -> crate::GenericArg {
|
||||
let default = || {
|
||||
self.ctx
|
||||
.ctx
|
||||
.db
|
||||
.generic_defaults(def)
|
||||
.get(preceding_args.len())
|
||||
.map(|default| default.clone().substitute(Interner, preceding_args))
|
||||
};
|
||||
match param {
|
||||
GenericParamDataRef::LifetimeParamData(_) => error_lifetime().cast(Interner),
|
||||
GenericParamDataRef::TypeParamData(param) => {
|
||||
if !infer_args && param.default.is_some() {
|
||||
if let Some(default) = default() {
|
||||
return default;
|
||||
}
|
||||
}
|
||||
TyKind::Error.intern(Interner).cast(Interner)
|
||||
}
|
||||
GenericParamDataRef::ConstParamData(param) => {
|
||||
if !infer_args && param.default.is_some() {
|
||||
if let Some(default) = default() {
|
||||
return default;
|
||||
}
|
||||
}
|
||||
let GenericParamId::ConstParamId(const_id) = param_id else {
|
||||
unreachable!("non-const param ID for const param");
|
||||
};
|
||||
unknown_const_as_generic(self.ctx.ctx.db.const_param_ty(const_id))
|
||||
.cast(Interner)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fill_self_param()
|
||||
};
|
||||
|
||||
// Then fill in the supplied lifetime args, or error lifetimes if there are too few
|
||||
// (default lifetimes aren't a thing)
|
||||
for arg in args
|
||||
.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
GenericArg::Lifetime(arg) => Some(self.ctx.lower_lifetime(arg)),
|
||||
_ => None,
|
||||
})
|
||||
.chain(iter::repeat(error_lifetime()))
|
||||
.take(lifetime_params)
|
||||
{
|
||||
substs.push(arg.cast(Interner));
|
||||
}
|
||||
|
||||
let skip = if has_self_type { 1 } else { 0 };
|
||||
// Fill in supplied type and const args
|
||||
// Note if non-lifetime args are provided, it should be all of them, but we can't rely on that
|
||||
for (arg, id) in args
|
||||
.iter()
|
||||
.filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
|
||||
.skip(skip)
|
||||
.take(type_params + const_params)
|
||||
.zip(def_toc_iter)
|
||||
{
|
||||
had_explicit_args = true;
|
||||
let arg = generic_arg_to_chalk(
|
||||
self.ctx.db,
|
||||
id,
|
||||
arg,
|
||||
self.ctx,
|
||||
self.ctx.store,
|
||||
|ctx, type_ref| ctx.lower_ty(type_ref),
|
||||
|ctx, const_ref, ty| ctx.lower_const(const_ref, ty),
|
||||
|ctx, path, ty| ctx.lower_path_as_const(path, ty),
|
||||
|ctx, lifetime_ref| ctx.lower_lifetime(lifetime_ref),
|
||||
);
|
||||
substs.push(arg);
|
||||
fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg {
|
||||
match param_id {
|
||||
GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
GenericParamId::ConstParamId(const_id) => {
|
||||
unknown_const_as_generic(self.ctx.ctx.db.const_param_ty(const_id))
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fill_self_param();
|
||||
}
|
||||
|
||||
// handle defaults. In expression or pattern path segments without
|
||||
// explicitly specified type arguments, missing type arguments are inferred
|
||||
// (i.e. defaults aren't used).
|
||||
// Generic parameters for associated types are not supposed to have defaults, so we just
|
||||
// ignore them.
|
||||
let is_assoc_ty = || match def {
|
||||
GenericDefId::TypeAliasId(id) => {
|
||||
matches!(id.lookup(self.ctx.db).container, ItemContainerId::TraitId(_))
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
let fill_defaults = (!infer_args || had_explicit_args) && !is_assoc_ty();
|
||||
if fill_defaults {
|
||||
let defaults = &*self.ctx.db.generic_defaults(def);
|
||||
|
||||
let rem =
|
||||
def_generics.iter_id().skip(substs.len()).map(param_to_err).collect::<Vec<_>>();
|
||||
// Fill in defaults for type/const params
|
||||
for (idx, default_ty) in defaults[substs.len()..].iter().enumerate() {
|
||||
// each default can depend on the previous parameters
|
||||
let substs_so_far = Substitution::from_iter(
|
||||
Interner,
|
||||
substs.iter().cloned().chain(rem[idx..].iter().cloned()),
|
||||
);
|
||||
substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
|
||||
}
|
||||
} else {
|
||||
// Fill in remaining def params and parent params
|
||||
substs.extend(def_generics.iter_id().skip(substs.len()).map(param_to_err));
|
||||
}
|
||||
|
||||
assert_eq!(substs.len(), total_len, "expected {} substs, got {}", total_len, substs.len());
|
||||
Substitution::from_iter(Interner, substs)
|
||||
substs_from_args_and_bindings(
|
||||
self.ctx.db,
|
||||
self.ctx.store,
|
||||
args_and_bindings,
|
||||
def,
|
||||
infer_args,
|
||||
self.position,
|
||||
explicit_self_ty,
|
||||
&mut LowererCtx { ctx: self, generics_source },
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn lower_trait_ref_from_resolved_path(
|
||||
@ -786,7 +797,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
trait_ref: TraitRef,
|
||||
) -> Option<impl Iterator<Item = QuantifiedWhereClause> + use<'a, 'b, 'c>> {
|
||||
self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| {
|
||||
args_and_bindings.bindings.iter().flat_map(move |binding| {
|
||||
args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| {
|
||||
let found = associated_type_by_name_including_super_traits(
|
||||
self.ctx.db,
|
||||
trait_ref.clone(),
|
||||
@ -805,6 +816,10 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
associated_ty.into(),
|
||||
false, // this is not relevant
|
||||
Some(super_trait_ref.self_type_parameter(Interner)),
|
||||
PathGenericsSource::AssocType {
|
||||
segment: self.current_segment_u32(),
|
||||
assoc_type: binding_idx as u32,
|
||||
},
|
||||
);
|
||||
let substitution = Substitution::from_iter(
|
||||
Interner,
|
||||
@ -845,3 +860,288 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A const that were parsed like a type.
|
||||
pub(crate) enum TypeLikeConst<'a> {
|
||||
Infer,
|
||||
Path(&'a Path),
|
||||
}
|
||||
|
||||
pub(crate) trait GenericArgsLowerer {
|
||||
fn report_len_mismatch(
|
||||
&mut self,
|
||||
def: GenericDefId,
|
||||
provided_count: u32,
|
||||
expected_count: u32,
|
||||
kind: IncorrectGenericsLenKind,
|
||||
);
|
||||
|
||||
fn report_arg_mismatch(&mut self, param_id: GenericParamId, arg_idx: u32, has_self_arg: bool);
|
||||
|
||||
fn provided_kind(
|
||||
&mut self,
|
||||
param_id: GenericParamId,
|
||||
param: GenericParamDataRef<'_>,
|
||||
arg: &GenericArg,
|
||||
) -> crate::GenericArg;
|
||||
|
||||
fn provided_type_like_const(&mut self, const_ty: Ty, arg: TypeLikeConst<'_>) -> crate::Const;
|
||||
|
||||
fn inferred_kind(
|
||||
&mut self,
|
||||
def: GenericDefId,
|
||||
param_id: GenericParamId,
|
||||
param: GenericParamDataRef<'_>,
|
||||
infer_args: bool,
|
||||
preceding_args: &[crate::GenericArg],
|
||||
) -> crate::GenericArg;
|
||||
|
||||
fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg;
|
||||
}
|
||||
|
||||
/// Returns true if there was an error.
|
||||
fn check_generic_args_len(
|
||||
args_and_bindings: Option<&GenericArgs>,
|
||||
def: GenericDefId,
|
||||
def_generics: &Generics,
|
||||
infer_args: bool,
|
||||
position: GenericArgsPosition,
|
||||
ctx: &mut impl GenericArgsLowerer,
|
||||
) -> bool {
|
||||
let mut had_error = false;
|
||||
|
||||
let (mut provided_lifetimes_count, mut provided_types_and_consts_count) = (0usize, 0usize);
|
||||
if let Some(args_and_bindings) = args_and_bindings {
|
||||
let args_no_self = &args_and_bindings.args[usize::from(args_and_bindings.has_self_type)..];
|
||||
for arg in args_no_self {
|
||||
match arg {
|
||||
GenericArg::Lifetime(_) => provided_lifetimes_count += 1,
|
||||
GenericArg::Type(_) | GenericArg::Const(_) => provided_types_and_consts_count += 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let infer_lifetimes =
|
||||
(position != GenericArgsPosition::Type || infer_args) && provided_lifetimes_count == 0;
|
||||
|
||||
let min_expected_lifetime_args =
|
||||
if infer_lifetimes { 0 } else { def_generics.len_lifetimes_self() };
|
||||
let max_expected_lifetime_args = def_generics.len_lifetimes_self();
|
||||
if !(min_expected_lifetime_args..=max_expected_lifetime_args)
|
||||
.contains(&provided_lifetimes_count)
|
||||
{
|
||||
ctx.report_len_mismatch(
|
||||
def,
|
||||
provided_lifetimes_count as u32,
|
||||
def_generics.len_lifetimes_self() as u32,
|
||||
IncorrectGenericsLenKind::Lifetimes,
|
||||
);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
let defaults_count =
|
||||
def_generics.iter_self_type_or_consts().filter(|(_, param)| param.has_default()).count();
|
||||
let named_type_and_const_params_count = def_generics
|
||||
.iter_self_type_or_consts()
|
||||
.filter(|(_, param)| match param {
|
||||
TypeOrConstParamData::TypeParamData(param) => {
|
||||
param.provenance == TypeParamProvenance::TypeParamList
|
||||
}
|
||||
TypeOrConstParamData::ConstParamData(_) => true,
|
||||
})
|
||||
.count();
|
||||
let expected_min =
|
||||
if infer_args { 0 } else { named_type_and_const_params_count - defaults_count };
|
||||
let expected_max = named_type_and_const_params_count;
|
||||
if !(expected_min..=expected_max).contains(&provided_types_and_consts_count) {
|
||||
ctx.report_len_mismatch(
|
||||
def,
|
||||
provided_types_and_consts_count as u32,
|
||||
named_type_and_const_params_count as u32,
|
||||
IncorrectGenericsLenKind::TypesAndConsts,
|
||||
);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
had_error
|
||||
}
|
||||
|
||||
pub(crate) fn substs_from_args_and_bindings(
|
||||
db: &dyn HirDatabase,
|
||||
store: &ExpressionStore,
|
||||
args_and_bindings: Option<&GenericArgs>,
|
||||
def: GenericDefId,
|
||||
mut infer_args: bool,
|
||||
position: GenericArgsPosition,
|
||||
explicit_self_ty: Option<Ty>,
|
||||
ctx: &mut impl GenericArgsLowerer,
|
||||
) -> Substitution {
|
||||
// Order is
|
||||
// - Parent parameters
|
||||
// - Optional Self parameter
|
||||
// - Lifetime parameters
|
||||
// - Type or Const parameters
|
||||
let def_generics = generics(db, def);
|
||||
let args_slice = args_and_bindings.map(|it| &*it.args).unwrap_or_default();
|
||||
|
||||
// We do not allow inference if there are specified args, i.e. we do not allow partial inference.
|
||||
let has_non_lifetime_args =
|
||||
args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_)));
|
||||
infer_args &= !has_non_lifetime_args;
|
||||
|
||||
let had_count_error =
|
||||
check_generic_args_len(args_and_bindings, def, &def_generics, infer_args, position, ctx);
|
||||
|
||||
let mut substs = Vec::with_capacity(def_generics.len());
|
||||
|
||||
substs.extend(def_generics.iter_parent_id().map(|id| ctx.parent_arg(id)));
|
||||
|
||||
let mut args = args_slice.iter().enumerate().peekable();
|
||||
let mut params = def_generics.iter_self().peekable();
|
||||
|
||||
// If we encounter a type or const when we expect a lifetime, we infer the lifetimes.
|
||||
// If we later encounter a lifetime, we know that the arguments were provided in the
|
||||
// wrong order. `force_infer_lt` records the type or const that forced lifetimes to be
|
||||
// inferred, so we can use it for diagnostics later.
|
||||
let mut force_infer_lt = None;
|
||||
|
||||
let has_self_arg = args_and_bindings.is_some_and(|it| it.has_self_type);
|
||||
// First, handle `Self` parameter. Consume it from the args if provided, otherwise from `explicit_self_ty`,
|
||||
// and lastly infer it.
|
||||
if let Some(&(
|
||||
self_param_id,
|
||||
self_param @ GenericParamDataRef::TypeParamData(TypeParamData {
|
||||
provenance: TypeParamProvenance::TraitSelf,
|
||||
..
|
||||
}),
|
||||
)) = params.peek()
|
||||
{
|
||||
let self_ty = if has_self_arg {
|
||||
let (_, self_ty) = args.next().expect("has_self_type=true, should have Self type");
|
||||
ctx.provided_kind(self_param_id, self_param, self_ty)
|
||||
} else {
|
||||
explicit_self_ty.map(|it| it.cast(Interner)).unwrap_or_else(|| {
|
||||
ctx.inferred_kind(def, self_param_id, self_param, infer_args, &substs)
|
||||
})
|
||||
};
|
||||
params.next();
|
||||
substs.push(self_ty);
|
||||
}
|
||||
|
||||
loop {
|
||||
// We're going to iterate through the generic arguments that the user
|
||||
// provided, matching them with the generic parameters we expect.
|
||||
// Mismatches can occur as a result of elided lifetimes, or for malformed
|
||||
// input. We try to handle both sensibly.
|
||||
match (args.peek(), params.peek()) {
|
||||
(Some(&(arg_idx, arg)), Some(&(param_id, param))) => match (arg, param) {
|
||||
(GenericArg::Type(_), GenericParamDataRef::TypeParamData(type_param))
|
||||
if type_param.provenance == TypeParamProvenance::ArgumentImplTrait =>
|
||||
{
|
||||
// Do not allow specifying `impl Trait` explicitly. We already err at that, but if we won't handle it here
|
||||
// we will handle it as if it was specified, instead of inferring it.
|
||||
substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
|
||||
params.next();
|
||||
}
|
||||
(GenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_))
|
||||
| (GenericArg::Type(_), GenericParamDataRef::TypeParamData(_))
|
||||
| (GenericArg::Const(_), GenericParamDataRef::ConstParamData(_)) => {
|
||||
substs.push(ctx.provided_kind(param_id, param, arg));
|
||||
args.next();
|
||||
params.next();
|
||||
}
|
||||
(
|
||||
GenericArg::Type(_) | GenericArg::Const(_),
|
||||
GenericParamDataRef::LifetimeParamData(_),
|
||||
) => {
|
||||
// We expected a lifetime argument, but got a type or const
|
||||
// argument. That means we're inferring the lifetime.
|
||||
substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
|
||||
params.next();
|
||||
force_infer_lt = Some((arg_idx as u32, param_id));
|
||||
}
|
||||
(GenericArg::Type(type_ref), GenericParamDataRef::ConstParamData(_)) => {
|
||||
if let Some(konst) = type_looks_like_const(store, *type_ref) {
|
||||
let GenericParamId::ConstParamId(param_id) = param_id else {
|
||||
panic!("unmatching param kinds");
|
||||
};
|
||||
let const_ty = db.const_param_ty(param_id);
|
||||
substs.push(ctx.provided_type_like_const(const_ty, konst).cast(Interner));
|
||||
args.next();
|
||||
params.next();
|
||||
} else {
|
||||
// See the `_ => { ... }` branch.
|
||||
if !had_count_error {
|
||||
ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg);
|
||||
}
|
||||
while args.next().is_some() {}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// We expected one kind of parameter, but the user provided
|
||||
// another. This is an error. However, if we already know that
|
||||
// the arguments don't match up with the parameters, we won't issue
|
||||
// an additional error, as the user already knows what's wrong.
|
||||
if !had_count_error {
|
||||
ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg);
|
||||
}
|
||||
|
||||
// We've reported the error, but we want to make sure that this
|
||||
// problem doesn't bubble down and create additional, irrelevant
|
||||
// errors. In this case, we're simply going to ignore the argument
|
||||
// and any following arguments. The rest of the parameters will be
|
||||
// inferred.
|
||||
while args.next().is_some() {}
|
||||
}
|
||||
},
|
||||
|
||||
(Some(&(_, arg)), None) => {
|
||||
// We should never be able to reach this point with well-formed input.
|
||||
// There are two situations in which we can encounter this issue.
|
||||
//
|
||||
// 1. The number of arguments is incorrect. In this case, an error
|
||||
// will already have been emitted, and we can ignore it.
|
||||
// 2. We've inferred some lifetimes, which have been provided later (i.e.
|
||||
// after a type or const). We want to throw an error in this case.
|
||||
if !had_count_error {
|
||||
assert!(
|
||||
matches!(arg, GenericArg::Lifetime(_)),
|
||||
"the only possible situation here is incorrect lifetime order"
|
||||
);
|
||||
let (provided_arg_idx, param_id) =
|
||||
force_infer_lt.expect("lifetimes ought to have been inferred");
|
||||
ctx.report_arg_mismatch(param_id, provided_arg_idx, has_self_arg);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
(None, Some(&(param_id, param))) => {
|
||||
// If there are fewer arguments than parameters, it means we're inferring the remaining arguments.
|
||||
substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
|
||||
params.next();
|
||||
}
|
||||
|
||||
(None, None) => break,
|
||||
}
|
||||
}
|
||||
|
||||
Substitution::from_iter(Interner, substs)
|
||||
}
|
||||
|
||||
fn type_looks_like_const(
|
||||
store: &ExpressionStore,
|
||||
type_ref: TypeRefId,
|
||||
) -> Option<TypeLikeConst<'_>> {
|
||||
// A path/`_` const will be parsed as a type, instead of a const, because when parsing/lowering
|
||||
// in hir-def we don't yet know the expected argument kind. rustc does this a bit differently,
|
||||
// when lowering to HIR it resolves the path, and if it doesn't resolve to the type namespace
|
||||
// it is lowered as a const. Our behavior could deviate from rustc when the value is resolvable
|
||||
// in both the type and value namespaces, but I believe we only allow more code.
|
||||
let type_ref = &store[type_ref];
|
||||
match type_ref {
|
||||
TypeRef::Path(path) => Some(TypeLikeConst::Path(path)),
|
||||
TypeRef::Placeholder => Some(TypeLikeConst::Infer),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -6,14 +6,17 @@
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
DefWithBodyId, SyntheticSyntax,
|
||||
expr_store::{ExprOrPatPtr, ExpressionStoreSourceMap, hir_segment_to_ast_segment},
|
||||
DefWithBodyId, GenericParamId, SyntheticSyntax,
|
||||
expr_store::{
|
||||
ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast,
|
||||
hir_generic_arg_to_ast, hir_segment_to_ast_segment,
|
||||
},
|
||||
hir::ExprOrPatId,
|
||||
};
|
||||
use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name};
|
||||
use hir_ty::{
|
||||
CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathLoweringDiagnostic,
|
||||
TyLoweringDiagnostic, TyLoweringDiagnosticKind,
|
||||
CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathGenericsSource,
|
||||
PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
|
||||
db::HirDatabase,
|
||||
diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
|
||||
};
|
||||
@ -24,11 +27,11 @@ use syntax::{
|
||||
};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{AssocItem, Field, Function, Local, Trait, Type};
|
||||
use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type};
|
||||
|
||||
pub use hir_def::VariantId;
|
||||
pub use hir_ty::{
|
||||
GenericArgsProhibitedReason,
|
||||
GenericArgsProhibitedReason, IncorrectGenericsLenKind,
|
||||
diagnostics::{CaseType, IncorrectCase},
|
||||
};
|
||||
|
||||
@ -113,6 +116,8 @@ diagnostics![
|
||||
GenericArgsProhibited,
|
||||
ParenthesizedGenericArgsWithoutFnTrait,
|
||||
BadRtn,
|
||||
IncorrectGenericsLen,
|
||||
IncorrectGenericsOrder,
|
||||
];
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -425,6 +430,39 @@ pub struct BadRtn {
|
||||
pub rtn: InFile<AstPtr<ast::ReturnTypeSyntax>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IncorrectGenericsLen {
|
||||
/// Points at the name if there are no generics.
|
||||
pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
|
||||
pub kind: IncorrectGenericsLenKind,
|
||||
pub provided: u32,
|
||||
pub expected: u32,
|
||||
pub def: GenericDef,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GenericArgKind {
|
||||
Lifetime,
|
||||
Type,
|
||||
Const,
|
||||
}
|
||||
|
||||
impl GenericArgKind {
|
||||
fn from_id(id: GenericParamId) -> Self {
|
||||
match id {
|
||||
GenericParamId::TypeParamId(_) => GenericArgKind::Type,
|
||||
GenericParamId::ConstParamId(_) => GenericArgKind::Const,
|
||||
GenericParamId::LifetimeParamId(_) => GenericArgKind::Lifetime,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IncorrectGenericsOrder {
|
||||
pub provided_arg: InFile<AstPtr<ast::GenericArg>>,
|
||||
pub expected_kind: GenericArgKind,
|
||||
}
|
||||
|
||||
impl AnyDiagnostic {
|
||||
pub(crate) fn body_validation_diagnostic(
|
||||
db: &dyn HirDatabase,
|
||||
@ -714,6 +752,47 @@ impl AnyDiagnostic {
|
||||
};
|
||||
Self::path_diagnostic(diag, source.with_value(path))?
|
||||
}
|
||||
&InferenceDiagnostic::MethodCallIncorrectGenericsLen {
|
||||
expr,
|
||||
provided_count,
|
||||
expected_count,
|
||||
kind,
|
||||
def,
|
||||
} => {
|
||||
let syntax = expr_syntax(expr)?;
|
||||
let file_id = syntax.file_id;
|
||||
let syntax =
|
||||
syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
|
||||
let generics_or_name = syntax
|
||||
.generic_arg_list()
|
||||
.map(Either::Left)
|
||||
.or_else(|| syntax.name_ref().map(Either::Right))?;
|
||||
let generics_or_name = InFile::new(file_id, AstPtr::new(&generics_or_name));
|
||||
IncorrectGenericsLen {
|
||||
generics_or_segment: generics_or_name,
|
||||
kind,
|
||||
provided: provided_count,
|
||||
expected: expected_count,
|
||||
def: def.into(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
&InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
|
||||
expr,
|
||||
param_id,
|
||||
arg_idx,
|
||||
has_self_arg,
|
||||
} => {
|
||||
let syntax = expr_syntax(expr)?;
|
||||
let file_id = syntax.file_id;
|
||||
let syntax =
|
||||
syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
|
||||
let generic_args = syntax.generic_arg_list()?;
|
||||
let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
|
||||
let provided_arg = InFile::new(file_id, AstPtr::new(&provided_arg));
|
||||
let expected_kind = GenericArgKind::from_id(param_id);
|
||||
IncorrectGenericsOrder { provided_arg, expected_kind }.into()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -750,6 +829,38 @@ impl AnyDiagnostic {
|
||||
let args = path.with_value(args);
|
||||
ParenthesizedGenericArgsWithoutFnTrait { args }.into()
|
||||
}
|
||||
PathLoweringDiagnostic::IncorrectGenericsLen {
|
||||
generics_source,
|
||||
provided_count,
|
||||
expected_count,
|
||||
kind,
|
||||
def,
|
||||
} => {
|
||||
let generics_or_segment =
|
||||
path_generics_source_to_ast(&path.value, generics_source)?;
|
||||
let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
|
||||
IncorrectGenericsLen {
|
||||
generics_or_segment,
|
||||
kind,
|
||||
provided: provided_count,
|
||||
expected: expected_count,
|
||||
def: def.into(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
PathLoweringDiagnostic::IncorrectGenericsOrder {
|
||||
generics_source,
|
||||
param_id,
|
||||
arg_idx,
|
||||
has_self_arg,
|
||||
} => {
|
||||
let generic_args =
|
||||
path_generics_source_to_ast(&path.value, generics_source)?.left()?;
|
||||
let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
|
||||
let provided_arg = path.with_value(AstPtr::new(&provided_arg));
|
||||
let expected_kind = GenericArgKind::from_id(param_id);
|
||||
IncorrectGenericsOrder { provided_arg, expected_kind }.into()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -771,3 +882,27 @@ impl AnyDiagnostic {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn path_generics_source_to_ast(
|
||||
path: &ast::Path,
|
||||
generics_source: PathGenericsSource,
|
||||
) -> Option<Either<ast::GenericArgList, ast::NameRef>> {
|
||||
Some(match generics_source {
|
||||
PathGenericsSource::Segment(segment) => {
|
||||
let segment = hir_segment_to_ast_segment(path, segment)?;
|
||||
segment
|
||||
.generic_arg_list()
|
||||
.map(Either::Left)
|
||||
.or_else(|| segment.name_ref().map(Either::Right))?
|
||||
}
|
||||
PathGenericsSource::AssocType { segment, assoc_type } => {
|
||||
let segment = hir_segment_to_ast_segment(path, segment)?;
|
||||
let segment_args = segment.generic_arg_list()?;
|
||||
let assoc = hir_assoc_type_binding_to_ast(&segment_args, assoc_type)?;
|
||||
assoc
|
||||
.generic_arg_list()
|
||||
.map(Either::Left)
|
||||
.or_else(|| assoc.name_ref().map(Either::Right))?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1709,10 +1709,11 @@ impl_from!(Struct, Union, Enum for Adt);
|
||||
impl Adt {
|
||||
pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
|
||||
let subst = db.generic_defaults(self.into());
|
||||
subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
|
||||
GenericArgData::Ty(it) => it.is_unknown(),
|
||||
_ => false,
|
||||
})
|
||||
(subst.is_empty() && db.generic_params(self.into()).len_type_or_consts() != 0)
|
||||
|| subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
|
||||
GenericArgData::Ty(it) => it.is_unknown(),
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
|
||||
@ -3000,10 +3001,11 @@ pub struct TypeAlias {
|
||||
impl TypeAlias {
|
||||
pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
|
||||
let subst = db.generic_defaults(self.id.into());
|
||||
subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
|
||||
GenericArgData::Ty(it) => it.is_unknown(),
|
||||
_ => false,
|
||||
})
|
||||
(subst.is_empty() && db.generic_params(self.id.into()).len_type_or_consts() != 0)
|
||||
|| subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
|
||||
GenericArgData::Ty(it) => it.is_unknown(),
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
||||
@ -3732,6 +3734,23 @@ impl GenericDef {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a string describing the kind of this type.
|
||||
#[inline]
|
||||
pub fn description(self) -> &'static str {
|
||||
match self {
|
||||
GenericDef::Function(_) => "function",
|
||||
GenericDef::Adt(Adt::Struct(_)) => "struct",
|
||||
GenericDef::Adt(Adt::Enum(_)) => "enum",
|
||||
GenericDef::Adt(Adt::Union(_)) => "union",
|
||||
GenericDef::Trait(_) => "trait",
|
||||
GenericDef::TraitAlias(_) => "trait alias",
|
||||
GenericDef::TypeAlias(_) => "type alias",
|
||||
GenericDef::Impl(_) => "impl",
|
||||
GenericDef::Const(_) => "constant",
|
||||
GenericDef::Static(_) => "static",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot call this `Substitution` unfortunately...
|
||||
@ -4276,7 +4295,8 @@ fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Optio
|
||||
let local_idx = hir_ty::param_idx(db, id)?;
|
||||
let defaults = db.generic_defaults(id.parent);
|
||||
let ty = defaults.get(local_idx)?.clone();
|
||||
let subst = TyBuilder::placeholder_subst(db, id.parent);
|
||||
let full_subst = TyBuilder::placeholder_subst(db, id.parent);
|
||||
let subst = &full_subst.as_slice(Interner)[..local_idx];
|
||||
Some(ty.substitute(Interner, &subst))
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ fn describe_reason(reason: GenericArgsProhibitedReason) -> String {
|
||||
}
|
||||
GenericArgsProhibitedReason::Const => "constants",
|
||||
GenericArgsProhibitedReason::Static => "statics",
|
||||
GenericArgsProhibitedReason::LocalVariable => "local variables",
|
||||
};
|
||||
format!("generic arguments are not allowed on {kind}")
|
||||
}
|
||||
@ -320,7 +321,7 @@ trait E<A: foo::<()>::Trait>
|
||||
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
|
||||
}
|
||||
|
||||
impl<A: foo::<()>::Trait> E for ()
|
||||
impl<A: foo::<()>::Trait> E<()> for ()
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
|
||||
where bool<i32>: foo::Trait
|
||||
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
|
||||
@ -518,14 +519,14 @@ fn baz() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn const_and_static() {
|
||||
fn const_param_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
|
||||
fn baz<const CONST_PARAM: usize>() {
|
||||
let _ = CONST_PARAM::<()>;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on constants
|
||||
let _ = STATIC::<()>;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on statics
|
||||
}
|
||||
@ -533,6 +534,19 @@ fn baz() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_variable() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn baz() {
|
||||
let x = 1;
|
||||
let _ = x::<()>;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on local variables
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_variant() {
|
||||
check_diagnostics(
|
||||
|
172
crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
Normal file
172
crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
Normal file
@ -0,0 +1,172 @@
|
||||
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
|
||||
use hir::IncorrectGenericsLenKind;
|
||||
|
||||
// Diagnostic: incorrect-generics-len
|
||||
//
|
||||
// This diagnostic is triggered if the number of generic arguments does not match their declaration.
|
||||
pub(crate) fn incorrect_generics_len(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::IncorrectGenericsLen,
|
||||
) -> Diagnostic {
|
||||
let owner_description = d.def.description();
|
||||
let expected = d.expected;
|
||||
let provided = d.provided;
|
||||
let kind_description = match d.kind {
|
||||
IncorrectGenericsLenKind::Lifetimes => "lifetime",
|
||||
IncorrectGenericsLenKind::TypesAndConsts => "generic",
|
||||
};
|
||||
let message = format!(
|
||||
"this {owner_description} takes {expected} {kind_description} argument{} \
|
||||
but {provided} {kind_description} argument{} {} supplied",
|
||||
if expected == 1 { "" } else { "s" },
|
||||
if provided == 1 { "" } else { "s" },
|
||||
if provided == 1 { "was" } else { "were" },
|
||||
);
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
DiagnosticCode::RustcHardError("E0107"),
|
||||
message,
|
||||
d.generics_or_segment.map(Into::into),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::check_diagnostics;
|
||||
|
||||
#[test]
|
||||
fn partially_specified_generics() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Bar<T, U>(T, U);
|
||||
|
||||
fn foo() {
|
||||
let _ = Bar::<()>;
|
||||
// ^^^^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_variant() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
enum Enum<T, U> {
|
||||
Variant(T, U),
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
let _ = Enum::<()>::Variant;
|
||||
// ^^^^^^ error: this enum takes 2 generic arguments but 1 generic argument was supplied
|
||||
let _ = Enum::Variant::<()>;
|
||||
// ^^^^^^ error: this enum takes 2 generic arguments but 1 generic argument was supplied
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lifetimes() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<'a, 'b>(&'a &'b ());
|
||||
struct Bar<'a>(&'a ());
|
||||
|
||||
fn foo() -> Foo {
|
||||
let _ = Foo;
|
||||
let _ = Foo::<>;
|
||||
let _ = Foo::<'static>;
|
||||
// ^^^^^^^^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn bar(_v: Bar) -> Foo { loop {} }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_error_for_elided_lifetimes() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<'a>(&'a ());
|
||||
|
||||
fn foo(_v: &()) -> Foo { loop {} }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errs_for_elided_lifetimes_if_lifetimes_are_explicitly_provided() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<'a, 'b>(&'a &'b ());
|
||||
|
||||
fn foo(_v: Foo<'_>
|
||||
// ^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
|
||||
) -> Foo<'static> { loop {} }
|
||||
// ^^^^^^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn types_and_consts() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<'a, T>(&'a T);
|
||||
fn foo(_v: Foo) {}
|
||||
// ^^^ error: this struct takes 1 generic argument but 0 generic arguments were supplied
|
||||
|
||||
struct Bar<T, const N: usize>(T);
|
||||
fn bar() {
|
||||
let _ = Bar::<()>;
|
||||
// ^^^^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn respects_defaults() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<T = (), const N: usize = 0>(T);
|
||||
fn foo(_v: Foo) {}
|
||||
|
||||
struct Bar<T, const N: usize = 0>(T);
|
||||
fn bar(_v: Bar<()>) {}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constant() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
const CONST: i32 = 0;
|
||||
fn baz() {
|
||||
let _ = CONST::<()>;
|
||||
// ^^^^^^ error: this constant takes 0 generic arguments but 1 generic argument was supplied
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assoc_type() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
trait Trait {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
fn foo<T: Trait<Assoc<i32> = bool>>() {}
|
||||
// ^^^^^ error: this type alias takes 0 generic arguments but 1 generic argument was supplied
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
|
||||
use hir::GenericArgKind;
|
||||
use syntax::SyntaxKind;
|
||||
|
||||
// Diagnostic: incorrect-generics-order
|
||||
//
|
||||
// This diagnostic is triggered the order of provided generic arguments does not match their declaration.
|
||||
pub(crate) fn incorrect_generics_order(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::IncorrectGenericsOrder,
|
||||
) -> Diagnostic {
|
||||
let provided_description = match d.provided_arg.value.kind() {
|
||||
SyntaxKind::CONST_ARG => "constant",
|
||||
SyntaxKind::LIFETIME_ARG => "lifetime",
|
||||
SyntaxKind::TYPE_ARG => "type",
|
||||
_ => panic!("non-generic-arg passed to `incorrect_generics_order()`"),
|
||||
};
|
||||
let expected_description = match d.expected_kind {
|
||||
GenericArgKind::Lifetime => "lifetime",
|
||||
GenericArgKind::Type => "type",
|
||||
GenericArgKind::Const => "constant",
|
||||
};
|
||||
let message =
|
||||
format!("{provided_description} provided when a {expected_description} was expected");
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
DiagnosticCode::RustcHardError("E0747"),
|
||||
message,
|
||||
d.provided_arg.map(Into::into),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::check_diagnostics;
|
||||
|
||||
#[test]
|
||||
fn lifetime_out_of_order() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<'a, T>(&'a T);
|
||||
|
||||
fn bar(_v: Foo<(), 'static>) {}
|
||||
// ^^ error: type provided when a lifetime was expected
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn types_and_consts() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<T>(T);
|
||||
fn foo1(_v: Foo<1>) {}
|
||||
// ^ error: constant provided when a type was expected
|
||||
fn foo2(_v: Foo<{ (1, 2) }>) {}
|
||||
// ^^^^^^^^^^ error: constant provided when a type was expected
|
||||
|
||||
struct Bar<const N: usize>;
|
||||
fn bar(_v: Bar<()>) {}
|
||||
// ^^ error: type provided when a constant was expected
|
||||
|
||||
struct Baz<T, const N: usize>(T);
|
||||
fn baz(_v: Baz<1, ()>) {}
|
||||
// ^ error: constant provided when a type was expected
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_error_when_num_incorrect() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Baz<T, U>(T, U);
|
||||
fn baz(_v: Baz<1>) {}
|
||||
// ^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -32,6 +32,8 @@ mod handlers {
|
||||
pub(crate) mod inactive_code;
|
||||
pub(crate) mod incoherent_impl;
|
||||
pub(crate) mod incorrect_case;
|
||||
pub(crate) mod incorrect_generics_len;
|
||||
pub(crate) mod incorrect_generics_order;
|
||||
pub(crate) mod invalid_cast;
|
||||
pub(crate) mod invalid_derive_target;
|
||||
pub(crate) mod macro_error;
|
||||
@ -499,6 +501,8 @@ pub fn semantic_diagnostics(
|
||||
handlers::parenthesized_generic_args_without_fn_trait::parenthesized_generic_args_without_fn_trait(&ctx, &d)
|
||||
}
|
||||
AnyDiagnostic::BadRtn(d) => handlers::bad_rtn::bad_rtn(&ctx, &d),
|
||||
AnyDiagnostic::IncorrectGenericsLen(d) => handlers::incorrect_generics_len::incorrect_generics_len(&ctx, &d),
|
||||
AnyDiagnostic::IncorrectGenericsOrder(d) => handlers::incorrect_generics_order::incorrect_generics_order(&ctx, &d),
|
||||
};
|
||||
res.push(d)
|
||||
}
|
||||
|
@ -1063,7 +1063,7 @@ pub mod cmp {
|
||||
// region:fmt
|
||||
pub mod fmt {
|
||||
pub struct Error;
|
||||
pub type Result = Result<(), Error>;
|
||||
pub type Result = crate::result::Result<(), Error>;
|
||||
pub struct Formatter<'a>;
|
||||
pub struct DebugTuple;
|
||||
pub struct DebugStruct;
|
||||
|
Loading…
x
Reference in New Issue
Block a user