mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
Merge commit '21b06c1beb
' into sync-from-ra
This commit is contained in:
parent
cac74d98f6
commit
e37cf75791
59 changed files with 1080 additions and 477 deletions
|
@ -113,7 +113,7 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
|
|||
// FIXME(const-generic-body): We should not get the return type in this way.
|
||||
ctx.return_ty = c
|
||||
.lookup(db.upcast())
|
||||
.thing
|
||||
.expected_ty
|
||||
.box_any()
|
||||
.downcast::<InTypeConstIdMetadata>()
|
||||
.unwrap()
|
||||
|
|
|
@ -262,7 +262,7 @@ impl InferenceContext<'_> {
|
|||
fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty {
|
||||
let mut expected = self.resolve_ty_shallow(expected);
|
||||
|
||||
if is_non_ref_pat(self.body, pat) {
|
||||
if self.is_non_ref_pat(self.body, pat) {
|
||||
let mut pat_adjustments = Vec::new();
|
||||
while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
|
||||
pat_adjustments.push(expected.clone());
|
||||
|
@ -496,24 +496,28 @@ impl InferenceContext<'_> {
|
|||
|
||||
self.infer_expr(expr, &Expectation::has_type(expected.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
|
||||
match &body[pat] {
|
||||
Pat::Tuple { .. }
|
||||
| Pat::TupleStruct { .. }
|
||||
| Pat::Record { .. }
|
||||
| Pat::Range { .. }
|
||||
| Pat::Slice { .. } => true,
|
||||
Pat::Or(pats) => pats.iter().all(|p| is_non_ref_pat(body, *p)),
|
||||
// FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented.
|
||||
Pat::Path(..) => true,
|
||||
Pat::ConstBlock(..) => true,
|
||||
Pat::Lit(expr) => !matches!(
|
||||
body[*expr],
|
||||
Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..))
|
||||
),
|
||||
Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false,
|
||||
fn is_non_ref_pat(&mut self, body: &hir_def::body::Body, pat: PatId) -> bool {
|
||||
match &body[pat] {
|
||||
Pat::Tuple { .. }
|
||||
| Pat::TupleStruct { .. }
|
||||
| Pat::Record { .. }
|
||||
| Pat::Range { .. }
|
||||
| Pat::Slice { .. } => true,
|
||||
Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(body, *p)),
|
||||
Pat::Path(p) => {
|
||||
let v = self.resolve_value_path_inner(p, pat.into());
|
||||
v.is_some_and(|x| !matches!(x.0, hir_def::resolver::ValueNs::ConstId(_)))
|
||||
}
|
||||
Pat::ConstBlock(..) => false,
|
||||
Pat::Lit(expr) => !matches!(
|
||||
body[*expr],
|
||||
Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..))
|
||||
),
|
||||
Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,33 +40,7 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
|
||||
fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<ValuePathResolution> {
|
||||
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 ctx =
|
||||
crate::lower::TyLoweringContext::new(self.db, &self.resolver, 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);
|
||||
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 {
|
||||
// FIXME: report error, unresolved first path segment
|
||||
let value_or_partial =
|
||||
self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
|
||||
|
||||
match value_or_partial {
|
||||
ResolveValueResult::ValueNs(it, _) => (it, None),
|
||||
ResolveValueResult::Partial(def, remaining_index, _) => self
|
||||
.resolve_assoc_item(def, path, remaining_index, id)
|
||||
.map(|(it, substs)| (it, Some(substs)))?,
|
||||
}
|
||||
};
|
||||
let (value, self_subst) = self.resolve_value_path_inner(path, id)?;
|
||||
|
||||
let value_def = match value {
|
||||
ValueNs::LocalBinding(pat) => match self.result.type_of_binding.get(pat) {
|
||||
|
@ -144,6 +118,41 @@ impl InferenceContext<'_> {
|
|||
Some(ValuePathResolution::GenericDef(value_def, generic_def, substs))
|
||||
}
|
||||
|
||||
pub(super) fn resolve_value_path_inner(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
id: ExprOrPatId,
|
||||
) -> Option<(ValueNs, Option<chalk_ir::Substitution<Interner>>)> {
|
||||
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 ctx =
|
||||
crate::lower::TyLoweringContext::new(self.db, &self.resolver, 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);
|
||||
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 {
|
||||
// FIXME: report error, unresolved first path segment
|
||||
let value_or_partial =
|
||||
self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
|
||||
|
||||
match value_or_partial {
|
||||
ResolveValueResult::ValueNs(it, _) => (it, None),
|
||||
ResolveValueResult::Partial(def, remaining_index, _) => self
|
||||
.resolve_assoc_item(def, path, remaining_index, id)
|
||||
.map(|(it, substs)| (it, Some(substs)))?,
|
||||
}
|
||||
};
|
||||
Some((value, self_subst))
|
||||
}
|
||||
|
||||
fn add_required_obligations_for_value_path(&mut self, def: GenericDefId, subst: &Substitution) {
|
||||
let predicates = self.db.generic_predicates(def);
|
||||
for predicate in predicates.iter() {
|
||||
|
|
|
@ -113,7 +113,9 @@ pub struct TyLoweringContext<'a> {
|
|||
pub db: &'a dyn HirDatabase,
|
||||
resolver: &'a Resolver,
|
||||
in_binders: DebruijnIndex,
|
||||
owner: TypeOwnerId,
|
||||
// FIXME: Should not be an `Option` but `Resolver` currently does not return owners in all cases
|
||||
// where expected
|
||||
owner: Option<TypeOwnerId>,
|
||||
/// Note: Conceptually, it's thinkable that we could be in a location where
|
||||
/// some type params should be represented as placeholders, and others
|
||||
/// should be converted to variables. I think in practice, this isn't
|
||||
|
@ -127,6 +129,14 @@ pub struct TyLoweringContext<'a> {
|
|||
|
||||
impl<'a> TyLoweringContext<'a> {
|
||||
pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver, owner: TypeOwnerId) -> Self {
|
||||
Self::new_maybe_unowned(db, resolver, Some(owner))
|
||||
}
|
||||
|
||||
pub fn new_maybe_unowned(
|
||||
db: &'a dyn HirDatabase,
|
||||
resolver: &'a Resolver,
|
||||
owner: Option<TypeOwnerId>,
|
||||
) -> Self {
|
||||
let impl_trait_mode = ImplTraitLoweringState::Disallowed;
|
||||
let type_param_mode = ParamLoweringMode::Placeholder;
|
||||
let in_binders = DebruijnIndex::INNERMOST;
|
||||
|
@ -213,10 +223,11 @@ impl<'a> TyLoweringContext<'a> {
|
|||
}
|
||||
|
||||
pub fn lower_const(&self, const_ref: &ConstRef, const_type: Ty) -> Const {
|
||||
let Some(owner) = self.owner else { return unknown_const(const_type) };
|
||||
const_or_path_to_chalk(
|
||||
self.db,
|
||||
self.resolver,
|
||||
self.owner,
|
||||
owner,
|
||||
const_type,
|
||||
const_ref,
|
||||
self.type_param_mode,
|
||||
|
@ -1768,10 +1779,11 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
|
|||
let resolver = t.resolver(db.upcast());
|
||||
let ctx = TyLoweringContext::new(db, &resolver, t.into())
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
if db.type_alias_data(t).is_extern {
|
||||
let type_alias_data = db.type_alias_data(t);
|
||||
if type_alias_data.is_extern {
|
||||
Binders::empty(Interner, TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner))
|
||||
} else {
|
||||
let type_ref = &db.type_alias_data(t).type_ref;
|
||||
let type_ref = &type_alias_data.type_ref;
|
||||
let inner = ctx.lower_ty(type_ref.as_deref().unwrap_or(&TypeRef::Error));
|
||||
make_binders(db, &generics, inner)
|
||||
}
|
||||
|
@ -2042,7 +2054,7 @@ pub(crate) fn const_or_path_to_chalk(
|
|||
.intern_in_type_const(InTypeConstLoc {
|
||||
id: it,
|
||||
owner,
|
||||
thing: Box::new(InTypeConstIdMetadata(expected_ty.clone())),
|
||||
expected_ty: Box::new(InTypeConstIdMetadata(expected_ty.clone())),
|
||||
})
|
||||
.into();
|
||||
intern_const_scalar(
|
||||
|
|
|
@ -9,11 +9,10 @@ use super::visit_module;
|
|||
fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
|
||||
let (mut db, pos) = TestDB::with_position(
|
||||
"
|
||||
//- /lib.rs
|
||||
fn foo() -> i32 {
|
||||
$01 + 1
|
||||
}
|
||||
",
|
||||
//- /lib.rs
|
||||
fn foo() -> i32 {
|
||||
$01 + 1
|
||||
}",
|
||||
);
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
|
@ -27,12 +26,11 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
|
|||
}
|
||||
|
||||
let new_text = "
|
||||
fn foo() -> i32 {
|
||||
1
|
||||
+
|
||||
1
|
||||
}
|
||||
";
|
||||
fn foo() -> i32 {
|
||||
1
|
||||
+
|
||||
1
|
||||
}";
|
||||
|
||||
db.set_file_text(pos.file_id, Arc::from(new_text));
|
||||
|
||||
|
@ -47,3 +45,55 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
|
|||
assert!(!format!("{events:?}").contains("infer"), "{events:#?}")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typing_inside_a_function_should_not_invalidate_types_in_another() {
|
||||
let (mut db, pos) = TestDB::with_position(
|
||||
"
|
||||
//- /lib.rs
|
||||
fn foo() -> f32 {
|
||||
1.0 + 2.0
|
||||
}
|
||||
fn bar() -> i32 {
|
||||
$01 + 1
|
||||
}
|
||||
fn baz() -> i32 {
|
||||
1 + 1
|
||||
}",
|
||||
);
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
let module = db.module_for_file(pos.file_id);
|
||||
let crate_def_map = module.def_map(&db);
|
||||
visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
|
||||
db.infer(def);
|
||||
});
|
||||
});
|
||||
assert!(format!("{events:?}").contains("infer"))
|
||||
}
|
||||
|
||||
let new_text = "
|
||||
fn foo() -> f32 {
|
||||
1.0 + 2.0
|
||||
}
|
||||
fn bar() -> i32 {
|
||||
53
|
||||
}
|
||||
fn baz() -> i32 {
|
||||
1 + 1
|
||||
}
|
||||
";
|
||||
|
||||
db.set_file_text(pos.file_id, Arc::from(new_text));
|
||||
|
||||
{
|
||||
let events = db.log_executed(|| {
|
||||
let module = db.module_for_file(pos.file_id);
|
||||
let crate_def_map = module.def_map(&db);
|
||||
visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
|
||||
db.infer(def);
|
||||
});
|
||||
});
|
||||
assert!(format!("{events:?}").matches("infer").count() == 1, "{events:#?}")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1153,3 +1153,41 @@ fn main() {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_mismatch_pat_const_reference() {
|
||||
check_no_mismatches(
|
||||
r#"
|
||||
const TEST_STR: &'static str = "abcd";
|
||||
|
||||
fn main() {
|
||||
let s = "abcd";
|
||||
match s {
|
||||
TEST_STR => (),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
struct Foo<T>(T);
|
||||
|
||||
impl<T> Foo<T> {
|
||||
const TEST_I32_REF: &'static i32 = &3;
|
||||
const TEST_I32: i32 = 3;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
match &6 {
|
||||
Foo::<i32>::TEST_I32_REF => (),
|
||||
Foo::<i32>::TEST_I32 => (),
|
||||
//^^^^^^^^^^^^^^^^^^^^ expected &i32, got i32
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue