mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 13:48:50 +00:00
Merge pull request #18254 from ChayimFriedman2/fix-mut
fix: Nail destructuring assignment once and for all
This commit is contained in:
commit
c286786888
29 changed files with 1171 additions and 860 deletions
|
@ -10,6 +10,7 @@ use std::ops::{Deref, Index};
|
|||
|
||||
use base_db::CrateId;
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_expand::{name::Name, ExpandError, InFile};
|
||||
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
@ -22,7 +23,8 @@ use crate::{
|
|||
db::DefDatabase,
|
||||
expander::Expander,
|
||||
hir::{
|
||||
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
|
||||
dummy_expr_id, Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label,
|
||||
LabelId, Pat, PatId, RecordFieldPat, Statement,
|
||||
},
|
||||
item_tree::AttrOwner,
|
||||
nameres::DefMap,
|
||||
|
@ -67,9 +69,12 @@ pub type LabelSource = InFile<LabelPtr>;
|
|||
pub type FieldPtr = AstPtr<ast::RecordExprField>;
|
||||
pub type FieldSource = InFile<FieldPtr>;
|
||||
|
||||
pub type PatFieldPtr = AstPtr<ast::RecordPatField>;
|
||||
pub type PatFieldPtr = AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>;
|
||||
pub type PatFieldSource = InFile<PatFieldPtr>;
|
||||
|
||||
pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>;
|
||||
pub type ExprOrPatSource = InFile<ExprOrPatPtr>;
|
||||
|
||||
/// An item body together with the mapping from syntax nodes to HIR expression
|
||||
/// IDs. This is needed to go from e.g. a position in a file to the HIR
|
||||
/// expression containing it; but for type inference etc., we want to operate on
|
||||
|
@ -83,11 +88,13 @@ pub type PatFieldSource = InFile<PatFieldPtr>;
|
|||
/// this properly for macros.
|
||||
#[derive(Default, Debug, Eq, PartialEq)]
|
||||
pub struct BodySourceMap {
|
||||
expr_map: FxHashMap<ExprSource, ExprId>,
|
||||
// AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
|
||||
// to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
|
||||
expr_map: FxHashMap<ExprSource, ExprOrPatId>,
|
||||
expr_map_back: ArenaMap<ExprId, ExprSource>,
|
||||
|
||||
pat_map: FxHashMap<PatSource, PatId>,
|
||||
pat_map_back: ArenaMap<PatId, PatSource>,
|
||||
pat_map_back: ArenaMap<PatId, ExprOrPatSource>,
|
||||
|
||||
label_map: FxHashMap<LabelSource, LabelId>,
|
||||
label_map_back: ArenaMap<LabelId, LabelSource>,
|
||||
|
@ -286,7 +293,8 @@ impl Body {
|
|||
| Pat::Path(..)
|
||||
| Pat::ConstBlock(..)
|
||||
| Pat::Wild
|
||||
| Pat::Missing => {}
|
||||
| Pat::Missing
|
||||
| Pat::Expr(_) => {}
|
||||
&Pat::Bind { subpat, .. } => {
|
||||
if let Some(subpat) = subpat {
|
||||
f(subpat);
|
||||
|
@ -322,6 +330,143 @@ impl Body {
|
|||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_child_exprs(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) {
|
||||
let expr = &self[expr_id];
|
||||
match expr {
|
||||
Expr::Continue { .. }
|
||||
| Expr::Const(_)
|
||||
| Expr::Missing
|
||||
| Expr::Path(_)
|
||||
| Expr::OffsetOf(_)
|
||||
| Expr::Literal(_)
|
||||
| Expr::Underscore => {}
|
||||
Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
|
||||
AsmOperand::In { expr, .. }
|
||||
| AsmOperand::Out { expr: Some(expr), .. }
|
||||
| AsmOperand::InOut { expr, .. } => f(*expr),
|
||||
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
f(*in_expr);
|
||||
if let Some(out_expr) = out_expr {
|
||||
f(*out_expr);
|
||||
}
|
||||
}
|
||||
AsmOperand::Out { expr: None, .. }
|
||||
| AsmOperand::Const(_)
|
||||
| AsmOperand::Label(_)
|
||||
| AsmOperand::Sym(_) => (),
|
||||
}),
|
||||
Expr::If { condition, then_branch, else_branch } => {
|
||||
f(*condition);
|
||||
f(*then_branch);
|
||||
if let &Some(else_branch) = else_branch {
|
||||
f(else_branch);
|
||||
}
|
||||
}
|
||||
Expr::Let { expr, .. } => {
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Block { statements, tail, .. }
|
||||
| Expr::Unsafe { statements, tail, .. }
|
||||
| Expr::Async { statements, tail, .. } => {
|
||||
for stmt in statements.iter() {
|
||||
match stmt {
|
||||
Statement::Let { initializer, else_branch, pat, .. } => {
|
||||
if let &Some(expr) = initializer {
|
||||
f(expr);
|
||||
}
|
||||
if let &Some(expr) = else_branch {
|
||||
f(expr);
|
||||
}
|
||||
self.walk_exprs_in_pat(*pat, &mut f);
|
||||
}
|
||||
Statement::Expr { expr: expression, .. } => f(*expression),
|
||||
Statement::Item => (),
|
||||
}
|
||||
}
|
||||
if let &Some(expr) = tail {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Loop { body, .. } => f(*body),
|
||||
Expr::Call { callee, args, .. } => {
|
||||
f(*callee);
|
||||
args.iter().copied().for_each(f);
|
||||
}
|
||||
Expr::MethodCall { receiver, args, .. } => {
|
||||
f(*receiver);
|
||||
args.iter().copied().for_each(f);
|
||||
}
|
||||
Expr::Match { expr, arms } => {
|
||||
f(*expr);
|
||||
arms.iter().map(|arm| arm.expr).for_each(f);
|
||||
}
|
||||
Expr::Break { expr, .. }
|
||||
| Expr::Return { expr }
|
||||
| Expr::Yield { expr }
|
||||
| Expr::Yeet { expr } => {
|
||||
if let &Some(expr) = expr {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Become { expr } => f(*expr),
|
||||
Expr::RecordLit { fields, spread, .. } => {
|
||||
for field in fields.iter() {
|
||||
f(field.expr);
|
||||
}
|
||||
if let &Some(expr) = spread {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Closure { body, .. } => {
|
||||
f(*body);
|
||||
}
|
||||
Expr::BinaryOp { lhs, rhs, .. } => {
|
||||
f(*lhs);
|
||||
f(*rhs);
|
||||
}
|
||||
Expr::Range { lhs, rhs, .. } => {
|
||||
if let &Some(lhs) = rhs {
|
||||
f(lhs);
|
||||
}
|
||||
if let &Some(rhs) = lhs {
|
||||
f(rhs);
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index, .. } => {
|
||||
f(*base);
|
||||
f(*index);
|
||||
}
|
||||
Expr::Field { expr, .. }
|
||||
| Expr::Await { expr }
|
||||
| Expr::Cast { expr, .. }
|
||||
| Expr::Ref { expr, .. }
|
||||
| Expr::UnaryOp { expr, .. }
|
||||
| Expr::Box { expr } => {
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
|
||||
Expr::Array(a) => match a {
|
||||
Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
|
||||
Array::Repeat { initializer, repeat } => {
|
||||
f(*initializer);
|
||||
f(*repeat)
|
||||
}
|
||||
},
|
||||
&Expr::Assignment { target, value } => {
|
||||
self.walk_exprs_in_pat(target, &mut f);
|
||||
f(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_exprs_in_pat(&self, pat_id: PatId, f: &mut impl FnMut(ExprId)) {
|
||||
self.walk_pats(pat_id, &mut |pat| {
|
||||
if let Pat::Expr(expr) | Pat::ConstBlock(expr) = self[pat] {
|
||||
f(expr);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Body {
|
||||
|
@ -375,11 +520,18 @@ impl Index<BindingId> for Body {
|
|||
// FIXME: Change `node_` prefix to something more reasonable.
|
||||
// Perhaps `expr_syntax` and `expr_id`?
|
||||
impl BodySourceMap {
|
||||
pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
|
||||
match id {
|
||||
ExprOrPatId::ExprId(id) => self.expr_syntax(id).map(|it| it.map(AstPtr::wrap_left)),
|
||||
ExprOrPatId::PatId(id) => self.pat_syntax(id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
|
||||
self.expr_map_back.get(expr).cloned().ok_or(SyntheticSyntax)
|
||||
}
|
||||
|
||||
pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
|
||||
pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprOrPatId> {
|
||||
let src = node.map(AstPtr::new);
|
||||
self.expr_map.get(&src).cloned()
|
||||
}
|
||||
|
@ -395,7 +547,7 @@ impl BodySourceMap {
|
|||
self.expansions.iter().map(|(&a, &b)| (a, b))
|
||||
}
|
||||
|
||||
pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
|
||||
pub fn pat_syntax(&self, pat: PatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
|
||||
self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
|
||||
}
|
||||
|
||||
|
@ -428,7 +580,7 @@ impl BodySourceMap {
|
|||
self.pat_field_map_back[&pat]
|
||||
}
|
||||
|
||||
pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprId> {
|
||||
pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprOrPatId> {
|
||||
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::MacroExpr>).map(AstPtr::upcast);
|
||||
self.expr_map.get(&src).copied()
|
||||
}
|
||||
|
@ -444,7 +596,11 @@ impl BodySourceMap {
|
|||
node: InFile<&ast::FormatArgsExpr>,
|
||||
) -> Option<&[(syntax::TextRange, Name)]> {
|
||||
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
|
||||
self.template_map.as_ref()?.0.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref)
|
||||
self.template_map
|
||||
.as_ref()?
|
||||
.0
|
||||
.get(&self.expr_map.get(&src)?.as_expr()?)
|
||||
.map(std::ops::Deref::deref)
|
||||
}
|
||||
|
||||
pub fn asm_template_args(
|
||||
|
@ -452,8 +608,8 @@ impl BodySourceMap {
|
|||
node: InFile<&ast::AsmExpr>,
|
||||
) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> {
|
||||
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
|
||||
let expr = self.expr_map.get(&src)?;
|
||||
Some(*expr).zip(self.template_map.as_ref()?.1.get(expr).map(std::ops::Deref::deref))
|
||||
let expr = self.expr_map.get(&src)?.as_expr()?;
|
||||
Some(expr).zip(self.template_map.as_ref()?.1.get(&expr).map(std::ops::Deref::deref))
|
||||
}
|
||||
|
||||
/// Get a reference to the body source map's diagnostics.
|
||||
|
|
|
@ -70,7 +70,6 @@ pub(super) fn lower(
|
|||
body: Body::default(),
|
||||
expander,
|
||||
current_try_block_label: None,
|
||||
is_lowering_assignee_expr: false,
|
||||
is_lowering_coroutine: false,
|
||||
label_ribs: Vec::new(),
|
||||
current_binding_owner: None,
|
||||
|
@ -89,7 +88,6 @@ struct ExprCollector<'a> {
|
|||
body: Body,
|
||||
source_map: BodySourceMap,
|
||||
|
||||
is_lowering_assignee_expr: bool,
|
||||
is_lowering_coroutine: bool,
|
||||
|
||||
current_try_block_label: Option<LabelId>,
|
||||
|
@ -359,14 +357,7 @@ impl ExprCollector<'_> {
|
|||
} else {
|
||||
Box::default()
|
||||
};
|
||||
self.alloc_expr(
|
||||
Expr::Call {
|
||||
callee,
|
||||
args,
|
||||
is_assignee_expr: self.is_lowering_assignee_expr,
|
||||
},
|
||||
syntax_ptr,
|
||||
)
|
||||
self.alloc_expr(Expr::Call { callee, args }, syntax_ptr)
|
||||
}
|
||||
}
|
||||
ast::Expr::MethodCallExpr(e) => {
|
||||
|
@ -433,7 +424,7 @@ impl ExprCollector<'_> {
|
|||
let inner = self.collect_expr_opt(e.expr());
|
||||
// make the paren expr point to the inner expression as well for IDE resolution
|
||||
let src = self.expander.in_file(syntax_ptr);
|
||||
self.source_map.expr_map.insert(src, inner);
|
||||
self.source_map.expr_map.insert(src, inner.into());
|
||||
inner
|
||||
}
|
||||
ast::Expr::ReturnExpr(e) => {
|
||||
|
@ -457,7 +448,6 @@ impl ExprCollector<'_> {
|
|||
ast::Expr::RecordExpr(e) => {
|
||||
let path =
|
||||
e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
|
||||
let is_assignee_expr = self.is_lowering_assignee_expr;
|
||||
let record_lit = if let Some(nfl) = e.record_expr_field_list() {
|
||||
let fields = nfl
|
||||
.fields()
|
||||
|
@ -476,16 +466,9 @@ impl ExprCollector<'_> {
|
|||
})
|
||||
.collect();
|
||||
let spread = nfl.spread().map(|s| self.collect_expr(s));
|
||||
let ellipsis = nfl.dotdot_token().is_some();
|
||||
Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr }
|
||||
Expr::RecordLit { path, fields, spread }
|
||||
} else {
|
||||
Expr::RecordLit {
|
||||
path,
|
||||
fields: Box::default(),
|
||||
spread: None,
|
||||
ellipsis: false,
|
||||
is_assignee_expr,
|
||||
}
|
||||
Expr::RecordLit { path, fields: Box::default(), spread: None }
|
||||
};
|
||||
|
||||
self.alloc_expr(record_lit, syntax_ptr)
|
||||
|
@ -602,13 +585,15 @@ impl ExprCollector<'_> {
|
|||
ast::Expr::BinExpr(e) => {
|
||||
let op = e.op_kind();
|
||||
if let Some(ast::BinaryOp::Assignment { op: None }) = op {
|
||||
self.is_lowering_assignee_expr = true;
|
||||
}
|
||||
let target = self.collect_expr_as_pat_opt(e.lhs());
|
||||
let value = self.collect_expr_opt(e.rhs());
|
||||
self.alloc_expr(Expr::Assignment { target, value }, syntax_ptr)
|
||||
} else {
|
||||
let lhs = self.collect_expr_opt(e.lhs());
|
||||
self.is_lowering_assignee_expr = false;
|
||||
let rhs = self.collect_expr_opt(e.rhs());
|
||||
self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
|
||||
}
|
||||
}
|
||||
ast::Expr::TupleExpr(e) => {
|
||||
let mut exprs: Vec<_> = e.fields().map(|expr| self.collect_expr(expr)).collect();
|
||||
// if there is a leading comma, the user is most likely to type out a leading expression
|
||||
|
@ -617,13 +602,7 @@ impl ExprCollector<'_> {
|
|||
exprs.insert(0, self.missing_expr());
|
||||
}
|
||||
|
||||
self.alloc_expr(
|
||||
Expr::Tuple {
|
||||
exprs: exprs.into_boxed_slice(),
|
||||
is_assignee_expr: self.is_lowering_assignee_expr,
|
||||
},
|
||||
syntax_ptr,
|
||||
)
|
||||
self.alloc_expr(Expr::Tuple { exprs: exprs.into_boxed_slice() }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::ArrayExpr(e) => {
|
||||
let kind = e.kind();
|
||||
|
@ -631,13 +610,7 @@ impl ExprCollector<'_> {
|
|||
match kind {
|
||||
ArrayExprKind::ElementList(e) => {
|
||||
let elements = e.map(|expr| self.collect_expr(expr)).collect();
|
||||
self.alloc_expr(
|
||||
Expr::Array(Array::ElementList {
|
||||
elements,
|
||||
is_assignee_expr: self.is_lowering_assignee_expr,
|
||||
}),
|
||||
syntax_ptr,
|
||||
)
|
||||
self.alloc_expr(Expr::Array(Array::ElementList { elements }), syntax_ptr)
|
||||
}
|
||||
ArrayExprKind::Repeat { initializer, repeat } => {
|
||||
let initializer = self.collect_expr_opt(initializer);
|
||||
|
@ -664,8 +637,7 @@ impl ExprCollector<'_> {
|
|||
ast::Expr::IndexExpr(e) => {
|
||||
let base = self.collect_expr_opt(e.base());
|
||||
let index = self.collect_expr_opt(e.index());
|
||||
let is_assignee_expr = self.is_lowering_assignee_expr;
|
||||
self.alloc_expr(Expr::Index { base, index, is_assignee_expr }, syntax_ptr)
|
||||
self.alloc_expr(Expr::Index { base, index }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::RangeExpr(e) => {
|
||||
let lhs = e.start().map(|lhs| self.collect_expr(lhs));
|
||||
|
@ -688,7 +660,7 @@ impl ExprCollector<'_> {
|
|||
// Make the macro-call point to its expanded expression so we can query
|
||||
// semantics on syntax pointers to the macro
|
||||
let src = self.expander.in_file(syntax_ptr);
|
||||
self.source_map.expr_map.insert(src, id);
|
||||
self.source_map.expr_map.insert(src, id.into());
|
||||
id
|
||||
}
|
||||
None => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
|
@ -705,6 +677,179 @@ impl ExprCollector<'_> {
|
|||
})
|
||||
}
|
||||
|
||||
fn collect_expr_as_pat_opt(&mut self, expr: Option<ast::Expr>) -> PatId {
|
||||
match expr {
|
||||
Some(expr) => self.collect_expr_as_pat(expr),
|
||||
_ => self.missing_pat(),
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_expr_as_pat(&mut self, expr: ast::Expr) -> PatId {
|
||||
self.maybe_collect_expr_as_pat(&expr).unwrap_or_else(|| {
|
||||
let src = self.expander.in_file(AstPtr::new(&expr).wrap_left());
|
||||
let expr = self.collect_expr(expr);
|
||||
// Do not use `alloc_pat_from_expr()` here, it will override the entry in `expr_map`.
|
||||
let id = self.body.pats.alloc(Pat::Expr(expr));
|
||||
self.source_map.pat_map_back.insert(id, src);
|
||||
id
|
||||
})
|
||||
}
|
||||
|
||||
fn maybe_collect_expr_as_pat(&mut self, expr: &ast::Expr) -> Option<PatId> {
|
||||
self.check_cfg(expr)?;
|
||||
let syntax_ptr = AstPtr::new(expr);
|
||||
|
||||
let result = match expr {
|
||||
ast::Expr::UnderscoreExpr(_) => self.alloc_pat_from_expr(Pat::Wild, syntax_ptr),
|
||||
ast::Expr::ParenExpr(e) => {
|
||||
// We special-case `(..)` for consistency with patterns.
|
||||
if let Some(ast::Expr::RangeExpr(range)) = e.expr() {
|
||||
if range.is_range_full() {
|
||||
return Some(self.alloc_pat_from_expr(
|
||||
Pat::Tuple { args: Box::default(), ellipsis: Some(0) },
|
||||
syntax_ptr,
|
||||
));
|
||||
}
|
||||
}
|
||||
return e.expr().and_then(|expr| self.maybe_collect_expr_as_pat(&expr));
|
||||
}
|
||||
ast::Expr::TupleExpr(e) => {
|
||||
let (ellipsis, args) = collect_tuple(self, e.fields());
|
||||
self.alloc_pat_from_expr(Pat::Tuple { args, ellipsis }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::ArrayExpr(e) => {
|
||||
if e.semicolon_token().is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut elements = e.exprs();
|
||||
let prefix = elements
|
||||
.by_ref()
|
||||
.map_while(|elem| collect_possibly_rest(self, elem).left())
|
||||
.collect();
|
||||
let suffix = elements.map(|elem| self.collect_expr_as_pat(elem)).collect();
|
||||
self.alloc_pat_from_expr(Pat::Slice { prefix, slice: None, suffix }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::CallExpr(e) => {
|
||||
let path = collect_path(self, e.expr()?)?;
|
||||
let path = path
|
||||
.path()
|
||||
.and_then(|path| self.expander.parse_path(self.db, path))
|
||||
.map(Box::new);
|
||||
let (ellipsis, args) = collect_tuple(self, e.arg_list()?.args());
|
||||
self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::PathExpr(e) => {
|
||||
let path = Box::new(self.expander.parse_path(self.db, e.path()?)?);
|
||||
self.alloc_pat_from_expr(Pat::Path(path), syntax_ptr)
|
||||
}
|
||||
ast::Expr::MacroExpr(e) => {
|
||||
let e = e.macro_call()?;
|
||||
let macro_ptr = AstPtr::new(&e);
|
||||
let src = self.expander.in_file(AstPtr::new(expr));
|
||||
let id = self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
|
||||
this.collect_expr_as_pat_opt(expansion)
|
||||
});
|
||||
self.source_map.expr_map.insert(src, id.into());
|
||||
id
|
||||
}
|
||||
ast::Expr::RecordExpr(e) => {
|
||||
let path =
|
||||
e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
|
||||
let record_field_list = e.record_expr_field_list()?;
|
||||
let ellipsis = record_field_list.dotdot_token().is_some();
|
||||
// FIXME: Report an error here if `record_field_list.spread().is_some()`.
|
||||
let args = record_field_list
|
||||
.fields()
|
||||
.filter_map(|f| {
|
||||
self.check_cfg(&f)?;
|
||||
let field_expr = f.expr()?;
|
||||
let pat = self.collect_expr_as_pat(field_expr);
|
||||
let name = f.field_name()?.as_name();
|
||||
let src = self.expander.in_file(AstPtr::new(&f).wrap_left());
|
||||
self.source_map.pat_field_map_back.insert(pat, src);
|
||||
Some(RecordFieldPat { name, pat })
|
||||
})
|
||||
.collect();
|
||||
self.alloc_pat_from_expr(Pat::Record { path, args, ellipsis }, syntax_ptr)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
return Some(result);
|
||||
|
||||
fn collect_path(this: &mut ExprCollector<'_>, expr: ast::Expr) -> Option<ast::PathExpr> {
|
||||
match expr {
|
||||
ast::Expr::PathExpr(e) => Some(e),
|
||||
ast::Expr::MacroExpr(mac) => {
|
||||
let call = mac.macro_call()?;
|
||||
{
|
||||
let macro_ptr = AstPtr::new(&call);
|
||||
this.collect_macro_call(call, macro_ptr, true, |this, expanded_path| {
|
||||
collect_path(this, expanded_path?)
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_possibly_rest(
|
||||
this: &mut ExprCollector<'_>,
|
||||
expr: ast::Expr,
|
||||
) -> Either<PatId, ()> {
|
||||
match &expr {
|
||||
ast::Expr::RangeExpr(e) if e.is_range_full() => Either::Right(()),
|
||||
ast::Expr::MacroExpr(mac) => match mac.macro_call() {
|
||||
Some(call) => {
|
||||
let macro_ptr = AstPtr::new(&call);
|
||||
let pat = this.collect_macro_call(
|
||||
call,
|
||||
macro_ptr,
|
||||
true,
|
||||
|this, expanded_expr| match expanded_expr {
|
||||
Some(expanded_pat) => collect_possibly_rest(this, expanded_pat),
|
||||
None => Either::Left(this.missing_pat()),
|
||||
},
|
||||
);
|
||||
if let Either::Left(pat) = pat {
|
||||
let src = this.expander.in_file(AstPtr::new(&expr).wrap_left());
|
||||
this.source_map.pat_map_back.insert(pat, src);
|
||||
}
|
||||
pat
|
||||
}
|
||||
None => {
|
||||
let ptr = AstPtr::new(&expr);
|
||||
Either::Left(this.alloc_pat_from_expr(Pat::Missing, ptr))
|
||||
}
|
||||
},
|
||||
_ => Either::Left(this.collect_expr_as_pat(expr)),
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_tuple(
|
||||
this: &mut ExprCollector<'_>,
|
||||
fields: ast::AstChildren<ast::Expr>,
|
||||
) -> (Option<u32>, Box<[la_arena::Idx<Pat>]>) {
|
||||
let mut ellipsis = None;
|
||||
let args = fields
|
||||
.enumerate()
|
||||
.filter_map(|(idx, elem)| {
|
||||
match collect_possibly_rest(this, elem) {
|
||||
Either::Left(pat) => Some(pat),
|
||||
Either::Right(()) => {
|
||||
if ellipsis.is_none() {
|
||||
ellipsis = Some(idx as u32);
|
||||
}
|
||||
// FIXME: Report an error here otherwise.
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
(ellipsis, args)
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_binding_owner(
|
||||
&mut self,
|
||||
syntax_ptr: AstPtr<ast::Expr>,
|
||||
|
@ -755,17 +900,13 @@ impl ExprCollector<'_> {
|
|||
|
||||
let callee = self.alloc_expr_desugared_with_ptr(Expr::Path(try_from_output), ptr);
|
||||
let next_tail = match btail {
|
||||
Some(tail) => self.alloc_expr_desugared_with_ptr(
|
||||
Expr::Call { callee, args: Box::new([tail]), is_assignee_expr: false },
|
||||
ptr,
|
||||
),
|
||||
Some(tail) => self
|
||||
.alloc_expr_desugared_with_ptr(Expr::Call { callee, args: Box::new([tail]) }, ptr),
|
||||
None => {
|
||||
let unit = self.alloc_expr_desugared_with_ptr(
|
||||
Expr::Tuple { exprs: Box::new([]), is_assignee_expr: false },
|
||||
ptr,
|
||||
);
|
||||
let unit =
|
||||
self.alloc_expr_desugared_with_ptr(Expr::Tuple { exprs: Box::new([]) }, ptr);
|
||||
self.alloc_expr_desugared_with_ptr(
|
||||
Expr::Call { callee, args: Box::new([unit]), is_assignee_expr: false },
|
||||
Expr::Call { callee, args: Box::new([unit]) },
|
||||
ptr,
|
||||
)
|
||||
}
|
||||
|
@ -851,11 +992,7 @@ impl ExprCollector<'_> {
|
|||
let head = self.collect_expr_opt(e.iterable());
|
||||
let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr);
|
||||
let iterator = self.alloc_expr(
|
||||
Expr::Call {
|
||||
callee: into_iter_fn_expr,
|
||||
args: Box::new([head]),
|
||||
is_assignee_expr: false,
|
||||
},
|
||||
Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) },
|
||||
syntax_ptr,
|
||||
);
|
||||
let none_arm = MatchArm {
|
||||
|
@ -884,11 +1021,7 @@ impl ExprCollector<'_> {
|
|||
);
|
||||
let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr);
|
||||
let iter_next_expr = self.alloc_expr(
|
||||
Expr::Call {
|
||||
callee: iter_next_fn_expr,
|
||||
args: Box::new([iter_expr_mut]),
|
||||
is_assignee_expr: false,
|
||||
},
|
||||
Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) },
|
||||
syntax_ptr,
|
||||
);
|
||||
let loop_inner = self.alloc_expr(
|
||||
|
@ -942,10 +1075,8 @@ impl ExprCollector<'_> {
|
|||
};
|
||||
let operand = self.collect_expr_opt(e.expr());
|
||||
let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr);
|
||||
let expr = self.alloc_expr(
|
||||
Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false },
|
||||
syntax_ptr,
|
||||
);
|
||||
let expr = self
|
||||
.alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr);
|
||||
let continue_name = Name::generate_new_name(self.body.bindings.len());
|
||||
let continue_binding =
|
||||
self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated);
|
||||
|
@ -975,10 +1106,8 @@ impl ExprCollector<'_> {
|
|||
expr: {
|
||||
let it = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr);
|
||||
let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr);
|
||||
let result = self.alloc_expr(
|
||||
Expr::Call { callee, args: Box::new([it]), is_assignee_expr: false },
|
||||
syntax_ptr,
|
||||
);
|
||||
let result =
|
||||
self.alloc_expr(Expr::Call { callee, args: Box::new([it]) }, syntax_ptr);
|
||||
self.alloc_expr(
|
||||
match self.current_try_block_label {
|
||||
Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
|
||||
|
@ -1108,7 +1237,7 @@ impl ExprCollector<'_> {
|
|||
// Make the macro-call point to its expanded expression so we can query
|
||||
// semantics on syntax pointers to the macro
|
||||
let src = self.expander.in_file(syntax_ptr);
|
||||
self.source_map.expr_map.insert(src, tail);
|
||||
self.source_map.expr_map.insert(src, tail.into());
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1372,7 +1501,7 @@ impl ExprCollector<'_> {
|
|||
let ast_pat = f.pat()?;
|
||||
let pat = self.collect_pat(ast_pat, binding_list);
|
||||
let name = f.field_name()?.as_name();
|
||||
let src = self.expander.in_file(AstPtr::new(&f));
|
||||
let src = self.expander.in_file(AstPtr::new(&f).wrap_right());
|
||||
self.source_map.pat_field_map_back.insert(pat, src);
|
||||
Some(RecordFieldPat { name, pat })
|
||||
})
|
||||
|
@ -1723,10 +1852,8 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
})
|
||||
.collect();
|
||||
let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements: lit_pieces,
|
||||
is_assignee_expr: false,
|
||||
}));
|
||||
let lit_pieces =
|
||||
self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: lit_pieces }));
|
||||
let lit_pieces = self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: lit_pieces,
|
||||
rawness: Rawness::Ref,
|
||||
|
@ -1743,10 +1870,7 @@ impl ExprCollector<'_> {
|
|||
Some(self.make_format_spec(placeholder, &mut argmap))
|
||||
})
|
||||
.collect();
|
||||
let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements,
|
||||
is_assignee_expr: false,
|
||||
}));
|
||||
let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements }));
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: array,
|
||||
rawness: Rawness::Ref,
|
||||
|
@ -1756,10 +1880,8 @@ impl ExprCollector<'_> {
|
|||
let arguments = &*fmt.arguments.arguments;
|
||||
|
||||
let args = if arguments.is_empty() {
|
||||
let expr = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements: Box::default(),
|
||||
is_assignee_expr: false,
|
||||
}));
|
||||
let expr = self
|
||||
.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: Box::default() }));
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr,
|
||||
rawness: Rawness::Ref,
|
||||
|
@ -1786,10 +1908,8 @@ impl ExprCollector<'_> {
|
|||
self.make_argument(arg, ty)
|
||||
})
|
||||
.collect();
|
||||
let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements: args,
|
||||
is_assignee_expr: false,
|
||||
}));
|
||||
let array =
|
||||
self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: array,
|
||||
rawness: Rawness::Ref,
|
||||
|
@ -1822,11 +1942,8 @@ impl ExprCollector<'_> {
|
|||
let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted));
|
||||
|
||||
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new));
|
||||
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call {
|
||||
callee: unsafe_arg_new,
|
||||
args: Box::default(),
|
||||
is_assignee_expr: false,
|
||||
});
|
||||
let unsafe_arg_new =
|
||||
self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() });
|
||||
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
|
||||
id: None,
|
||||
// We collect the unused expressions here so that we still infer them instead of
|
||||
|
@ -1843,7 +1960,6 @@ impl ExprCollector<'_> {
|
|||
Expr::Call {
|
||||
callee: new_v1_formatted,
|
||||
args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
|
||||
is_assignee_expr: false,
|
||||
},
|
||||
syntax_ptr,
|
||||
);
|
||||
|
@ -1938,7 +2054,6 @@ impl ExprCollector<'_> {
|
|||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: format_placeholder_new,
|
||||
args: Box::new([position, fill, align, flags, precision, width]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1980,11 +2095,7 @@ impl ExprCollector<'_> {
|
|||
Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)),
|
||||
None => self.missing_expr(),
|
||||
};
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: count_is,
|
||||
args: Box::new([args]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]) })
|
||||
}
|
||||
Some(FormatCount::Argument(arg)) => {
|
||||
if let Ok(arg_index) = arg.index {
|
||||
|
@ -2005,7 +2116,6 @@ impl ExprCollector<'_> {
|
|||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: count_param,
|
||||
args: Box::new([args]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
} else {
|
||||
// FIXME: This drops arg causing it to potentially not be resolved/type checked
|
||||
|
@ -2054,11 +2164,7 @@ impl ExprCollector<'_> {
|
|||
Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)),
|
||||
None => self.missing_expr(),
|
||||
};
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: new_fn,
|
||||
args: Box::new([arg]),
|
||||
is_assignee_expr: false,
|
||||
})
|
||||
self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) })
|
||||
}
|
||||
|
||||
// endregion: format
|
||||
|
@ -2082,7 +2188,7 @@ impl ExprCollector<'_> {
|
|||
let src = self.expander.in_file(ptr);
|
||||
let id = self.body.exprs.alloc(expr);
|
||||
self.source_map.expr_map_back.insert(id, src);
|
||||
self.source_map.expr_map.insert(src, id);
|
||||
self.source_map.expr_map.insert(src, id.into());
|
||||
id
|
||||
}
|
||||
// FIXME: desugared exprs don't have ptr, that's wrong and should be fixed.
|
||||
|
@ -2110,10 +2216,17 @@ impl ExprCollector<'_> {
|
|||
binding
|
||||
}
|
||||
|
||||
fn alloc_pat_from_expr(&mut self, pat: Pat, ptr: ExprPtr) -> PatId {
|
||||
let src = self.expander.in_file(ptr);
|
||||
let id = self.body.pats.alloc(pat);
|
||||
self.source_map.expr_map.insert(src, id.into());
|
||||
self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
|
||||
id
|
||||
}
|
||||
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
|
||||
let src = self.expander.in_file(ptr);
|
||||
let id = self.body.pats.alloc(pat);
|
||||
self.source_map.pat_map_back.insert(id, src);
|
||||
self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
|
||||
self.source_map.pat_map.insert(src, id);
|
||||
id
|
||||
}
|
||||
|
|
|
@ -277,7 +277,7 @@ impl Printer<'_> {
|
|||
w!(self, "loop ");
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Call { callee, args, is_assignee_expr: _ } => {
|
||||
Expr::Call { callee, args } => {
|
||||
self.print_expr(*callee);
|
||||
w!(self, "(");
|
||||
if !args.is_empty() {
|
||||
|
@ -372,7 +372,7 @@ impl Printer<'_> {
|
|||
self.print_expr(*expr);
|
||||
}
|
||||
}
|
||||
Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr: _ } => {
|
||||
Expr::RecordLit { path, fields, spread } => {
|
||||
match path {
|
||||
Some(path) => self.print_path(path),
|
||||
None => w!(self, "<EFBFBD>"),
|
||||
|
@ -391,9 +391,6 @@ impl Printer<'_> {
|
|||
p.print_expr(*spread);
|
||||
wln!(p);
|
||||
}
|
||||
if *ellipsis {
|
||||
wln!(p, "..");
|
||||
}
|
||||
});
|
||||
w!(self, "}}");
|
||||
}
|
||||
|
@ -466,7 +463,7 @@ impl Printer<'_> {
|
|||
w!(self, ") ");
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index, is_assignee_expr: _ } => {
|
||||
Expr::Index { base, index } => {
|
||||
self.print_expr(*base);
|
||||
w!(self, "[");
|
||||
self.print_expr(*index);
|
||||
|
@ -507,7 +504,7 @@ impl Printer<'_> {
|
|||
self.whitespace();
|
||||
self.print_expr(*body);
|
||||
}
|
||||
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||
Expr::Tuple { exprs } => {
|
||||
w!(self, "(");
|
||||
for expr in exprs.iter() {
|
||||
self.print_expr(*expr);
|
||||
|
@ -519,7 +516,7 @@ impl Printer<'_> {
|
|||
w!(self, "[");
|
||||
if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) {
|
||||
self.indented(|p| match arr {
|
||||
Array::ElementList { elements, is_assignee_expr: _ } => {
|
||||
Array::ElementList { elements } => {
|
||||
for elem in elements.iter() {
|
||||
p.print_expr(*elem);
|
||||
w!(p, ", ");
|
||||
|
@ -551,6 +548,11 @@ impl Printer<'_> {
|
|||
Expr::Const(id) => {
|
||||
w!(self, "const {{ /* {id:?} */ }}");
|
||||
}
|
||||
&Expr::Assignment { target, value } => {
|
||||
self.print_pat(target);
|
||||
w!(self, " = ");
|
||||
self.print_expr(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -719,6 +721,9 @@ impl Printer<'_> {
|
|||
w!(self, "const ");
|
||||
self.print_expr(*c);
|
||||
}
|
||||
Pat::Expr(expr) => {
|
||||
self.print_expr(*expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -282,7 +282,7 @@ fn compute_expr_scopes(
|
|||
*scope = scopes.new_scope(*scope);
|
||||
scopes.add_pat_bindings(body, *scope, pat);
|
||||
}
|
||||
e => e.walk_child_exprs(|e| compute_expr_scopes(scopes, e, scope)),
|
||||
_ => body.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -333,6 +333,8 @@ mod tests {
|
|||
|
||||
let expr_id = source_map
|
||||
.node_expr(InFile { file_id: file_id.into(), value: &marker.into() })
|
||||
.unwrap()
|
||||
.as_expr()
|
||||
.unwrap();
|
||||
let scope = scopes.scope_for(expr_id);
|
||||
|
||||
|
@ -488,8 +490,11 @@ fn foo() {
|
|||
|
||||
let expr_scope = {
|
||||
let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap();
|
||||
let expr_id =
|
||||
source_map.node_expr(InFile { file_id: file_id.into(), value: &expr_ast }).unwrap();
|
||||
let expr_id = source_map
|
||||
.node_expr(InFile { file_id: file_id.into(), value: &expr_ast })
|
||||
.unwrap()
|
||||
.as_expr()
|
||||
.unwrap();
|
||||
scopes.scope_for(expr_id).unwrap()
|
||||
};
|
||||
|
||||
|
|
|
@ -370,3 +370,37 @@ fn f(a: i32, b: u32) -> String {
|
|||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assignment_tuple_macro() {
|
||||
// This is a funny one. `let m!()() = Bar()` is an error in rustc, because `m!()()` isn't a valid pattern,
|
||||
// but in destructuring assignment it is valid, because `m!()()` is a valid expression, and destructuring
|
||||
// assignments start their lives as expressions. So we have to do the same.
|
||||
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
struct Bar();
|
||||
|
||||
macro_rules! m {
|
||||
() => { Bar };
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
m!()() = Bar();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
let (_, source_map) = db.body_with_source_map(def);
|
||||
assert_eq!(source_map.diagnostics(), &[]);
|
||||
|
||||
for (_, def_map) in body.blocks(&db) {
|
||||
assert_eq!(def_map.diagnostics(), &[]);
|
||||
}
|
||||
|
||||
expect![[r#"
|
||||
fn foo() -> () {
|
||||
Bar() = Bar();
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
|
|
@ -48,6 +48,22 @@ pub enum ExprOrPatId {
|
|||
ExprId(ExprId),
|
||||
PatId(PatId),
|
||||
}
|
||||
|
||||
impl ExprOrPatId {
|
||||
pub fn as_expr(self) -> Option<ExprId> {
|
||||
match self {
|
||||
Self::ExprId(v) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_pat(self) -> Option<PatId> {
|
||||
match self {
|
||||
Self::PatId(v) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
stdx::impl_from!(ExprId, PatId for ExprOrPatId);
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
@ -204,7 +220,6 @@ pub enum Expr {
|
|||
Call {
|
||||
callee: ExprId,
|
||||
args: Box<[ExprId]>,
|
||||
is_assignee_expr: bool,
|
||||
},
|
||||
MethodCall {
|
||||
receiver: ExprId,
|
||||
|
@ -239,8 +254,6 @@ pub enum Expr {
|
|||
path: Option<Box<Path>>,
|
||||
fields: Box<[RecordLitField]>,
|
||||
spread: Option<ExprId>,
|
||||
ellipsis: bool,
|
||||
is_assignee_expr: bool,
|
||||
},
|
||||
Field {
|
||||
expr: ExprId,
|
||||
|
@ -265,11 +278,17 @@ pub enum Expr {
|
|||
expr: ExprId,
|
||||
op: UnaryOp,
|
||||
},
|
||||
/// `op` cannot be bare `=` (but can be `op=`), these are lowered to `Assignment` instead.
|
||||
BinaryOp {
|
||||
lhs: ExprId,
|
||||
rhs: ExprId,
|
||||
op: Option<BinaryOp>,
|
||||
},
|
||||
// Assignments need a special treatment because of destructuring assignment.
|
||||
Assignment {
|
||||
target: PatId,
|
||||
value: ExprId,
|
||||
},
|
||||
Range {
|
||||
lhs: Option<ExprId>,
|
||||
rhs: Option<ExprId>,
|
||||
|
@ -278,7 +297,6 @@ pub enum Expr {
|
|||
Index {
|
||||
base: ExprId,
|
||||
index: ExprId,
|
||||
is_assignee_expr: bool,
|
||||
},
|
||||
Closure {
|
||||
args: Box<[PatId]>,
|
||||
|
@ -290,7 +308,6 @@ pub enum Expr {
|
|||
},
|
||||
Tuple {
|
||||
exprs: Box<[ExprId]>,
|
||||
is_assignee_expr: bool,
|
||||
},
|
||||
Array(Array),
|
||||
Literal(Literal),
|
||||
|
@ -446,7 +463,7 @@ pub enum Movability {
|
|||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Array {
|
||||
ElementList { elements: Box<[ExprId]>, is_assignee_expr: bool },
|
||||
ElementList { elements: Box<[ExprId]> },
|
||||
Repeat { initializer: ExprId, repeat: ExprId },
|
||||
}
|
||||
|
||||
|
@ -480,130 +497,6 @@ pub enum Statement {
|
|||
Item,
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
|
||||
match self {
|
||||
Expr::Missing => {}
|
||||
Expr::Path(_) | Expr::OffsetOf(_) => {}
|
||||
Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
|
||||
AsmOperand::In { expr, .. }
|
||||
| AsmOperand::Out { expr: Some(expr), .. }
|
||||
| AsmOperand::InOut { expr, .. } => f(*expr),
|
||||
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
f(*in_expr);
|
||||
if let Some(out_expr) = out_expr {
|
||||
f(*out_expr);
|
||||
}
|
||||
}
|
||||
AsmOperand::Out { expr: None, .. }
|
||||
| AsmOperand::Const(_)
|
||||
| AsmOperand::Label(_)
|
||||
| AsmOperand::Sym(_) => (),
|
||||
}),
|
||||
Expr::If { condition, then_branch, else_branch } => {
|
||||
f(*condition);
|
||||
f(*then_branch);
|
||||
if let &Some(else_branch) = else_branch {
|
||||
f(else_branch);
|
||||
}
|
||||
}
|
||||
Expr::Let { expr, .. } => {
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Const(_) => (),
|
||||
Expr::Block { statements, tail, .. }
|
||||
| Expr::Unsafe { statements, tail, .. }
|
||||
| Expr::Async { statements, tail, .. } => {
|
||||
for stmt in statements.iter() {
|
||||
match stmt {
|
||||
Statement::Let { initializer, else_branch, .. } => {
|
||||
if let &Some(expr) = initializer {
|
||||
f(expr);
|
||||
}
|
||||
if let &Some(expr) = else_branch {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Statement::Expr { expr: expression, .. } => f(*expression),
|
||||
Statement::Item => (),
|
||||
}
|
||||
}
|
||||
if let &Some(expr) = tail {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Loop { body, .. } => f(*body),
|
||||
Expr::Call { callee, args, .. } => {
|
||||
f(*callee);
|
||||
args.iter().copied().for_each(f);
|
||||
}
|
||||
Expr::MethodCall { receiver, args, .. } => {
|
||||
f(*receiver);
|
||||
args.iter().copied().for_each(f);
|
||||
}
|
||||
Expr::Match { expr, arms } => {
|
||||
f(*expr);
|
||||
arms.iter().map(|arm| arm.expr).for_each(f);
|
||||
}
|
||||
Expr::Continue { .. } => {}
|
||||
Expr::Break { expr, .. }
|
||||
| Expr::Return { expr }
|
||||
| Expr::Yield { expr }
|
||||
| Expr::Yeet { expr } => {
|
||||
if let &Some(expr) = expr {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Become { expr } => f(*expr),
|
||||
Expr::RecordLit { fields, spread, .. } => {
|
||||
for field in fields.iter() {
|
||||
f(field.expr);
|
||||
}
|
||||
if let &Some(expr) = spread {
|
||||
f(expr);
|
||||
}
|
||||
}
|
||||
Expr::Closure { body, .. } => {
|
||||
f(*body);
|
||||
}
|
||||
Expr::BinaryOp { lhs, rhs, .. } => {
|
||||
f(*lhs);
|
||||
f(*rhs);
|
||||
}
|
||||
Expr::Range { lhs, rhs, .. } => {
|
||||
if let &Some(lhs) = rhs {
|
||||
f(lhs);
|
||||
}
|
||||
if let &Some(rhs) = lhs {
|
||||
f(rhs);
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index, .. } => {
|
||||
f(*base);
|
||||
f(*index);
|
||||
}
|
||||
Expr::Field { expr, .. }
|
||||
| Expr::Await { expr }
|
||||
| Expr::Cast { expr, .. }
|
||||
| Expr::Ref { expr, .. }
|
||||
| Expr::UnaryOp { expr, .. }
|
||||
| Expr::Box { expr } => {
|
||||
f(*expr);
|
||||
}
|
||||
Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
|
||||
Expr::Array(a) => match a {
|
||||
Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
|
||||
Array::Repeat { initializer, repeat } => {
|
||||
f(*initializer);
|
||||
f(*repeat)
|
||||
}
|
||||
},
|
||||
Expr::Literal(_) => {}
|
||||
Expr::Underscore => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Explicit binding annotations given in the HIR for a binding. Note
|
||||
/// that this is not the final binding *mode* that we infer after type
|
||||
/// inference.
|
||||
|
@ -665,18 +558,49 @@ pub struct RecordFieldPat {
|
|||
pub enum Pat {
|
||||
Missing,
|
||||
Wild,
|
||||
Tuple { args: Box<[PatId]>, ellipsis: Option<u32> },
|
||||
Tuple {
|
||||
args: Box<[PatId]>,
|
||||
ellipsis: Option<u32>,
|
||||
},
|
||||
Or(Box<[PatId]>),
|
||||
Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool },
|
||||
Range { start: Option<Box<LiteralOrConst>>, end: Option<Box<LiteralOrConst>> },
|
||||
Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
|
||||
Record {
|
||||
path: Option<Box<Path>>,
|
||||
args: Box<[RecordFieldPat]>,
|
||||
ellipsis: bool,
|
||||
},
|
||||
Range {
|
||||
start: Option<Box<LiteralOrConst>>,
|
||||
end: Option<Box<LiteralOrConst>>,
|
||||
},
|
||||
Slice {
|
||||
prefix: Box<[PatId]>,
|
||||
slice: Option<PatId>,
|
||||
suffix: Box<[PatId]>,
|
||||
},
|
||||
/// This might refer to a variable if a single segment path (specifically, on destructuring assignment).
|
||||
Path(Box<Path>),
|
||||
Lit(ExprId),
|
||||
Bind { id: BindingId, subpat: Option<PatId> },
|
||||
TupleStruct { path: Option<Box<Path>>, args: Box<[PatId]>, ellipsis: Option<u32> },
|
||||
Ref { pat: PatId, mutability: Mutability },
|
||||
Box { inner: PatId },
|
||||
Bind {
|
||||
id: BindingId,
|
||||
subpat: Option<PatId>,
|
||||
},
|
||||
TupleStruct {
|
||||
path: Option<Box<Path>>,
|
||||
args: Box<[PatId]>,
|
||||
ellipsis: Option<u32>,
|
||||
},
|
||||
Ref {
|
||||
pat: PatId,
|
||||
mutability: Mutability,
|
||||
},
|
||||
Box {
|
||||
inner: PatId,
|
||||
},
|
||||
ConstBlock(ExprId),
|
||||
/// An expression inside a pattern. That can only occur inside assignments.
|
||||
///
|
||||
/// E.g. in `(a, *b) = (1, &mut 2)`, `*b` is an expression.
|
||||
Expr(ExprId),
|
||||
}
|
||||
|
||||
impl Pat {
|
||||
|
@ -687,7 +611,8 @@ impl Pat {
|
|||
| Pat::Path(..)
|
||||
| Pat::ConstBlock(..)
|
||||
| Pat::Wild
|
||||
| Pat::Missing => {}
|
||||
| Pat::Missing
|
||||
| Pat::Expr(_) => {}
|
||||
Pat::Bind { subpat, .. } => {
|
||||
subpat.iter().copied().for_each(f);
|
||||
}
|
||||
|
|
|
@ -198,7 +198,10 @@ impl TestDB {
|
|||
.filter_map(|node| {
|
||||
let block = ast::BlockExpr::cast(node)?;
|
||||
let expr = ast::Expr::from(block);
|
||||
let expr_id = source_map.node_expr(InFile::new(position.file_id.into(), &expr))?;
|
||||
let expr_id = source_map
|
||||
.node_expr(InFile::new(position.file_id.into(), &expr))?
|
||||
.as_expr()
|
||||
.unwrap();
|
||||
let scope = scopes.scope_for(expr_id).unwrap();
|
||||
Some(scope)
|
||||
});
|
||||
|
|
|
@ -319,7 +319,7 @@ pub(crate) fn eval_to_const(
|
|||
return true;
|
||||
}
|
||||
let mut r = false;
|
||||
body[expr].walk_child_exprs(|idx| r |= has_closure(body, idx));
|
||||
body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx));
|
||||
r
|
||||
}
|
||||
if has_closure(ctx.body, expr) {
|
||||
|
|
|
@ -546,10 +546,7 @@ pub fn record_literal_missing_fields(
|
|||
expr: &Expr,
|
||||
) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> {
|
||||
let (fields, exhaustive) = match expr {
|
||||
Expr::RecordLit { fields, spread, ellipsis, is_assignee_expr, .. } => {
|
||||
let exhaustive = if *is_assignee_expr { !*ellipsis } else { spread.is_none() };
|
||||
(fields, exhaustive)
|
||||
}
|
||||
Expr::RecordLit { fields, spread, .. } => (fields, spread.is_none()),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use hir_def::{
|
||||
body::Body,
|
||||
hir::{Expr, ExprId, UnaryOp},
|
||||
hir::{Expr, ExprId, ExprOrPatId, Pat, UnaryOp},
|
||||
resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
|
||||
type_ref::Rawness,
|
||||
DefWithBodyId,
|
||||
|
@ -16,7 +16,7 @@ use crate::{
|
|||
/// Returns `(unsafe_exprs, fn_is_unsafe)`.
|
||||
///
|
||||
/// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`.
|
||||
pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> (Vec<ExprId>, bool) {
|
||||
pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> (Vec<ExprOrPatId>, bool) {
|
||||
let _p = tracing::info_span!("missing_unsafe").entered();
|
||||
|
||||
let mut res = Vec::new();
|
||||
|
@ -32,7 +32,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> (Vec<ExprId>,
|
|||
let infer = db.infer(def);
|
||||
unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| {
|
||||
if !expr.inside_unsafe_block {
|
||||
res.push(expr.expr);
|
||||
res.push(expr.node);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -40,7 +40,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> (Vec<ExprId>,
|
|||
}
|
||||
|
||||
pub struct UnsafeExpr {
|
||||
pub expr: ExprId,
|
||||
pub node: ExprOrPatId,
|
||||
pub inside_unsafe_block: bool,
|
||||
}
|
||||
|
||||
|
@ -75,26 +75,28 @@ fn walk_unsafe(
|
|||
inside_unsafe_block: bool,
|
||||
unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
|
||||
) {
|
||||
let expr = &body.exprs[current];
|
||||
match expr {
|
||||
&Expr::Call { callee, .. } => {
|
||||
if let Some(func) = infer[callee].as_fn_def(db) {
|
||||
if is_fn_unsafe_to_call(db, func) {
|
||||
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Path(path) => {
|
||||
let mut mark_unsafe_path = |path, node| {
|
||||
let g = resolver.update_to_inner_scope(db.upcast(), def, current);
|
||||
let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
|
||||
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
|
||||
let static_data = db.static_data(id);
|
||||
if static_data.mutable || (static_data.is_extern && !static_data.has_safe_kw) {
|
||||
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
|
||||
unsafe_expr_cb(UnsafeExpr { node, inside_unsafe_block });
|
||||
}
|
||||
}
|
||||
resolver.reset_to_guard(g);
|
||||
};
|
||||
|
||||
let expr = &body.exprs[current];
|
||||
match expr {
|
||||
&Expr::Call { callee, .. } => {
|
||||
if let Some(func) = infer[callee].as_fn_def(db) {
|
||||
if is_fn_unsafe_to_call(db, func) {
|
||||
unsafe_expr_cb(UnsafeExpr { node: current.into(), inside_unsafe_block });
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Path(path) => mark_unsafe_path(path, current.into()),
|
||||
Expr::Ref { expr, rawness: Rawness::RawPtr, mutability: _ } => {
|
||||
if let Expr::Path(_) = body.exprs[*expr] {
|
||||
// Do not report unsafe for `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)`,
|
||||
|
@ -108,23 +110,30 @@ fn walk_unsafe(
|
|||
.map(|(func, _)| is_fn_unsafe_to_call(db, func))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
|
||||
unsafe_expr_cb(UnsafeExpr { node: current.into(), inside_unsafe_block });
|
||||
}
|
||||
}
|
||||
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
|
||||
if let TyKind::Raw(..) = &infer[*expr].kind(Interner) {
|
||||
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
|
||||
unsafe_expr_cb(UnsafeExpr { node: current.into(), inside_unsafe_block });
|
||||
}
|
||||
}
|
||||
Expr::Unsafe { .. } => {
|
||||
return expr.walk_child_exprs(|child| {
|
||||
return body.walk_child_exprs(current, |child| {
|
||||
walk_unsafe(db, infer, body, resolver, def, child, true, unsafe_expr_cb);
|
||||
});
|
||||
}
|
||||
&Expr::Assignment { target, value: _ } => {
|
||||
body.walk_pats(target, &mut |pat| {
|
||||
if let Pat::Path(path) = &body[pat] {
|
||||
mark_unsafe_path(path, pat.into());
|
||||
}
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
expr.walk_child_exprs(|child| {
|
||||
body.walk_child_exprs(current, |child| {
|
||||
walk_unsafe(db, infer, body, resolver, def, child, inside_unsafe_block, unsafe_expr_cb);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -228,7 +228,7 @@ pub enum InferenceDiagnostic {
|
|||
id: ExprOrPatId,
|
||||
},
|
||||
UnresolvedIdent {
|
||||
expr: ExprId,
|
||||
id: ExprOrPatId,
|
||||
},
|
||||
// FIXME: This should be emitted in body lowering
|
||||
BreakOutsideOfLoop {
|
||||
|
@ -482,12 +482,27 @@ impl InferenceResult {
|
|||
pub fn variant_resolution_for_pat(&self, id: PatId) -> Option<VariantId> {
|
||||
self.variant_resolutions.get(&id.into()).copied()
|
||||
}
|
||||
pub fn variant_resolution_for_expr_or_pat(&self, id: ExprOrPatId) -> Option<VariantId> {
|
||||
match id {
|
||||
ExprOrPatId::ExprId(id) => self.variant_resolution_for_expr(id),
|
||||
ExprOrPatId::PatId(id) => self.variant_resolution_for_pat(id),
|
||||
}
|
||||
}
|
||||
pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<(AssocItemId, Substitution)> {
|
||||
self.assoc_resolutions.get(&id.into()).cloned()
|
||||
}
|
||||
pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<(AssocItemId, Substitution)> {
|
||||
self.assoc_resolutions.get(&id.into()).cloned()
|
||||
}
|
||||
pub fn assoc_resolutions_for_expr_or_pat(
|
||||
&self,
|
||||
id: ExprOrPatId,
|
||||
) -> Option<(AssocItemId, Substitution)> {
|
||||
match id {
|
||||
ExprOrPatId::ExprId(id) => self.assoc_resolutions_for_expr(id),
|
||||
ExprOrPatId::PatId(id) => self.assoc_resolutions_for_pat(id),
|
||||
}
|
||||
}
|
||||
pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
|
||||
self.type_mismatches.get(&expr.into())
|
||||
}
|
||||
|
@ -506,6 +521,12 @@ impl InferenceResult {
|
|||
pub fn closure_info(&self, closure: &ClosureId) -> &(Vec<CapturedItem>, FnTrait) {
|
||||
self.closure_info.get(closure).unwrap()
|
||||
}
|
||||
pub fn type_of_expr_or_pat(&self, id: ExprOrPatId) -> Option<&Ty> {
|
||||
match id {
|
||||
ExprOrPatId::ExprId(id) => self.type_of_expr.get(id),
|
||||
ExprOrPatId::PatId(id) => self.type_of_pat.get(id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<ExprId> for InferenceResult {
|
||||
|
@ -524,6 +545,14 @@ impl Index<PatId> for InferenceResult {
|
|||
}
|
||||
}
|
||||
|
||||
impl Index<ExprOrPatId> for InferenceResult {
|
||||
type Output = Ty;
|
||||
|
||||
fn index(&self, id: ExprOrPatId) -> &Ty {
|
||||
self.type_of_expr_or_pat(id).unwrap_or(&self.standard_types.unknown)
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<BindingId> for InferenceResult {
|
||||
type Output = Ty;
|
||||
|
||||
|
@ -561,6 +590,9 @@ pub(crate) struct InferenceContext<'a> {
|
|||
diverges: Diverges,
|
||||
breakables: Vec<BreakableContext>,
|
||||
|
||||
/// Whether we are inside the pattern of a destructuring assignment.
|
||||
inside_assignment: bool,
|
||||
|
||||
deferred_cast_checks: Vec<CastCheck>,
|
||||
|
||||
// fields related to closure capture
|
||||
|
@ -656,6 +688,7 @@ impl<'a> InferenceContext<'a> {
|
|||
current_closure: None,
|
||||
deferred_closures: FxHashMap::default(),
|
||||
closure_dependencies: FxHashMap::default(),
|
||||
inside_assignment: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,11 +11,12 @@ use either::Either;
|
|||
use hir_def::{
|
||||
data::adt::VariantData,
|
||||
hir::{
|
||||
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement,
|
||||
UnaryOp,
|
||||
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId,
|
||||
Statement, UnaryOp,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||
path::Path,
|
||||
resolver::ValueNs,
|
||||
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
@ -508,18 +509,37 @@ impl InferenceContext<'_> {
|
|||
apply_adjusts_to_place(&mut self.current_capture_span_stack, r, adjustments)
|
||||
}
|
||||
|
||||
/// Pushes the span into `current_capture_span_stack`, *without clearing it first*.
|
||||
fn path_place(&mut self, path: &Path, id: ExprOrPatId) -> Option<HirPlace> {
|
||||
if path.type_anchor().is_some() {
|
||||
return None;
|
||||
}
|
||||
let result = self.resolver.resolve_path_in_value_ns_fully(self.db.upcast(), path).and_then(
|
||||
|result| match result {
|
||||
ValueNs::LocalBinding(binding) => {
|
||||
let mir_span = match id {
|
||||
ExprOrPatId::ExprId(id) => MirSpan::ExprId(id),
|
||||
ExprOrPatId::PatId(id) => MirSpan::PatId(id),
|
||||
};
|
||||
self.current_capture_span_stack.push(mir_span);
|
||||
Some(HirPlace { local: binding, projections: Vec::new() })
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
);
|
||||
result
|
||||
}
|
||||
|
||||
/// Changes `current_capture_span_stack` to contain the stack of spans for this expr.
|
||||
fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
|
||||
self.current_capture_span_stack.clear();
|
||||
match &self.body[tgt_expr] {
|
||||
Expr::Path(p) => {
|
||||
let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
|
||||
if let Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(b), _)) =
|
||||
resolver.resolve_path_in_value_ns(self.db.upcast(), p)
|
||||
{
|
||||
self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
|
||||
return Some(HirPlace { local: b, projections: vec![] });
|
||||
}
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
|
||||
let result = self.path_place(p, tgt_expr.into());
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
return result;
|
||||
}
|
||||
Expr::Field { expr, name: _ } => {
|
||||
let mut place = self.place_of_expr(*expr)?;
|
||||
|
@ -590,6 +610,16 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn mutate_path_pat(&mut self, path: &Path, id: PatId) {
|
||||
if let Some(place) = self.path_place(path, id.into()) {
|
||||
self.add_capture(
|
||||
place,
|
||||
CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }),
|
||||
);
|
||||
self.current_capture_span_stack.pop(); // Remove the pattern span.
|
||||
}
|
||||
}
|
||||
|
||||
fn mutate_expr(&mut self, expr: ExprId, place: Option<HirPlace>) {
|
||||
if let Some(place) = place {
|
||||
self.add_capture(
|
||||
|
@ -722,7 +752,7 @@ impl InferenceContext<'_> {
|
|||
self.consume_expr(*tail);
|
||||
}
|
||||
}
|
||||
Expr::Call { callee, args, is_assignee_expr: _ } => {
|
||||
Expr::Call { callee, args } => {
|
||||
self.consume_expr(*callee);
|
||||
self.consume_exprs(args.iter().copied());
|
||||
}
|
||||
|
@ -838,7 +868,7 @@ impl InferenceContext<'_> {
|
|||
self.consume_expr(expr);
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index, is_assignee_expr: _ } => {
|
||||
Expr::Index { base, index } => {
|
||||
self.select_from_expr(*base);
|
||||
self.consume_expr(*index);
|
||||
}
|
||||
|
@ -862,10 +892,30 @@ impl InferenceContext<'_> {
|
|||
}));
|
||||
self.current_captures = cc;
|
||||
}
|
||||
Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ })
|
||||
| Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||
Expr::Array(Array::ElementList { elements: exprs }) | Expr::Tuple { exprs } => {
|
||||
self.consume_exprs(exprs.iter().copied())
|
||||
}
|
||||
&Expr::Assignment { target, value } => {
|
||||
self.walk_expr(value);
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
|
||||
match self.place_of_expr(value) {
|
||||
Some(rhs_place) => {
|
||||
self.inside_assignment = true;
|
||||
self.consume_with_pat(rhs_place, target);
|
||||
self.inside_assignment = false;
|
||||
}
|
||||
None => self.body.walk_pats(target, &mut |pat| match &self.body[pat] {
|
||||
Pat::Path(path) => self.mutate_path_pat(path, pat),
|
||||
&Pat::Expr(expr) => {
|
||||
let place = self.place_of_expr(expr);
|
||||
self.mutate_expr(expr, place);
|
||||
}
|
||||
_ => {}
|
||||
}),
|
||||
}
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
}
|
||||
|
||||
Expr::Missing
|
||||
| Expr::Continue { .. }
|
||||
|
@ -903,6 +953,7 @@ impl InferenceContext<'_> {
|
|||
| Pat::Missing
|
||||
| Pat::Wild
|
||||
| Pat::Tuple { .. }
|
||||
| Pat::Expr(_)
|
||||
| Pat::Or(_) => (),
|
||||
Pat::TupleStruct { .. } | Pat::Record { .. } => {
|
||||
if let Some(variant) = self.result.variant_resolution_for_pat(p) {
|
||||
|
@ -1122,11 +1173,15 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Pat::Range { .. }
|
||||
| Pat::Slice { .. }
|
||||
| Pat::ConstBlock(_)
|
||||
| Pat::Path(_)
|
||||
| Pat::Lit(_) => self.consume_place(place),
|
||||
Pat::Range { .. } | Pat::Slice { .. } | Pat::ConstBlock(_) | Pat::Lit(_) => {
|
||||
self.consume_place(place)
|
||||
}
|
||||
Pat::Path(path) => {
|
||||
if self.inside_assignment {
|
||||
self.mutate_path_pat(path, tgt_pat);
|
||||
}
|
||||
self.consume_place(place);
|
||||
}
|
||||
&Pat::Bind { id, subpat: _ } => {
|
||||
let mode = self.result.binding_modes[tgt_pat];
|
||||
let capture_kind = match mode {
|
||||
|
@ -1180,6 +1235,15 @@ impl InferenceContext<'_> {
|
|||
self.current_capture_span_stack.pop();
|
||||
}
|
||||
Pat::Box { .. } => (), // not supported
|
||||
&Pat::Expr(expr) => {
|
||||
self.consume_place(place);
|
||||
let pat_capture_span_stack = mem::take(&mut self.current_capture_span_stack);
|
||||
let old_inside_assignment = mem::replace(&mut self.inside_assignment, false);
|
||||
let lhs_place = self.place_of_expr(expr);
|
||||
self.mutate_expr(expr, lhs_place);
|
||||
self.inside_assignment = old_inside_assignment;
|
||||
self.current_capture_span_stack = pat_capture_span_stack;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.current_capture_span_stack
|
||||
|
|
|
@ -9,8 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
|
|||
use either::Either;
|
||||
use hir_def::{
|
||||
hir::{
|
||||
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId,
|
||||
Literal, Pat, PatId, Statement, UnaryOp,
|
||||
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, ExprOrPatId,
|
||||
LabelId, Literal, Pat, PatId, Statement, UnaryOp,
|
||||
},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
path::{GenericArg, GenericArgs, Path},
|
||||
|
@ -188,6 +188,9 @@ impl InferenceContext<'_> {
|
|||
| Pat::ConstBlock(_)
|
||||
| Pat::Record { .. }
|
||||
| Pat::Missing => true,
|
||||
Pat::Expr(_) => unreachable!(
|
||||
"we don't call pat_guaranteed_to_constitute_read_for_never() with assignments"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,6 +226,7 @@ impl InferenceContext<'_> {
|
|||
| Expr::Const(..)
|
||||
| Expr::UnaryOp { .. }
|
||||
| Expr::BinaryOp { .. }
|
||||
| Expr::Assignment { .. }
|
||||
| Expr::Yield { .. }
|
||||
| Expr::Cast { .. }
|
||||
| Expr::Async { .. }
|
||||
|
@ -609,23 +613,7 @@ impl InferenceContext<'_> {
|
|||
coerce.complete(self)
|
||||
}
|
||||
}
|
||||
Expr::Path(p) => {
|
||||
let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
|
||||
let ty = match self.infer_path(p, tgt_expr.into()) {
|
||||
Some(ty) => ty,
|
||||
None => {
|
||||
if matches!(p, Path::Normal { mod_path, .. } if mod_path.is_ident() || mod_path.is_self())
|
||||
{
|
||||
self.push_diagnostic(InferenceDiagnostic::UnresolvedIdent {
|
||||
expr: tgt_expr,
|
||||
});
|
||||
}
|
||||
self.err_ty()
|
||||
}
|
||||
};
|
||||
self.resolver.reset_to_guard(g);
|
||||
ty
|
||||
}
|
||||
Expr::Path(p) => self.infer_expr_path(p, tgt_expr.into(), tgt_expr),
|
||||
&Expr::Continue { label } => {
|
||||
if find_continuable(&mut self.breakables, label).is_none() {
|
||||
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
|
||||
|
@ -892,36 +880,6 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
}
|
||||
Expr::BinaryOp { lhs, rhs, op } => match op {
|
||||
Some(BinaryOp::Assignment { op: None }) => {
|
||||
let lhs = *lhs;
|
||||
let is_ordinary = match &self.body[lhs] {
|
||||
Expr::Array(_)
|
||||
| Expr::RecordLit { .. }
|
||||
| Expr::Tuple { .. }
|
||||
| Expr::Underscore => false,
|
||||
Expr::Call { callee, .. } => !matches!(&self.body[*callee], Expr::Path(_)),
|
||||
_ => true,
|
||||
};
|
||||
|
||||
// In ordinary (non-destructuring) assignments, the type of
|
||||
// `lhs` must be inferred first so that the ADT fields
|
||||
// instantiations in RHS can be coerced to it. Note that this
|
||||
// cannot happen in destructuring assignments because of how
|
||||
// they are desugared.
|
||||
if is_ordinary {
|
||||
// LHS of assignment doesn't constitute reads.
|
||||
let lhs_ty = self.infer_expr(lhs, &Expectation::none(), ExprIsRead::No);
|
||||
self.infer_expr_coerce(
|
||||
*rhs,
|
||||
&Expectation::has_type(lhs_ty),
|
||||
ExprIsRead::No,
|
||||
);
|
||||
} else {
|
||||
let rhs_ty = self.infer_expr(*rhs, &Expectation::none(), ExprIsRead::Yes);
|
||||
self.infer_assignee_expr(lhs, &rhs_ty);
|
||||
}
|
||||
self.result.standard_types.unit.clone()
|
||||
}
|
||||
Some(BinaryOp::LogicOp(_)) => {
|
||||
let bool_ty = self.result.standard_types.bool_.clone();
|
||||
self.infer_expr_coerce(
|
||||
|
@ -942,6 +900,35 @@ impl InferenceContext<'_> {
|
|||
Some(op) => self.infer_overloadable_binop(*lhs, *op, *rhs, tgt_expr),
|
||||
_ => self.err_ty(),
|
||||
},
|
||||
&Expr::Assignment { target, value } => {
|
||||
// In ordinary (non-destructuring) assignments, the type of
|
||||
// `lhs` must be inferred first so that the ADT fields
|
||||
// instantiations in RHS can be coerced to it. Note that this
|
||||
// cannot happen in destructuring assignments because of how
|
||||
// they are desugared.
|
||||
let lhs_ty = match &self.body[target] {
|
||||
// LHS of assignment doesn't constitute reads.
|
||||
&Pat::Expr(expr) => {
|
||||
Some(self.infer_expr(expr, &Expectation::none(), ExprIsRead::No))
|
||||
}
|
||||
Pat::Path(path) => Some(self.infer_expr_path(path, target.into(), tgt_expr)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(lhs_ty) = lhs_ty {
|
||||
self.write_pat_ty(target, lhs_ty.clone());
|
||||
self.infer_expr_coerce(value, &Expectation::has_type(lhs_ty), ExprIsRead::No);
|
||||
} else {
|
||||
let rhs_ty = self.infer_expr(value, &Expectation::none(), ExprIsRead::Yes);
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
|
||||
self.inside_assignment = true;
|
||||
self.infer_top_pat(target, &rhs_ty);
|
||||
self.inside_assignment = false;
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
}
|
||||
self.result.standard_types.unit.clone()
|
||||
}
|
||||
Expr::Range { lhs, rhs, range_type } => {
|
||||
let lhs_ty =
|
||||
lhs.map(|e| self.infer_expr_inner(e, &Expectation::none(), ExprIsRead::Yes));
|
||||
|
@ -981,7 +968,7 @@ impl InferenceContext<'_> {
|
|||
(RangeOp::Inclusive, _, None) => self.err_ty(),
|
||||
}
|
||||
}
|
||||
Expr::Index { base, index, is_assignee_expr } => {
|
||||
Expr::Index { base, index } => {
|
||||
let base_ty = self.infer_expr_inner(*base, &Expectation::none(), ExprIsRead::Yes);
|
||||
let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes);
|
||||
|
||||
|
@ -1017,23 +1004,11 @@ impl InferenceContext<'_> {
|
|||
self.write_method_resolution(tgt_expr, func, subst);
|
||||
}
|
||||
let assoc = self.resolve_ops_index_output();
|
||||
let res = self.resolve_associated_type_with_params(
|
||||
self.resolve_associated_type_with_params(
|
||||
self_ty.clone(),
|
||||
assoc,
|
||||
&[index_ty.clone().cast(Interner)],
|
||||
);
|
||||
|
||||
if *is_assignee_expr {
|
||||
if let Some(index_trait) = self.resolve_lang_trait(LangItem::IndexMut) {
|
||||
let trait_ref = TyBuilder::trait_ref(self.db, index_trait)
|
||||
.push(self_ty)
|
||||
.fill(|_| index_ty.clone().cast(Interner))
|
||||
.build();
|
||||
self.push_obligation(trait_ref.cast(Interner));
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
)
|
||||
} else {
|
||||
self.err_ty()
|
||||
}
|
||||
|
@ -1151,9 +1126,7 @@ impl InferenceContext<'_> {
|
|||
},
|
||||
},
|
||||
Expr::Underscore => {
|
||||
// Underscore expressions may only appear in assignee expressions,
|
||||
// which are handled by `infer_assignee_expr()`.
|
||||
// Any other underscore expression is an error, we render a specialized diagnostic
|
||||
// Underscore expression is an error, we render a specialized diagnostic
|
||||
// to let the user know what type is expected though.
|
||||
let expected = expected.to_option(&mut self.table).unwrap_or_else(|| self.err_ty());
|
||||
self.push_diagnostic(InferenceDiagnostic::TypedHole {
|
||||
|
@ -1232,6 +1205,22 @@ impl InferenceContext<'_> {
|
|||
ty
|
||||
}
|
||||
|
||||
fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty {
|
||||
let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, scope_id);
|
||||
let ty = match self.infer_path(path, id) {
|
||||
Some(ty) => ty,
|
||||
None => {
|
||||
if matches!(path, Path::Normal { mod_path, .. } if mod_path.is_ident() || mod_path.is_self())
|
||||
{
|
||||
self.push_diagnostic(InferenceDiagnostic::UnresolvedIdent { id });
|
||||
}
|
||||
self.err_ty()
|
||||
}
|
||||
};
|
||||
self.resolver.reset_to_guard(g);
|
||||
ty
|
||||
}
|
||||
|
||||
fn infer_async_block(
|
||||
&mut self,
|
||||
tgt_expr: ExprId,
|
||||
|
@ -1482,107 +1471,6 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn infer_assignee_expr(&mut self, lhs: ExprId, rhs_ty: &Ty) -> Ty {
|
||||
let is_rest_expr = |expr| {
|
||||
matches!(
|
||||
&self.body[expr],
|
||||
Expr::Range { lhs: None, rhs: None, range_type: RangeOp::Exclusive },
|
||||
)
|
||||
};
|
||||
|
||||
let rhs_ty = self.resolve_ty_shallow(rhs_ty);
|
||||
|
||||
let ty = match &self.body[lhs] {
|
||||
Expr::Tuple { exprs, .. } => {
|
||||
// We don't consider multiple ellipses. This is analogous to
|
||||
// `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
|
||||
let ellipsis = exprs.iter().position(|e| is_rest_expr(*e)).map(|it| it as u32);
|
||||
let exprs: Vec<_> = exprs.iter().filter(|e| !is_rest_expr(**e)).copied().collect();
|
||||
|
||||
self.infer_tuple_pat_like(&rhs_ty, (), ellipsis, &exprs)
|
||||
}
|
||||
Expr::Call { callee, args, .. } => {
|
||||
// Tuple structs
|
||||
let path = match &self.body[*callee] {
|
||||
Expr::Path(path) => Some(path),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// We don't consider multiple ellipses. This is analogous to
|
||||
// `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
|
||||
let ellipsis = args.iter().position(|e| is_rest_expr(*e)).map(|it| it as u32);
|
||||
let args: Vec<_> = args.iter().filter(|e| !is_rest_expr(**e)).copied().collect();
|
||||
|
||||
self.infer_tuple_struct_pat_like(path, &rhs_ty, (), lhs, ellipsis, &args)
|
||||
}
|
||||
Expr::Array(Array::ElementList { elements, .. }) => {
|
||||
let elem_ty = match rhs_ty.kind(Interner) {
|
||||
TyKind::Array(st, _) => st.clone(),
|
||||
_ => self.err_ty(),
|
||||
};
|
||||
|
||||
// There's no need to handle `..` as it cannot be bound.
|
||||
let sub_exprs = elements.iter().filter(|e| !is_rest_expr(**e));
|
||||
|
||||
for e in sub_exprs {
|
||||
self.infer_assignee_expr(*e, &elem_ty);
|
||||
}
|
||||
|
||||
match rhs_ty.kind(Interner) {
|
||||
TyKind::Array(_, _) => rhs_ty.clone(),
|
||||
// Even when `rhs_ty` is not an array type, this assignee
|
||||
// expression is inferred to be an array (of unknown element
|
||||
// type and length). This should not be just an error type,
|
||||
// because we are to compute the unifiability of this type and
|
||||
// `rhs_ty` in the end of this function to issue type mismatches.
|
||||
_ => TyKind::Array(
|
||||
self.err_ty(),
|
||||
crate::consteval::usize_const(self.db, None, self.resolver.krate()),
|
||||
)
|
||||
.intern(Interner),
|
||||
}
|
||||
}
|
||||
Expr::RecordLit { path, fields, .. } => {
|
||||
let subs = fields.iter().map(|f| (f.name.clone(), f.expr));
|
||||
|
||||
self.infer_record_pat_like(path.as_deref(), &rhs_ty, (), lhs, subs)
|
||||
}
|
||||
Expr::Underscore => rhs_ty.clone(),
|
||||
_ => {
|
||||
// `lhs` is a place expression, a unit struct, or an enum variant.
|
||||
// LHS of assignment doesn't constitute reads.
|
||||
let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none(), ExprIsRead::No);
|
||||
|
||||
// This is the only branch where this function may coerce any type.
|
||||
// We are returning early to avoid the unifiability check below.
|
||||
let lhs_ty = self.insert_type_vars_shallow(lhs_ty);
|
||||
let ty = match self.coerce(None, &rhs_ty, &lhs_ty, CoerceNever::Yes) {
|
||||
Ok(ty) => ty,
|
||||
Err(_) => {
|
||||
self.result.type_mismatches.insert(
|
||||
lhs.into(),
|
||||
TypeMismatch { expected: rhs_ty.clone(), actual: lhs_ty.clone() },
|
||||
);
|
||||
// `rhs_ty` is returned so no further type mismatches are
|
||||
// reported because of this mismatch.
|
||||
rhs_ty
|
||||
}
|
||||
};
|
||||
self.write_expr_ty(lhs, ty.clone());
|
||||
return ty;
|
||||
}
|
||||
};
|
||||
|
||||
let ty = self.insert_type_vars_shallow(ty);
|
||||
if !self.unify(&ty, &rhs_ty) {
|
||||
self.result
|
||||
.type_mismatches
|
||||
.insert(lhs.into(), TypeMismatch { expected: rhs_ty.clone(), actual: ty.clone() });
|
||||
}
|
||||
self.write_expr_ty(lhs, ty.clone());
|
||||
ty
|
||||
}
|
||||
|
||||
fn infer_overloadable_binop(
|
||||
&mut self,
|
||||
lhs: ExprId,
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
use chalk_ir::{cast::Cast, Mutability};
|
||||
use hir_def::{
|
||||
hir::{
|
||||
Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp,
|
||||
Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement,
|
||||
UnaryOp,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
};
|
||||
|
@ -96,7 +97,7 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
}
|
||||
Expr::MethodCall { receiver: it, method_name: _, args, generic_args: _ }
|
||||
| Expr::Call { callee: it, args, is_assignee_expr: _ } => {
|
||||
| Expr::Call { callee: it, args } => {
|
||||
self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*it)));
|
||||
}
|
||||
Expr::Match { expr, arms } => {
|
||||
|
@ -120,10 +121,10 @@ impl InferenceContext<'_> {
|
|||
Expr::Become { expr } => {
|
||||
self.infer_mut_expr(*expr, Mutability::Not);
|
||||
}
|
||||
Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => {
|
||||
Expr::RecordLit { path: _, fields, spread } => {
|
||||
self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread))
|
||||
}
|
||||
&Expr::Index { base, index, is_assignee_expr } => {
|
||||
&Expr::Index { base, index } => {
|
||||
if mutability == Mutability::Mut {
|
||||
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
|
||||
if let Some(index_trait) = self
|
||||
|
@ -148,12 +149,9 @@ impl InferenceContext<'_> {
|
|||
target,
|
||||
}) = base_adjustments
|
||||
{
|
||||
// For assignee exprs `IndexMut` obligations are already applied
|
||||
if !is_assignee_expr {
|
||||
if let TyKind::Ref(_, _, ty) = target.kind(Interner) {
|
||||
base_ty = Some(ty.clone());
|
||||
}
|
||||
}
|
||||
*mutability = Mutability::Mut;
|
||||
}
|
||||
|
||||
|
@ -233,6 +231,14 @@ impl InferenceContext<'_> {
|
|||
self.infer_mut_expr(*lhs, Mutability::Mut);
|
||||
self.infer_mut_expr(*rhs, Mutability::Not);
|
||||
}
|
||||
&Expr::Assignment { target, value } => {
|
||||
self.body.walk_pats(target, &mut |pat| match self.body[pat] {
|
||||
Pat::Expr(expr) => self.infer_mut_expr(expr, Mutability::Mut),
|
||||
Pat::ConstBlock(block) => self.infer_mut_expr(block, Mutability::Not),
|
||||
_ => {}
|
||||
});
|
||||
self.infer_mut_expr(value, Mutability::Not);
|
||||
}
|
||||
Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs })
|
||||
| Expr::BinaryOp { lhs, rhs, op: _ }
|
||||
| Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => {
|
||||
|
@ -242,8 +248,7 @@ impl InferenceContext<'_> {
|
|||
Expr::Closure { body, .. } => {
|
||||
self.infer_mut_expr(*body, Mutability::Not);
|
||||
}
|
||||
Expr::Tuple { exprs, is_assignee_expr: _ }
|
||||
| Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) => {
|
||||
Expr::Tuple { exprs } | Expr::Array(Array::ElementList { elements: exprs }) => {
|
||||
self.infer_mut_not_expr_iter(exprs.iter().copied());
|
||||
}
|
||||
// These don't need any action, as they don't have sub expressions
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::iter::repeat_with;
|
|||
|
||||
use hir_def::{
|
||||
body::Body,
|
||||
hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
|
||||
hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId},
|
||||
path::Path,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
@ -12,63 +12,28 @@ use stdx::TupleExt;
|
|||
|
||||
use crate::{
|
||||
consteval::{try_const_usize, usize_const},
|
||||
infer::{expr::ExprIsRead, BindingMode, Expectation, InferenceContext, TypeMismatch},
|
||||
infer::{
|
||||
coerce::CoerceNever, expr::ExprIsRead, BindingMode, Expectation, InferenceContext,
|
||||
TypeMismatch,
|
||||
},
|
||||
lower::lower_to_chalk_mutability,
|
||||
primitive::UintTy,
|
||||
static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty,
|
||||
TyBuilder, TyExt, TyKind,
|
||||
};
|
||||
|
||||
/// Used to generalize patterns and assignee expressions.
|
||||
pub(super) trait PatLike: Into<ExprOrPatId> + Copy {
|
||||
type BindingMode: Copy;
|
||||
|
||||
fn infer(
|
||||
this: &mut InferenceContext<'_>,
|
||||
id: Self,
|
||||
expected_ty: &Ty,
|
||||
default_bm: Self::BindingMode,
|
||||
) -> Ty;
|
||||
}
|
||||
|
||||
impl PatLike for ExprId {
|
||||
type BindingMode = ();
|
||||
|
||||
fn infer(
|
||||
this: &mut InferenceContext<'_>,
|
||||
id: Self,
|
||||
expected_ty: &Ty,
|
||||
(): Self::BindingMode,
|
||||
) -> Ty {
|
||||
this.infer_assignee_expr(id, expected_ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl PatLike for PatId {
|
||||
type BindingMode = BindingMode;
|
||||
|
||||
fn infer(
|
||||
this: &mut InferenceContext<'_>,
|
||||
id: Self,
|
||||
expected_ty: &Ty,
|
||||
default_bm: Self::BindingMode,
|
||||
) -> Ty {
|
||||
this.infer_pat(id, expected_ty, default_bm)
|
||||
}
|
||||
}
|
||||
|
||||
impl InferenceContext<'_> {
|
||||
/// Infers type for tuple struct pattern or its corresponding assignee expression.
|
||||
///
|
||||
/// Ellipses found in the original pattern or expression must be filtered out.
|
||||
pub(super) fn infer_tuple_struct_pat_like<T: PatLike>(
|
||||
pub(super) fn infer_tuple_struct_pat_like(
|
||||
&mut self,
|
||||
path: Option<&Path>,
|
||||
expected: &Ty,
|
||||
default_bm: T::BindingMode,
|
||||
id: T,
|
||||
default_bm: BindingMode,
|
||||
id: PatId,
|
||||
ellipsis: Option<u32>,
|
||||
subs: &[T],
|
||||
subs: &[PatId],
|
||||
) -> Ty {
|
||||
let (ty, def) = self.resolve_variant(path, true);
|
||||
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
|
||||
|
@ -127,13 +92,13 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
};
|
||||
|
||||
T::infer(self, subpat, &expected_ty, default_bm);
|
||||
self.infer_pat(subpat, &expected_ty, default_bm);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let err_ty = self.err_ty();
|
||||
for &inner in subs {
|
||||
T::infer(self, inner, &err_ty, default_bm);
|
||||
self.infer_pat(inner, &err_ty, default_bm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,13 +107,13 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
|
||||
/// Infers type for record pattern or its corresponding assignee expression.
|
||||
pub(super) fn infer_record_pat_like<T: PatLike>(
|
||||
pub(super) fn infer_record_pat_like(
|
||||
&mut self,
|
||||
path: Option<&Path>,
|
||||
expected: &Ty,
|
||||
default_bm: T::BindingMode,
|
||||
id: T,
|
||||
subs: impl ExactSizeIterator<Item = (Name, T)>,
|
||||
default_bm: BindingMode,
|
||||
id: PatId,
|
||||
subs: impl ExactSizeIterator<Item = (Name, PatId)>,
|
||||
) -> Ty {
|
||||
let (ty, def) = self.resolve_variant(path, false);
|
||||
if let Some(variant) = def {
|
||||
|
@ -197,13 +162,13 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
};
|
||||
|
||||
T::infer(self, inner, &expected_ty, default_bm);
|
||||
self.infer_pat(inner, &expected_ty, default_bm);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let err_ty = self.err_ty();
|
||||
for (_, inner) in subs {
|
||||
T::infer(self, inner, &err_ty, default_bm);
|
||||
self.infer_pat(inner, &err_ty, default_bm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,12 +179,12 @@ impl InferenceContext<'_> {
|
|||
/// Infers type for tuple pattern or its corresponding assignee expression.
|
||||
///
|
||||
/// Ellipses found in the original pattern or expression must be filtered out.
|
||||
pub(super) fn infer_tuple_pat_like<T: PatLike>(
|
||||
pub(super) fn infer_tuple_pat_like(
|
||||
&mut self,
|
||||
expected: &Ty,
|
||||
default_bm: T::BindingMode,
|
||||
default_bm: BindingMode,
|
||||
ellipsis: Option<u32>,
|
||||
subs: &[T],
|
||||
subs: &[PatId],
|
||||
) -> Ty {
|
||||
let expected = self.resolve_ty_shallow(expected);
|
||||
let expectations = match expected.as_tuple() {
|
||||
|
@ -244,18 +209,20 @@ impl InferenceContext<'_> {
|
|||
|
||||
// Process pre
|
||||
for (ty, pat) in inner_tys.iter_mut().zip(pre) {
|
||||
*ty = T::infer(self, *pat, ty, default_bm);
|
||||
*ty = self.infer_pat(*pat, ty, default_bm);
|
||||
}
|
||||
|
||||
// Process post
|
||||
for (ty, pat) in inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) {
|
||||
*ty = T::infer(self, *pat, ty, default_bm);
|
||||
*ty = self.infer_pat(*pat, ty, default_bm);
|
||||
}
|
||||
|
||||
TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
|
||||
.intern(Interner)
|
||||
}
|
||||
|
||||
/// The resolver needs to be updated to the surrounding expression when inside assignment
|
||||
/// (because there, `Pat::Path` can refer to a variable).
|
||||
pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty) {
|
||||
self.infer_pat(pat, expected, BindingMode::default());
|
||||
}
|
||||
|
@ -263,7 +230,14 @@ 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 self.is_non_ref_pat(self.body, pat) {
|
||||
if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment {
|
||||
cov_mark::hit!(match_ergonomics_ref);
|
||||
// When you encounter a `&pat` pattern, reset to Move.
|
||||
// This is so that `w` is by value: `let (_, &w) = &(1, &2);`
|
||||
// Destructuring assignments also reset the binding mode and
|
||||
// don't do match ergonomics.
|
||||
default_bm = BindingMode::Move;
|
||||
} else 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());
|
||||
|
@ -279,11 +253,6 @@ impl InferenceContext<'_> {
|
|||
pat_adjustments.shrink_to_fit();
|
||||
self.result.pat_adjustments.insert(pat, pat_adjustments);
|
||||
}
|
||||
} else if let Pat::Ref { .. } = &self.body[pat] {
|
||||
cov_mark::hit!(match_ergonomics_ref);
|
||||
// When you encounter a `&pat` pattern, reset to Move.
|
||||
// This is so that `w` is by value: `let (_, &w) = &(1, &2);`
|
||||
default_bm = BindingMode::Move;
|
||||
}
|
||||
|
||||
// Lose mutability.
|
||||
|
@ -320,8 +289,34 @@ impl InferenceContext<'_> {
|
|||
self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs)
|
||||
}
|
||||
Pat::Path(path) => {
|
||||
// FIXME update resolver for the surrounding expression
|
||||
self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty())
|
||||
let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty());
|
||||
let ty_inserted_vars = self.insert_type_vars_shallow(ty.clone());
|
||||
match self.table.coerce(&expected, &ty_inserted_vars, CoerceNever::Yes) {
|
||||
Ok((adjustments, coerced_ty)) => {
|
||||
if !adjustments.is_empty() {
|
||||
self.result
|
||||
.pat_adjustments
|
||||
.entry(pat)
|
||||
.or_default()
|
||||
.extend(adjustments.into_iter().map(|adjust| adjust.target));
|
||||
}
|
||||
self.write_pat_ty(pat, coerced_ty);
|
||||
return self.pat_ty_after_adjustment(pat);
|
||||
}
|
||||
Err(_) => {
|
||||
self.result.type_mismatches.insert(
|
||||
pat.into(),
|
||||
TypeMismatch {
|
||||
expected: expected.clone(),
|
||||
actual: ty_inserted_vars.clone(),
|
||||
},
|
||||
);
|
||||
self.write_pat_ty(pat, ty);
|
||||
// We return `expected` to prevent cascading errors. I guess an alternative is to
|
||||
// not emit type mismatches for error types and emit an error type here.
|
||||
return expected;
|
||||
}
|
||||
}
|
||||
}
|
||||
Pat::Bind { id, subpat } => {
|
||||
return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected);
|
||||
|
@ -361,7 +356,40 @@ impl InferenceContext<'_> {
|
|||
None => self.err_ty(),
|
||||
},
|
||||
Pat::ConstBlock(expr) => {
|
||||
self.infer_expr(*expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes)
|
||||
let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false);
|
||||
let result = self.infer_expr(
|
||||
*expr,
|
||||
&Expectation::has_type(expected.clone()),
|
||||
ExprIsRead::Yes,
|
||||
);
|
||||
self.inside_assignment = old_inside_assign;
|
||||
result
|
||||
}
|
||||
Pat::Expr(expr) => {
|
||||
let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false);
|
||||
// LHS of assignment doesn't constitute reads.
|
||||
let result = self.infer_expr_coerce(
|
||||
*expr,
|
||||
&Expectation::has_type(expected.clone()),
|
||||
ExprIsRead::No,
|
||||
);
|
||||
// We are returning early to avoid the unifiability check below.
|
||||
let lhs_ty = self.insert_type_vars_shallow(result);
|
||||
let ty = match self.coerce(None, &expected, &lhs_ty, CoerceNever::Yes) {
|
||||
Ok(ty) => ty,
|
||||
Err(_) => {
|
||||
self.result.type_mismatches.insert(
|
||||
pat.into(),
|
||||
TypeMismatch { expected: expected.clone(), actual: lhs_ty.clone() },
|
||||
);
|
||||
// `rhs_ty` is returned so no further type mismatches are
|
||||
// reported because of this mismatch.
|
||||
expected
|
||||
}
|
||||
};
|
||||
self.write_pat_ty(pat, ty.clone());
|
||||
self.inside_assignment = old_inside_assign;
|
||||
return ty;
|
||||
}
|
||||
Pat::Missing => self.err_ty(),
|
||||
};
|
||||
|
@ -517,9 +545,12 @@ impl InferenceContext<'_> {
|
|||
body[*expr],
|
||||
Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..))
|
||||
),
|
||||
Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => {
|
||||
false
|
||||
}
|
||||
Pat::Wild
|
||||
| Pat::Bind { .. }
|
||||
| Pat::Ref { .. }
|
||||
| Pat::Box { .. }
|
||||
| Pat::Missing
|
||||
| Pat::Expr(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use hir_def::{
|
|||
},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
path::Path,
|
||||
resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs},
|
||||
resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
|
||||
AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId,
|
||||
Lookup, TraitId, TupleId, TypeOrConstParamId,
|
||||
};
|
||||
|
@ -76,6 +76,7 @@ struct MirLowerCtx<'a> {
|
|||
db: &'a dyn HirDatabase,
|
||||
body: &'a Body,
|
||||
infer: &'a InferenceResult,
|
||||
resolver: Resolver,
|
||||
drop_scopes: Vec<DropScope>,
|
||||
}
|
||||
|
||||
|
@ -278,6 +279,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
owner,
|
||||
closures: vec![],
|
||||
};
|
||||
let resolver = owner.resolver(db.upcast());
|
||||
|
||||
MirLowerCtx {
|
||||
result: mir,
|
||||
|
@ -285,6 +287,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
infer,
|
||||
body,
|
||||
owner,
|
||||
resolver,
|
||||
current_loop_blocks: None,
|
||||
labeled_loop_blocks: Default::default(),
|
||||
discr_temp: None,
|
||||
|
@ -410,8 +413,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
Err(MirLowerError::IncompleteExpr)
|
||||
}
|
||||
Expr::Path(p) => {
|
||||
let pr =
|
||||
if let Some((assoc, subst)) = self.infer.assoc_resolutions_for_expr(expr_id) {
|
||||
let pr = if let Some((assoc, subst)) =
|
||||
self.infer.assoc_resolutions_for_expr(expr_id)
|
||||
{
|
||||
match assoc {
|
||||
hir_def::AssocItemId::ConstId(c) => {
|
||||
self.lower_const(
|
||||
|
@ -440,12 +444,16 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
VariantId::UnionId(_) => implementation_error!("Union variant as path"),
|
||||
}
|
||||
} else {
|
||||
let unresolved_name =
|
||||
|| MirLowerError::unresolved_path(self.db, p, self.edition());
|
||||
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
||||
resolver
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
||||
let result = self
|
||||
.resolver
|
||||
.resolve_path_in_value_ns_fully(self.db.upcast(), p)
|
||||
.ok_or_else(unresolved_name)?
|
||||
.ok_or_else(|| {
|
||||
MirLowerError::unresolved_path(self.db, p, self.edition())
|
||||
})?;
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
result
|
||||
};
|
||||
match pr {
|
||||
ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => {
|
||||
|
@ -553,8 +561,11 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
return Ok(None);
|
||||
};
|
||||
self.push_fake_read(current, cond_place, expr_id.into());
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
||||
let (then_target, else_target) =
|
||||
self.pattern_match(current, None, cond_place, *pat)?;
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
self.write_bytes_to_place(
|
||||
then_target,
|
||||
place,
|
||||
|
@ -688,6 +699,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
};
|
||||
self.push_fake_read(current, cond_place, expr_id.into());
|
||||
let mut end = None;
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
||||
for MatchArm { pat, guard, expr } in arms.iter() {
|
||||
let (then, mut otherwise) =
|
||||
self.pattern_match(current, None, cond_place, *pat)?;
|
||||
|
@ -721,6 +734,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
if self.is_unterminated(current) {
|
||||
self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into());
|
||||
}
|
||||
|
@ -795,7 +809,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
}
|
||||
Expr::Become { .. } => not_supported!("tail-calls"),
|
||||
Expr::Yield { .. } => not_supported!("yield"),
|
||||
Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => {
|
||||
Expr::RecordLit { fields, path, spread } => {
|
||||
let spread_place = match spread {
|
||||
&Some(it) => {
|
||||
let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else {
|
||||
|
@ -1010,8 +1024,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
);
|
||||
}
|
||||
}
|
||||
if let hir_def::hir::BinaryOp::Assignment { op } = op {
|
||||
if let Some(op) = op {
|
||||
if let hir_def::hir::BinaryOp::Assignment { op: Some(op) } = op {
|
||||
// last adjustment is `&mut` which we don't want it.
|
||||
let adjusts = self
|
||||
.infer
|
||||
|
@ -1019,16 +1032,13 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
.get(lhs)
|
||||
.and_then(|it| it.split_last())
|
||||
.map(|it| it.1)
|
||||
.ok_or(MirLowerError::TypeError(
|
||||
"adjustment of binary op was missing",
|
||||
))?;
|
||||
.ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?;
|
||||
let Some((lhs_place, current)) =
|
||||
self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some((rhs_op, current)) =
|
||||
self.lower_expr_to_some_operand(*rhs, current)?
|
||||
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
@ -1036,9 +1046,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place), rhs_op);
|
||||
self.push_assignment(current, lhs_place, r_value, expr_id.into());
|
||||
return Ok(Some(current));
|
||||
} else {
|
||||
return self.lower_assignment(current, *lhs, *rhs, expr_id.into());
|
||||
}
|
||||
}
|
||||
let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)?
|
||||
else {
|
||||
|
@ -1097,6 +1104,18 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
);
|
||||
Ok(Some(current))
|
||||
}
|
||||
&Expr::Assignment { target, value } => {
|
||||
let Some((value, mut current)) = self.lower_expr_as_place(current, value, true)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.push_fake_read(current, value, expr_id.into());
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
||||
current = self.pattern_match_assignment(current, value, target)?;
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
Ok(Some(current))
|
||||
}
|
||||
&Expr::Range { lhs, rhs, range_type: _ } => {
|
||||
let ty = self.expr_ty_without_adjust(expr_id);
|
||||
let Some((adt, subst)) = ty.as_adt() else {
|
||||
|
@ -1213,7 +1232,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
);
|
||||
Ok(Some(current))
|
||||
}
|
||||
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||
Expr::Tuple { exprs } => {
|
||||
let Some(values) = exprs
|
||||
.iter()
|
||||
.map(|it| {
|
||||
|
@ -1291,73 +1310,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lower_destructing_assignment(
|
||||
&mut self,
|
||||
mut current: BasicBlockId,
|
||||
lhs: ExprId,
|
||||
rhs: Place,
|
||||
span: MirSpan,
|
||||
) -> Result<Option<BasicBlockId>> {
|
||||
match &self.body.exprs[lhs] {
|
||||
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||
for (i, expr) in exprs.iter().enumerate() {
|
||||
let rhs = rhs.project(
|
||||
ProjectionElem::Field(Either::Right(TupleFieldId {
|
||||
tuple: TupleId(!0), // Dummy this as its unused
|
||||
index: i as u32,
|
||||
})),
|
||||
&mut self.result.projection_store,
|
||||
);
|
||||
let Some(c) = self.lower_destructing_assignment(current, *expr, rhs, span)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
current = c;
|
||||
}
|
||||
Ok(Some(current))
|
||||
}
|
||||
Expr::Underscore => Ok(Some(current)),
|
||||
_ => {
|
||||
let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.push_assignment(current, lhs_place, Operand::Copy(rhs).into(), span);
|
||||
Ok(Some(current))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_assignment(
|
||||
&mut self,
|
||||
current: BasicBlockId,
|
||||
lhs: ExprId,
|
||||
rhs: ExprId,
|
||||
span: MirSpan,
|
||||
) -> Result<Option<BasicBlockId>> {
|
||||
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(rhs, current)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
if matches!(&self.body.exprs[lhs], Expr::Underscore) {
|
||||
self.push_fake_read_for_operand(current, rhs_op, span);
|
||||
return Ok(Some(current));
|
||||
}
|
||||
if matches!(
|
||||
&self.body.exprs[lhs],
|
||||
Expr::Tuple { .. } | Expr::RecordLit { .. } | Expr::Call { .. }
|
||||
) {
|
||||
let temp = self.temp(self.expr_ty_after_adjustments(rhs), current, rhs.into())?;
|
||||
let temp = Place::from(temp);
|
||||
self.push_assignment(current, temp, rhs_op.into(), span);
|
||||
return self.lower_destructing_assignment(current, lhs, temp, span);
|
||||
}
|
||||
let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.push_assignment(current, lhs_place, rhs_op.into(), span);
|
||||
Ok(Some(current))
|
||||
}
|
||||
|
||||
fn placeholder_subst(&mut self) -> Substitution {
|
||||
match self.owner.as_generic_def_id(self.db.upcast()) {
|
||||
Some(it) => TyBuilder::placeholder_subst(self.db, it),
|
||||
|
@ -1407,8 +1359,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
let edition = self.edition();
|
||||
let unresolved_name =
|
||||
|| MirLowerError::unresolved_path(self.db, c.as_ref(), edition);
|
||||
let resolver = self.owner.resolver(self.db.upcast());
|
||||
let pr = resolver
|
||||
let pr = self
|
||||
.resolver
|
||||
.resolve_path_in_value_ns(self.db.upcast(), c.as_ref())
|
||||
.ok_or_else(unresolved_name)?;
|
||||
match pr {
|
||||
|
@ -1632,12 +1584,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
self.push_statement(block, StatementKind::FakeRead(p).with_span(span));
|
||||
}
|
||||
|
||||
fn push_fake_read_for_operand(&mut self, block: BasicBlockId, operand: Operand, span: MirSpan) {
|
||||
if let Operand::Move(p) | Operand::Copy(p) = operand {
|
||||
self.push_fake_read(block, p, span);
|
||||
}
|
||||
}
|
||||
|
||||
fn push_assignment(
|
||||
&mut self,
|
||||
block: BasicBlockId,
|
||||
|
@ -1791,8 +1737,16 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
};
|
||||
current = c;
|
||||
self.push_fake_read(current, init_place, span);
|
||||
// Using the initializer for the resolver scope is good enough for us, as it cannot create new declarations
|
||||
// and has all declarations of the `let`.
|
||||
let resolver_guard = self.resolver.update_to_inner_scope(
|
||||
self.db.upcast(),
|
||||
self.owner,
|
||||
*expr_id,
|
||||
);
|
||||
(current, else_block) =
|
||||
self.pattern_match(current, None, init_place, *pat)?;
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
match (else_block, else_branch) {
|
||||
(None, _) => (),
|
||||
(Some(else_block), None) => {
|
||||
|
@ -2066,11 +2020,13 @@ pub fn mir_body_for_closure_query(
|
|||
let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else {
|
||||
implementation_error!("closure has not callable sig");
|
||||
};
|
||||
let resolver_guard = ctx.resolver.update_to_inner_scope(db.upcast(), owner, expr);
|
||||
let current = ctx.lower_params_and_bindings(
|
||||
args.iter().zip(sig.params().iter()).map(|(it, y)| (*it, y.clone())),
|
||||
None,
|
||||
|_| true,
|
||||
)?;
|
||||
ctx.resolver.reset_to_guard(resolver_guard);
|
||||
if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
|
||||
let current = ctx.pop_drop_scope_assert_finished(current, root.into())?;
|
||||
ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
|
||||
|
|
|
@ -135,8 +135,11 @@ impl MirLowerCtx<'_> {
|
|||
};
|
||||
match &self.body.exprs[expr_id] {
|
||||
Expr::Path(p) => {
|
||||
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
||||
let Some(pr) = resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p) else {
|
||||
let resolver_guard =
|
||||
self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
|
||||
let resolved = self.resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p);
|
||||
self.resolver.reset_to_guard(resolver_guard);
|
||||
let Some(pr) = resolved else {
|
||||
return try_rvalue(self);
|
||||
};
|
||||
match pr {
|
||||
|
@ -216,7 +219,7 @@ impl MirLowerCtx<'_> {
|
|||
self.push_field_projection(&mut r, expr_id)?;
|
||||
Ok(Some((r, current)))
|
||||
}
|
||||
Expr::Index { base, index, is_assignee_expr: _ } => {
|
||||
Expr::Index { base, index } => {
|
||||
let base_ty = self.expr_ty_after_adjustments(*base);
|
||||
let index_ty = self.expr_ty_after_adjustments(*index);
|
||||
if index_ty != TyBuilder::usize()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! MIR lowering for patterns
|
||||
|
||||
use hir_def::{hir::LiteralOrConst, resolver::HasResolver, AssocItemId};
|
||||
use hir_def::{hir::LiteralOrConst, AssocItemId};
|
||||
|
||||
use crate::{
|
||||
mir::{
|
||||
|
@ -46,6 +46,8 @@ enum MatchingMode {
|
|||
Check,
|
||||
/// Assume that this pattern matches, fill bindings
|
||||
Bind,
|
||||
/// Assume that this pattern matches, assign to existing variables.
|
||||
Assign,
|
||||
}
|
||||
|
||||
impl MirLowerCtx<'_> {
|
||||
|
@ -82,6 +84,17 @@ impl MirLowerCtx<'_> {
|
|||
Ok((current, current_else))
|
||||
}
|
||||
|
||||
pub(super) fn pattern_match_assignment(
|
||||
&mut self,
|
||||
current: BasicBlockId,
|
||||
value: Place,
|
||||
pattern: PatId,
|
||||
) -> Result<BasicBlockId> {
|
||||
let (current, _) =
|
||||
self.pattern_match_inner(current, None, value, pattern, MatchingMode::Assign)?;
|
||||
Ok(current)
|
||||
}
|
||||
|
||||
pub(super) fn match_self_param(
|
||||
&mut self,
|
||||
id: BindingId,
|
||||
|
@ -155,14 +168,8 @@ impl MirLowerCtx<'_> {
|
|||
*pat,
|
||||
MatchingMode::Check,
|
||||
)?;
|
||||
if mode == MatchingMode::Bind {
|
||||
(next, _) = self.pattern_match_inner(
|
||||
next,
|
||||
None,
|
||||
cond_place,
|
||||
*pat,
|
||||
MatchingMode::Bind,
|
||||
)?;
|
||||
if mode != MatchingMode::Check {
|
||||
(next, _) = self.pattern_match_inner(next, None, cond_place, *pat, mode)?;
|
||||
}
|
||||
self.set_goto(next, then_target, pattern.into());
|
||||
match next_else {
|
||||
|
@ -176,11 +183,11 @@ impl MirLowerCtx<'_> {
|
|||
}
|
||||
}
|
||||
if !finished {
|
||||
if mode == MatchingMode::Bind {
|
||||
self.set_terminator(current, TerminatorKind::Unreachable, pattern.into());
|
||||
} else {
|
||||
if mode == MatchingMode::Check {
|
||||
let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
|
||||
self.set_goto(current, ce, pattern.into());
|
||||
} else {
|
||||
self.set_terminator(current, TerminatorKind::Unreachable, pattern.into());
|
||||
}
|
||||
}
|
||||
(then_target, current_else)
|
||||
|
@ -300,7 +307,7 @@ impl MirLowerCtx<'_> {
|
|||
self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
|
||||
}
|
||||
if let &Some(slice) = slice {
|
||||
if mode == MatchingMode::Bind {
|
||||
if mode != MatchingMode::Check {
|
||||
if let Pat::Bind { id, subpat: _ } = self.body[slice] {
|
||||
let next_place = cond_place.project(
|
||||
ProjectionElem::Subslice {
|
||||
|
@ -342,17 +349,34 @@ impl MirLowerCtx<'_> {
|
|||
mode,
|
||||
)?,
|
||||
None => {
|
||||
// The path is not a variant, so it is a const
|
||||
let unresolved_name =
|
||||
|| MirLowerError::unresolved_path(self.db, p, self.edition());
|
||||
let pr = self
|
||||
.resolver
|
||||
.resolve_path_in_value_ns(self.db.upcast(), p)
|
||||
.ok_or_else(unresolved_name)?;
|
||||
|
||||
if let (
|
||||
MatchingMode::Assign,
|
||||
ResolveValueResult::ValueNs(ValueNs::LocalBinding(binding), _),
|
||||
) = (mode, &pr)
|
||||
{
|
||||
let local = self.binding_local(*binding)?;
|
||||
self.push_match_assignment(
|
||||
current,
|
||||
local,
|
||||
BindingMode::Move,
|
||||
cond_place,
|
||||
pattern.into(),
|
||||
);
|
||||
return Ok((current, current_else));
|
||||
}
|
||||
|
||||
// The path is not a variant or a local, so it is a const
|
||||
if mode != MatchingMode::Check {
|
||||
// A const don't bind anything. Only needs check.
|
||||
return Ok((current, current_else));
|
||||
}
|
||||
let unresolved_name =
|
||||
|| MirLowerError::unresolved_path(self.db, p, self.edition());
|
||||
let resolver = self.owner.resolver(self.db.upcast());
|
||||
let pr = resolver
|
||||
.resolve_path_in_value_ns(self.db.upcast(), p)
|
||||
.ok_or_else(unresolved_name)?;
|
||||
let (c, subst) = 'b: {
|
||||
if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern) {
|
||||
if let AssocItemId::ConstId(c) = x.0 {
|
||||
|
@ -415,7 +439,7 @@ impl MirLowerCtx<'_> {
|
|||
(current, current_else) =
|
||||
self.pattern_match_inner(current, current_else, cond_place, *subpat, mode)?
|
||||
}
|
||||
if mode == MatchingMode::Bind {
|
||||
if mode != MatchingMode::Check {
|
||||
let mode = self.infer.binding_modes[pattern];
|
||||
self.pattern_match_binding(
|
||||
*id,
|
||||
|
@ -448,6 +472,23 @@ impl MirLowerCtx<'_> {
|
|||
cond_place.project(ProjectionElem::Deref, &mut self.result.projection_store);
|
||||
self.pattern_match_inner(current, current_else, cond_place, *pat, mode)?
|
||||
}
|
||||
&Pat::Expr(expr) => {
|
||||
stdx::always!(
|
||||
mode == MatchingMode::Assign,
|
||||
"Pat::Expr can only come in destructuring assignments"
|
||||
);
|
||||
let Some((lhs_place, current)) = self.lower_expr_as_place(current, expr, false)?
|
||||
else {
|
||||
return Ok((current, current_else));
|
||||
};
|
||||
self.push_assignment(
|
||||
current,
|
||||
lhs_place,
|
||||
Operand::Copy(cond_place).into(),
|
||||
expr.into(),
|
||||
);
|
||||
(current, current_else)
|
||||
}
|
||||
Pat::Box { .. } => not_supported!("box pattern"),
|
||||
Pat::ConstBlock(_) => not_supported!("const block pattern"),
|
||||
})
|
||||
|
@ -464,6 +505,18 @@ impl MirLowerCtx<'_> {
|
|||
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
|
||||
let target_place = self.binding_local(id)?;
|
||||
self.push_storage_live(id, current)?;
|
||||
self.push_match_assignment(current, target_place, mode, cond_place, span);
|
||||
Ok((current, current_else))
|
||||
}
|
||||
|
||||
fn push_match_assignment(
|
||||
&mut self,
|
||||
current: BasicBlockId,
|
||||
target_place: LocalId,
|
||||
mode: BindingMode,
|
||||
cond_place: Place,
|
||||
span: MirSpan,
|
||||
) {
|
||||
self.push_assignment(
|
||||
current,
|
||||
target_place.into(),
|
||||
|
@ -476,7 +529,6 @@ impl MirLowerCtx<'_> {
|
|||
},
|
||||
span,
|
||||
);
|
||||
Ok((current, current_else))
|
||||
}
|
||||
|
||||
fn pattern_match_const(
|
||||
|
|
|
@ -3418,11 +3418,11 @@ struct TS(usize);
|
|||
fn main() {
|
||||
let x;
|
||||
[x,] = &[1,];
|
||||
//^^^^expected &'? [i32; 1], got [{unknown}; _]
|
||||
//^^^^expected &'? [i32; 1], got [{unknown}]
|
||||
|
||||
let x;
|
||||
[(x,),] = &[(1,),];
|
||||
//^^^^^^^expected &'? [(i32,); 1], got [{unknown}; _]
|
||||
//^^^^^^^expected &'? [(i32,); 1], got [{unknown}]
|
||||
|
||||
let x;
|
||||
((x,),) = &((1,),);
|
||||
|
|
|
@ -246,7 +246,7 @@ pub struct UnresolvedAssocItem {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct UnresolvedIdent {
|
||||
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||
pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -257,7 +257,7 @@ pub struct PrivateField {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct MissingUnsafe {
|
||||
pub expr: InFile<AstPtr<ast::Expr>>,
|
||||
pub expr: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
|
||||
/// If true, the diagnostics is an `unsafe_op_in_unsafe_fn` lint instead of a hard error.
|
||||
pub only_lint: bool,
|
||||
}
|
||||
|
@ -398,22 +398,23 @@ impl AnyDiagnostic {
|
|||
.map(|idx| variant_data.fields()[idx].name.clone())
|
||||
.collect();
|
||||
|
||||
match record {
|
||||
Either::Left(record_expr) => match source_map.expr_syntax(record_expr) {
|
||||
Ok(source_ptr) => {
|
||||
let root = source_ptr.file_syntax(db.upcast());
|
||||
if let ast::Expr::RecordExpr(record_expr) =
|
||||
source_ptr.value.to_node(&root)
|
||||
{
|
||||
let record = match record {
|
||||
Either::Left(record_expr) => {
|
||||
source_map.expr_syntax(record_expr).ok()?.map(AstPtr::wrap_left)
|
||||
}
|
||||
Either::Right(record_pat) => source_map.pat_syntax(record_pat).ok()?,
|
||||
};
|
||||
let file = record.file_id;
|
||||
let root = record.file_syntax(db.upcast());
|
||||
match record.value.to_node(&root) {
|
||||
Either::Left(ast::Expr::RecordExpr(record_expr)) => {
|
||||
if record_expr.record_expr_field_list().is_some() {
|
||||
let field_list_parent_path =
|
||||
record_expr.path().map(|path| AstPtr::new(&path));
|
||||
return Some(
|
||||
MissingFields {
|
||||
file: source_ptr.file_id,
|
||||
field_list_parent: AstPtr::new(&Either::Left(
|
||||
record_expr,
|
||||
)),
|
||||
file,
|
||||
field_list_parent: AstPtr::new(&Either::Left(record_expr)),
|
||||
field_list_parent_path,
|
||||
missed_fields,
|
||||
}
|
||||
|
@ -421,23 +422,14 @@ impl AnyDiagnostic {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(SyntheticSyntax) => (),
|
||||
},
|
||||
Either::Right(record_pat) => match source_map.pat_syntax(record_pat) {
|
||||
Ok(source_ptr) => {
|
||||
if let Some(ptr) = source_ptr.value.cast::<ast::RecordPat>() {
|
||||
let root = source_ptr.file_syntax(db.upcast());
|
||||
let record_pat = ptr.to_node(&root);
|
||||
Either::Right(ast::Pat::RecordPat(record_pat)) => {
|
||||
if record_pat.record_pat_field_list().is_some() {
|
||||
let field_list_parent_path =
|
||||
record_pat.path().map(|path| AstPtr::new(&path));
|
||||
return Some(
|
||||
MissingFields {
|
||||
file: source_ptr.file_id,
|
||||
field_list_parent: AstPtr::new(&Either::Right(
|
||||
record_pat,
|
||||
)),
|
||||
file,
|
||||
field_list_parent: AstPtr::new(&Either::Right(record_pat)),
|
||||
field_list_parent_path,
|
||||
missed_fields,
|
||||
}
|
||||
|
@ -445,9 +437,7 @@ impl AnyDiagnostic {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(SyntheticSyntax) => (),
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => {
|
||||
|
@ -541,15 +531,17 @@ impl AnyDiagnostic {
|
|||
let pat_syntax = |pat| {
|
||||
source_map.pat_syntax(pat).inspect_err(|_| tracing::error!("synthetic syntax")).ok()
|
||||
};
|
||||
let expr_or_pat_syntax = |id| match id {
|
||||
ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(|it| it.map(AstPtr::wrap_left)),
|
||||
ExprOrPatId::PatId(pat) => pat_syntax(pat),
|
||||
};
|
||||
Some(match d {
|
||||
&InferenceDiagnostic::NoSuchField { field: expr, private, variant } => {
|
||||
let expr_or_pat = match expr {
|
||||
ExprOrPatId::ExprId(expr) => {
|
||||
source_map.field_syntax(expr).map(AstPtr::wrap_left)
|
||||
}
|
||||
ExprOrPatId::PatId(pat) => {
|
||||
source_map.pat_field_syntax(pat).map(AstPtr::wrap_right)
|
||||
}
|
||||
ExprOrPatId::PatId(pat) => source_map.pat_field_syntax(pat),
|
||||
};
|
||||
NoSuchField { field: expr_or_pat, private, variant }.into()
|
||||
}
|
||||
|
@ -562,10 +554,7 @@ impl AnyDiagnostic {
|
|||
PrivateField { expr, field }.into()
|
||||
}
|
||||
&InferenceDiagnostic::PrivateAssocItem { id, item } => {
|
||||
let expr_or_pat = match id {
|
||||
ExprOrPatId::ExprId(expr) => expr_syntax(expr)?.map(AstPtr::wrap_left),
|
||||
ExprOrPatId::PatId(pat) => pat_syntax(pat)?.map(AstPtr::wrap_right),
|
||||
};
|
||||
let expr_or_pat = expr_or_pat_syntax(id)?;
|
||||
let item = item.into();
|
||||
PrivateAssocItem { expr_or_pat, item }.into()
|
||||
}
|
||||
|
@ -609,15 +598,12 @@ impl AnyDiagnostic {
|
|||
.into()
|
||||
}
|
||||
&InferenceDiagnostic::UnresolvedAssocItem { id } => {
|
||||
let expr_or_pat = match id {
|
||||
ExprOrPatId::ExprId(expr) => expr_syntax(expr)?.map(AstPtr::wrap_left),
|
||||
ExprOrPatId::PatId(pat) => pat_syntax(pat)?.map(AstPtr::wrap_right),
|
||||
};
|
||||
let expr_or_pat = expr_or_pat_syntax(id)?;
|
||||
UnresolvedAssocItem { expr_or_pat }.into()
|
||||
}
|
||||
&InferenceDiagnostic::UnresolvedIdent { expr } => {
|
||||
let expr = expr_syntax(expr)?;
|
||||
UnresolvedIdent { expr }.into()
|
||||
&InferenceDiagnostic::UnresolvedIdent { id } => {
|
||||
let expr_or_pat = expr_or_pat_syntax(id)?;
|
||||
UnresolvedIdent { expr_or_pat }.into()
|
||||
}
|
||||
&InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
|
||||
let expr = expr_syntax(expr)?;
|
||||
|
|
|
@ -1885,7 +1885,7 @@ impl DefWithBody {
|
|||
|
||||
let (unafe_exprs, only_lint) = hir_ty::diagnostics::missing_unsafe(db, self.into());
|
||||
for expr in unafe_exprs {
|
||||
match source_map.expr_syntax(expr) {
|
||||
match source_map.expr_or_pat_syntax(expr) {
|
||||
Ok(expr) => acc.push(MissingUnsafe { expr, only_lint }.into()),
|
||||
Err(SyntheticSyntax) => {
|
||||
// FIXME: Here and elsewhere in this file, the `expr` was
|
||||
|
@ -3481,7 +3481,7 @@ impl Local {
|
|||
LocalSource {
|
||||
local: self,
|
||||
source: src.map(|ast| match ast.to_node(&root) {
|
||||
ast::Pat::IdentPat(it) => Either::Left(it),
|
||||
Either::Right(ast::Pat::IdentPat(it)) => Either::Left(it),
|
||||
_ => unreachable!("local with non ident-pattern"),
|
||||
}),
|
||||
}
|
||||
|
@ -3510,7 +3510,7 @@ impl Local {
|
|||
LocalSource {
|
||||
local: self,
|
||||
source: src.map(|ast| match ast.to_node(&root) {
|
||||
ast::Pat::IdentPat(it) => Either::Left(it),
|
||||
Either::Right(ast::Pat::IdentPat(it)) => Either::Left(it),
|
||||
_ => unreachable!("local with non ident-pattern"),
|
||||
}),
|
||||
}
|
||||
|
@ -4235,10 +4235,7 @@ impl CaptureUsages {
|
|||
}
|
||||
mir::MirSpan::PatId(pat) => {
|
||||
if let Ok(pat) = source_map.pat_syntax(pat) {
|
||||
result.push(CaptureUsageSource {
|
||||
is_ref,
|
||||
source: pat.map(AstPtr::wrap_right),
|
||||
});
|
||||
result.push(CaptureUsageSource { is_ref, source: pat });
|
||||
}
|
||||
}
|
||||
mir::MirSpan::BindingId(binding) => result.extend(
|
||||
|
@ -4246,10 +4243,7 @@ impl CaptureUsages {
|
|||
.patterns_for_binding(binding)
|
||||
.iter()
|
||||
.filter_map(|&pat| source_map.pat_syntax(pat).ok())
|
||||
.map(|pat| CaptureUsageSource {
|
||||
is_ref,
|
||||
source: pat.map(AstPtr::wrap_right),
|
||||
}),
|
||||
.map(|pat| CaptureUsageSource { is_ref, source: pat }),
|
||||
),
|
||||
mir::MirSpan::SelfParam | mir::MirSpan::Unknown => {
|
||||
unreachable!("invalid capture usage span")
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::{
|
|||
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
hir::Expr,
|
||||
hir::{Expr, ExprOrPatId},
|
||||
lower::LowerCtx,
|
||||
nameres::{MacroSubNs, ModuleOrigin},
|
||||
path::ModPath,
|
||||
|
@ -1777,7 +1777,9 @@ impl<'db> SemanticsImpl<'db> {
|
|||
}
|
||||
|
||||
if let Some(parent) = ast::Expr::cast(parent.clone()) {
|
||||
if let Some(expr_id) = source_map.node_expr(InFile { file_id, value: &parent }) {
|
||||
if let Some(ExprOrPatId::ExprId(expr_id)) =
|
||||
source_map.node_expr(InFile { file_id, value: &parent })
|
||||
{
|
||||
if let Expr::Unsafe { .. } = body[expr_id] {
|
||||
break true;
|
||||
}
|
||||
|
|
|
@ -328,7 +328,7 @@ impl SourceToDefCtx<'_, '_> {
|
|||
.position(|it| it == *src.value)?;
|
||||
let container = self.find_pat_or_label_container(src.syntax_ref())?;
|
||||
let (_, source_map) = self.db.body_with_source_map(container);
|
||||
let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?;
|
||||
let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?.as_expr()?;
|
||||
Some(InlineAsmOperand { owner: container, expr, index })
|
||||
}
|
||||
|
||||
|
@ -372,7 +372,8 @@ impl SourceToDefCtx<'_, '_> {
|
|||
let break_or_continue = ast::Expr::cast(src.value.syntax().parent()?)?;
|
||||
let container = self.find_pat_or_label_container(src.syntax_ref())?;
|
||||
let (body, source_map) = self.db.body_with_source_map(container);
|
||||
let break_or_continue = source_map.node_expr(src.with_value(&break_or_continue))?;
|
||||
let break_or_continue =
|
||||
source_map.node_expr(src.with_value(&break_or_continue))?.as_expr()?;
|
||||
let (Expr::Break { label, .. } | Expr::Continue { label }) = body[break_or_continue] else {
|
||||
return None;
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@ use hir_def::{
|
|||
scope::{ExprScopes, ScopeId},
|
||||
Body, BodySourceMap,
|
||||
},
|
||||
hir::{BindingId, ExprId, Pat, PatId},
|
||||
hir::{BindingId, ExprId, ExprOrPatId, Pat, PatId},
|
||||
lang_item::LangItem,
|
||||
lower::LowerCtx,
|
||||
nameres::MacroSubNs,
|
||||
|
@ -120,7 +120,7 @@ impl SourceAnalyzer {
|
|||
self.def.as_ref().map(|(_, body, _)| &**body)
|
||||
}
|
||||
|
||||
fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprId> {
|
||||
fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprOrPatId> {
|
||||
let src = match expr {
|
||||
ast::Expr::MacroExpr(expr) => {
|
||||
self.expand_expr(db, InFile::new(self.file_id, expr.macro_call()?))?.into()
|
||||
|
@ -174,7 +174,9 @@ impl SourceAnalyzer {
|
|||
db: &dyn HirDatabase,
|
||||
expr: &ast::Expr,
|
||||
) -> Option<&[Adjustment]> {
|
||||
let expr_id = self.expr_id(db, expr)?;
|
||||
// It is safe to omit destructuring assignments here because they have no adjustments (neither
|
||||
// expressions nor patterns).
|
||||
let expr_id = self.expr_id(db, expr)?.as_expr()?;
|
||||
let infer = self.infer.as_ref()?;
|
||||
infer.expr_adjustments.get(&expr_id).map(|v| &**v)
|
||||
}
|
||||
|
@ -186,9 +188,9 @@ impl SourceAnalyzer {
|
|||
) -> Option<(Type, Option<Type>)> {
|
||||
let expr_id = self.expr_id(db, expr)?;
|
||||
let infer = self.infer.as_ref()?;
|
||||
let coerced = infer
|
||||
.expr_adjustments
|
||||
.get(&expr_id)
|
||||
let coerced = expr_id
|
||||
.as_expr()
|
||||
.and_then(|expr_id| infer.expr_adjustments.get(&expr_id))
|
||||
.and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.clone()));
|
||||
let ty = infer[expr_id].clone();
|
||||
let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
|
||||
|
@ -268,7 +270,7 @@ impl SourceAnalyzer {
|
|||
db: &dyn HirDatabase,
|
||||
call: &ast::MethodCallExpr,
|
||||
) -> Option<Callable> {
|
||||
let expr_id = self.expr_id(db, &call.clone().into())?;
|
||||
let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?;
|
||||
let (func, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
|
||||
let ty = db.value_ty(func.into())?.substitute(Interner, &substs);
|
||||
let ty = Type::new_with_resolver(db, &self.resolver, ty);
|
||||
|
@ -282,7 +284,7 @@ impl SourceAnalyzer {
|
|||
db: &dyn HirDatabase,
|
||||
call: &ast::MethodCallExpr,
|
||||
) -> Option<Function> {
|
||||
let expr_id = self.expr_id(db, &call.clone().into())?;
|
||||
let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?;
|
||||
let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
|
||||
|
||||
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into())
|
||||
|
@ -293,7 +295,7 @@ impl SourceAnalyzer {
|
|||
db: &dyn HirDatabase,
|
||||
call: &ast::MethodCallExpr,
|
||||
) -> Option<Either<Function, Field>> {
|
||||
let expr_id = self.expr_id(db, &call.clone().into())?;
|
||||
let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?;
|
||||
let inference_result = self.infer.as_ref()?;
|
||||
match inference_result.method_resolution(expr_id) {
|
||||
Some((f_in_trait, substs)) => Some(Either::Left(
|
||||
|
@ -322,7 +324,7 @@ impl SourceAnalyzer {
|
|||
field: &ast::FieldExpr,
|
||||
) -> Option<Either<Field, TupleField>> {
|
||||
let &(def, ..) = self.def.as_ref()?;
|
||||
let expr_id = self.expr_id(db, &field.clone().into())?;
|
||||
let expr_id = self.expr_id(db, &field.clone().into())?.as_expr()?;
|
||||
self.infer.as_ref()?.field_resolution(expr_id).map(|it| {
|
||||
it.map_either(Into::into, |f| TupleField { owner: def, tuple: f.tuple, index: f.index })
|
||||
})
|
||||
|
@ -334,7 +336,7 @@ impl SourceAnalyzer {
|
|||
field: &ast::FieldExpr,
|
||||
) -> Option<Either<Either<Field, TupleField>, Function>> {
|
||||
let &(def, ..) = self.def.as_ref()?;
|
||||
let expr_id = self.expr_id(db, &field.clone().into())?;
|
||||
let expr_id = self.expr_id(db, &field.clone().into())?.as_expr()?;
|
||||
let inference_result = self.infer.as_ref()?;
|
||||
match inference_result.field_resolution(expr_id) {
|
||||
Some(field) => Some(Either::Left(field.map_either(Into::into, |f| TupleField {
|
||||
|
@ -442,7 +444,7 @@ impl SourceAnalyzer {
|
|||
self.infer
|
||||
.as_ref()
|
||||
.and_then(|infer| {
|
||||
let expr = self.expr_id(db, &prefix_expr.clone().into())?;
|
||||
let expr = self.expr_id(db, &prefix_expr.clone().into())?.as_expr()?;
|
||||
let (func, _) = infer.method_resolution(expr)?;
|
||||
let (deref_mut_trait, deref_mut) = self.lang_trait_fn(
|
||||
db,
|
||||
|
@ -488,7 +490,7 @@ impl SourceAnalyzer {
|
|||
.infer
|
||||
.as_ref()
|
||||
.and_then(|infer| {
|
||||
let expr = self.expr_id(db, &index_expr.clone().into())?;
|
||||
let expr = self.expr_id(db, &index_expr.clone().into())?.as_expr()?;
|
||||
let (func, _) = infer.method_resolution(expr)?;
|
||||
let (index_mut_trait, index_mut_fn) = self.lang_trait_fn(
|
||||
db,
|
||||
|
@ -576,8 +578,8 @@ impl SourceAnalyzer {
|
|||
_ => None,
|
||||
}
|
||||
};
|
||||
let (_, subst) = self.infer.as_ref()?.type_of_expr.get(expr_id)?.as_adt()?;
|
||||
let variant = self.infer.as_ref()?.variant_resolution_for_expr(expr_id)?;
|
||||
let (_, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?;
|
||||
let variant = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?;
|
||||
let variant_data = variant.variant_data(db.upcast());
|
||||
let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? };
|
||||
let field_ty =
|
||||
|
@ -645,10 +647,10 @@ impl SourceAnalyzer {
|
|||
let infer = self.infer.as_deref()?;
|
||||
if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) {
|
||||
let expr_id = self.expr_id(db, &path_expr.into())?;
|
||||
if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr(expr_id) {
|
||||
if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_id) {
|
||||
let assoc = match assoc {
|
||||
AssocItemId::FunctionId(f_in_trait) => {
|
||||
match infer.type_of_expr.get(expr_id) {
|
||||
match infer.type_of_expr_or_pat(expr_id) {
|
||||
None => assoc,
|
||||
Some(func_ty) => {
|
||||
if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) {
|
||||
|
@ -673,7 +675,7 @@ impl SourceAnalyzer {
|
|||
return Some(PathResolution::Def(AssocItem::from(assoc).into()));
|
||||
}
|
||||
if let Some(VariantId::EnumVariantId(variant)) =
|
||||
infer.variant_resolution_for_expr(expr_id)
|
||||
infer.variant_resolution_for_expr_or_pat(expr_id)
|
||||
{
|
||||
return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
|
||||
}
|
||||
|
@ -697,7 +699,7 @@ impl SourceAnalyzer {
|
|||
} else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) {
|
||||
let expr_id = self.expr_id(db, &rec_lit.into())?;
|
||||
if let Some(VariantId::EnumVariantId(variant)) =
|
||||
infer.variant_resolution_for_expr(expr_id)
|
||||
infer.variant_resolution_for_expr_or_pat(expr_id)
|
||||
{
|
||||
return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
|
||||
}
|
||||
|
@ -829,10 +831,16 @@ impl SourceAnalyzer {
|
|||
let infer = self.infer.as_ref()?;
|
||||
|
||||
let expr_id = self.expr_id(db, &literal.clone().into())?;
|
||||
let substs = infer.type_of_expr[expr_id].as_adt()?.1;
|
||||
let substs = infer[expr_id].as_adt()?.1;
|
||||
|
||||
let (variant, missing_fields, _exhaustive) =
|
||||
record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?;
|
||||
let (variant, missing_fields, _exhaustive) = match expr_id {
|
||||
ExprOrPatId::ExprId(expr_id) => {
|
||||
record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?
|
||||
}
|
||||
ExprOrPatId::PatId(pat_id) => {
|
||||
record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?
|
||||
}
|
||||
};
|
||||
let res = self.missing_fields(db, substs, variant, missing_fields);
|
||||
Some(res)
|
||||
}
|
||||
|
@ -895,7 +903,7 @@ impl SourceAnalyzer {
|
|||
) -> Option<VariantId> {
|
||||
let infer = self.infer.as_ref()?;
|
||||
let expr_id = self.expr_id(db, &record_lit.into())?;
|
||||
infer.variant_resolution_for_expr(expr_id)
|
||||
infer.variant_resolution_for_expr_or_pat(expr_id)
|
||||
}
|
||||
|
||||
pub(crate) fn is_unsafe_macro_call_expr(
|
||||
|
@ -906,14 +914,24 @@ impl SourceAnalyzer {
|
|||
if let (Some((def, body, sm)), Some(infer)) = (&self.def, &self.infer) {
|
||||
if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr) {
|
||||
let mut is_unsafe = false;
|
||||
let mut walk_expr = |expr_id| {
|
||||
unsafe_expressions(
|
||||
db,
|
||||
infer,
|
||||
*def,
|
||||
body,
|
||||
expanded_expr,
|
||||
&mut |UnsafeExpr { inside_unsafe_block, .. }| is_unsafe |= !inside_unsafe_block,
|
||||
);
|
||||
expr_id,
|
||||
&mut |UnsafeExpr { inside_unsafe_block, .. }| {
|
||||
is_unsafe |= !inside_unsafe_block
|
||||
},
|
||||
)
|
||||
};
|
||||
match expanded_expr {
|
||||
ExprOrPatId::ExprId(expanded_expr) => walk_expr(expanded_expr),
|
||||
ExprOrPatId::PatId(expanded_pat) => {
|
||||
body.walk_exprs_in_pat(expanded_pat, &mut walk_expr)
|
||||
}
|
||||
}
|
||||
return is_unsafe;
|
||||
}
|
||||
}
|
||||
|
@ -1030,7 +1048,7 @@ impl SourceAnalyzer {
|
|||
}
|
||||
|
||||
fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
|
||||
self.infer.as_ref()?.type_of_expr.get(self.expr_id(db, expr)?)
|
||||
self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1043,7 +1061,7 @@ fn scope_for(
|
|||
node.ancestors_with_macros(db.upcast())
|
||||
.take_while(|it| !ast::Item::can_cast(it.kind()) || ast::MacroCall::can_cast(it.kind()))
|
||||
.filter_map(|it| it.map(ast::Expr::cast).transpose())
|
||||
.filter_map(|it| source_map.node_expr(it.as_ref()))
|
||||
.filter_map(|it| source_map.node_expr(it.as_ref())?.as_expr())
|
||||
.find_map(|it| scopes.scope_for(it))
|
||||
}
|
||||
|
||||
|
|
|
@ -308,22 +308,27 @@ struct T(S);
|
|||
fn regular(a: S) {
|
||||
let s;
|
||||
S { s, .. } = a;
|
||||
_ = s;
|
||||
}
|
||||
fn nested(a: S2) {
|
||||
let s;
|
||||
S2 { s: S { s, .. }, .. } = a;
|
||||
_ = s;
|
||||
}
|
||||
fn in_tuple(a: (S,)) {
|
||||
let s;
|
||||
(S { s, .. },) = a;
|
||||
_ = s;
|
||||
}
|
||||
fn in_array(a: [S;1]) {
|
||||
let s;
|
||||
[S { s, .. },] = a;
|
||||
_ = s;
|
||||
}
|
||||
fn in_tuple_struct(a: T) {
|
||||
let s;
|
||||
T(S { s, .. }) = a;
|
||||
_ = s;
|
||||
}
|
||||
",
|
||||
);
|
||||
|
|
|
@ -32,7 +32,8 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option<Vec<Ass
|
|||
}
|
||||
|
||||
let root = ctx.sema.db.parse_or_expand(d.expr.file_id);
|
||||
let expr = d.expr.value.to_node(&root);
|
||||
let node = d.expr.value.to_node(&root);
|
||||
let expr = node.syntax().ancestors().find_map(ast::Expr::cast)?;
|
||||
|
||||
let node_to_add_unsafe_block = pick_best_node_to_add_unsafe_block(&expr)?;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use hir::db::ExpandDatabase;
|
||||
use ide_db::source_change::SourceChange;
|
||||
use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T};
|
||||
use syntax::{ast, AstNode, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, T};
|
||||
use text_edit::TextEdit;
|
||||
|
||||
use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
|
||||
|
@ -8,18 +9,27 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
|
|||
//
|
||||
// This diagnostic is triggered on mutating an immutable variable.
|
||||
pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option<Diagnostic> {
|
||||
if d.span.file_id.macro_file().is_some() {
|
||||
// FIXME: Our infra can't handle allow from within macro expansions rn
|
||||
return None;
|
||||
let root = ctx.sema.db.parse_or_expand(d.span.file_id);
|
||||
let node = d.span.value.to_node(&root);
|
||||
let mut span = d.span;
|
||||
if let Some(parent) = node.parent() {
|
||||
if ast::BinExpr::can_cast(parent.kind()) {
|
||||
// In case of an assignment, the diagnostic is provided on the variable name.
|
||||
// We want to expand it to include the whole assignment, but only when this
|
||||
// is an ordinary assignment, not a destructuring assignment. So, the direct
|
||||
// parent is an assignment expression.
|
||||
span = d.span.with_value(SyntaxNodePtr::new(&parent));
|
||||
}
|
||||
};
|
||||
|
||||
let fixes = (|| {
|
||||
if d.local.is_ref(ctx.sema.db) {
|
||||
// There is no simple way to add `mut` to `ref x` and `ref mut x`
|
||||
return None;
|
||||
}
|
||||
let file_id = d.span.file_id.file_id()?;
|
||||
let file_id = span.file_id.file_id()?;
|
||||
let mut edit_builder = TextEdit::builder();
|
||||
let use_range = d.span.value.text_range();
|
||||
let use_range = span.value.text_range();
|
||||
for source in d.local.sources(ctx.sema.db) {
|
||||
let Some(ast) = source.name() else { continue };
|
||||
// FIXME: macros
|
||||
|
@ -33,6 +43,7 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option
|
|||
use_range,
|
||||
)])
|
||||
})();
|
||||
|
||||
Some(
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
|
@ -42,7 +53,7 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option
|
|||
"cannot mutate immutable variable `{}`",
|
||||
d.local.name(ctx.sema.db).display(ctx.sema.db, ctx.edition)
|
||||
),
|
||||
d.span,
|
||||
span,
|
||||
)
|
||||
.with_fixes(fixes),
|
||||
)
|
||||
|
@ -53,10 +64,6 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option
|
|||
// This diagnostic is triggered when a mutable variable isn't actually mutated.
|
||||
pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Option<Diagnostic> {
|
||||
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
|
||||
if ast.file_id.macro_file().is_some() {
|
||||
// FIXME: Our infra can't handle allow from within macro expansions rn
|
||||
return None;
|
||||
}
|
||||
let fixes = (|| {
|
||||
let file_id = ast.file_id.file_id()?;
|
||||
let mut edit_builder = TextEdit::builder();
|
||||
|
@ -937,7 +944,6 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
|
|||
|
||||
#[test]
|
||||
fn closure() {
|
||||
// FIXME: Diagnostic spans are inconsistent inside and outside closure
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: copy, fn
|
||||
|
@ -950,11 +956,11 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
|
|||
fn f() {
|
||||
let x = 5;
|
||||
let closure1 = || { x = 2; };
|
||||
//^ 💡 error: cannot mutate immutable variable `x`
|
||||
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
let _ = closure1();
|
||||
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
|
||||
let closure2 = || { x = x; };
|
||||
//^ 💡 error: cannot mutate immutable variable `x`
|
||||
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
let closure3 = || {
|
||||
let x = 2;
|
||||
x = 5;
|
||||
|
@ -996,7 +1002,7 @@ fn f() {
|
|||
|| {
|
||||
let x = 2;
|
||||
|| { || { x = 5; } }
|
||||
//^ 💡 error: cannot mutate immutable variable `x`
|
||||
//^^^^^ 💡 error: cannot mutate immutable variable `x`
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1279,6 +1285,21 @@ fn main() {
|
|||
unsafe {
|
||||
**wrap = 6;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destructuring_assignment_needs_mut() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: fn
|
||||
|
||||
fn main() {
|
||||
let mut var = 1;
|
||||
let mut func = || (var,) = (2,);
|
||||
func();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
|
@ -11,7 +11,7 @@ pub(crate) fn unresolved_ident(
|
|||
ctx,
|
||||
DiagnosticCode::RustcHardError("E0425"),
|
||||
"no such value in this scope",
|
||||
d.expr.map(Into::into),
|
||||
d.expr_or_pat.map(Into::into),
|
||||
)
|
||||
.experimental()
|
||||
}
|
||||
|
|
|
@ -232,6 +232,10 @@ impl ast::RangeExpr {
|
|||
Some((ix, token, bin_op))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_range_full(&self) -> bool {
|
||||
support::children::<Expr>(&self.syntax).next().is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl RangeItem for ast::RangeExpr {
|
||||
|
|
Loading…
Reference in a new issue