mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 11:20:54 +00:00
Merge pull request #20273 from ShoyuVanilla/match-adjusts
fix: Apply adjusts to pats and exprs when doing pat analysis
This commit is contained in:
commit
9a1ee18e4d
@ -175,8 +175,9 @@ impl ExprValidator {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let receiver_ty = self.infer[*receiver].clone();
|
if let Some(receiver_ty) = self.infer.type_of_expr_with_adjust(*receiver) {
|
||||||
checker.prev_receiver_ty = Some(receiver_ty);
|
checker.prev_receiver_ty = Some(receiver_ty.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +188,9 @@ impl ExprValidator {
|
|||||||
arms: &[MatchArm],
|
arms: &[MatchArm],
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
) {
|
) {
|
||||||
let scrut_ty = &self.infer[scrutinee_expr];
|
let Some(scrut_ty) = self.infer.type_of_expr_with_adjust(scrutinee_expr) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
if scrut_ty.contains_unknown() {
|
if scrut_ty.contains_unknown() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -200,7 +203,7 @@ impl ExprValidator {
|
|||||||
// Note: Skipping the entire diagnostic rather than just not including a faulty match arm is
|
// Note: Skipping the entire diagnostic rather than just not including a faulty match arm is
|
||||||
// preferred to avoid the chance of false positives.
|
// preferred to avoid the chance of false positives.
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) else {
|
let Some(pat_ty) = self.infer.type_of_pat_with_adjust(arm.pat) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if pat_ty.contains_unknown() {
|
if pat_ty.contains_unknown() {
|
||||||
@ -328,7 +331,7 @@ impl ExprValidator {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let Some(initializer) = initializer else { continue };
|
let Some(initializer) = initializer else { continue };
|
||||||
let ty = &self.infer[initializer];
|
let Some(ty) = self.infer.type_of_expr_with_adjust(initializer) else { continue };
|
||||||
if ty.contains_unknown() {
|
if ty.contains_unknown() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -433,44 +436,44 @@ impl ExprValidator {
|
|||||||
Statement::Expr { expr, .. } => Some(*expr),
|
Statement::Expr { expr, .. } => Some(*expr),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
if let Some(last_then_expr) = last_then_expr {
|
if let Some(last_then_expr) = last_then_expr
|
||||||
let last_then_expr_ty = &self.infer[last_then_expr];
|
&& let Some(last_then_expr_ty) =
|
||||||
if last_then_expr_ty.is_never() {
|
self.infer.type_of_expr_with_adjust(last_then_expr)
|
||||||
// Only look at sources if the then branch diverges and we have an else branch.
|
&& last_then_expr_ty.is_never()
|
||||||
let source_map = db.body_with_source_map(self.owner).1;
|
{
|
||||||
let Ok(source_ptr) = source_map.expr_syntax(id) else {
|
// Only look at sources if the then branch diverges and we have an else branch.
|
||||||
return;
|
let source_map = db.body_with_source_map(self.owner).1;
|
||||||
};
|
let Ok(source_ptr) = source_map.expr_syntax(id) else {
|
||||||
let root = source_ptr.file_syntax(db);
|
return;
|
||||||
let either::Left(ast::Expr::IfExpr(if_expr)) =
|
};
|
||||||
source_ptr.value.to_node(&root)
|
let root = source_ptr.file_syntax(db);
|
||||||
else {
|
let either::Left(ast::Expr::IfExpr(if_expr)) = source_ptr.value.to_node(&root)
|
||||||
return;
|
else {
|
||||||
};
|
return;
|
||||||
let mut top_if_expr = if_expr;
|
};
|
||||||
loop {
|
let mut top_if_expr = if_expr;
|
||||||
let parent = top_if_expr.syntax().parent();
|
loop {
|
||||||
let has_parent_expr_stmt_or_stmt_list =
|
let parent = top_if_expr.syntax().parent();
|
||||||
parent.as_ref().is_some_and(|node| {
|
let has_parent_expr_stmt_or_stmt_list =
|
||||||
ast::ExprStmt::can_cast(node.kind())
|
parent.as_ref().is_some_and(|node| {
|
||||||
| ast::StmtList::can_cast(node.kind())
|
ast::ExprStmt::can_cast(node.kind())
|
||||||
});
|
| ast::StmtList::can_cast(node.kind())
|
||||||
if has_parent_expr_stmt_or_stmt_list {
|
});
|
||||||
// Only emit diagnostic if parent or direct ancestor is either
|
if has_parent_expr_stmt_or_stmt_list {
|
||||||
// an expr stmt or a stmt list.
|
// Only emit diagnostic if parent or direct ancestor is either
|
||||||
break;
|
// an expr stmt or a stmt list.
|
||||||
}
|
break;
|
||||||
let Some(parent_if_expr) = parent.and_then(ast::IfExpr::cast) else {
|
|
||||||
// Bail if parent is neither an if expr, an expr stmt nor a stmt list.
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
// Check parent if expr.
|
|
||||||
top_if_expr = parent_if_expr;
|
|
||||||
}
|
}
|
||||||
|
let Some(parent_if_expr) = parent.and_then(ast::IfExpr::cast) else {
|
||||||
self.diagnostics
|
// Bail if parent is neither an if expr, an expr stmt nor a stmt list.
|
||||||
.push(BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr: id })
|
return;
|
||||||
|
};
|
||||||
|
// Check parent if expr.
|
||||||
|
top_if_expr = parent_if_expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.diagnostics
|
||||||
|
.push(BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr: id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -561,6 +561,32 @@ impl InferenceResult {
|
|||||||
ExprOrPatId::PatId(id) => self.type_of_pat.get(id),
|
ExprOrPatId::PatId(id) => self.type_of_pat.get(id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn type_of_expr_with_adjust(&self, id: ExprId) -> Option<&Ty> {
|
||||||
|
match self.expr_adjustments.get(&id).and_then(|adjustments| {
|
||||||
|
adjustments
|
||||||
|
.iter()
|
||||||
|
.filter(|adj| {
|
||||||
|
// https://github.com/rust-lang/rust/blob/67819923ac8ea353aaa775303f4c3aacbf41d010/compiler/rustc_mir_build/src/thir/cx/expr.rs#L140
|
||||||
|
!matches!(
|
||||||
|
adj,
|
||||||
|
Adjustment {
|
||||||
|
kind: Adjust::NeverToAny,
|
||||||
|
target,
|
||||||
|
} if target.is_never()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.next_back()
|
||||||
|
}) {
|
||||||
|
Some(adjustment) => Some(&adjustment.target),
|
||||||
|
None => self.type_of_expr.get(id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn type_of_pat_with_adjust(&self, id: PatId) -> Option<&Ty> {
|
||||||
|
match self.pat_adjustments.get(&id).and_then(|adjustments| adjustments.last()) {
|
||||||
|
adjusted @ Some(_) => adjusted,
|
||||||
|
None => self.type_of_pat.get(id),
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn is_erroneous(&self) -> bool {
|
pub fn is_erroneous(&self) -> bool {
|
||||||
self.has_errors && self.type_of_expr.iter().count() == 0
|
self.has_errors && self.type_of_expr.iter().count() == 0
|
||||||
}
|
}
|
||||||
|
@ -441,7 +441,7 @@ impl<'db> SourceAnalyzer<'db> {
|
|||||||
) -> Option<GenericSubstitution<'db>> {
|
) -> Option<GenericSubstitution<'db>> {
|
||||||
let body = self.store()?;
|
let body = self.store()?;
|
||||||
if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] {
|
if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] {
|
||||||
let (adt, subst) = type_of_expr_including_adjust(infer, object_expr)?.as_adt()?;
|
let (adt, subst) = infer.type_of_expr_with_adjust(object_expr)?.as_adt()?;
|
||||||
return Some(GenericSubstitution::new(
|
return Some(GenericSubstitution::new(
|
||||||
adt.into(),
|
adt.into(),
|
||||||
subst.clone(),
|
subst.clone(),
|
||||||
@ -1780,10 +1780,3 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H
|
|||||||
let ctx = span_map.span_at(name.value.text_range().start()).ctx;
|
let ctx = span_map.span_at(name.value.text_range().start()).ctx;
|
||||||
HygieneId::new(ctx.opaque_and_semitransparent(db))
|
HygieneId::new(ctx.opaque_and_semitransparent(db))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_of_expr_including_adjust(infer: &InferenceResult, id: ExprId) -> Option<&Ty> {
|
|
||||||
match infer.expr_adjustment(id).and_then(|adjustments| adjustments.last()) {
|
|
||||||
Some(adjustment) => Some(&adjustment.target),
|
|
||||||
None => Some(&infer[id]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -131,4 +131,28 @@ fn foo(v: Enum<()>) {
|
|||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn regression_20259() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: deref
|
||||||
|
use core::ops::Deref;
|
||||||
|
|
||||||
|
struct Foo<T>(T);
|
||||||
|
|
||||||
|
impl<T> Deref for Foo<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test(x: Foo<(i32, bool)>) {
|
||||||
|
let (_a, _b): &(i32, bool) = &x;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user