mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 05:53:45 +00:00
Merge remote-tracking branch 'origin/master' into sync-from-rust-2
This commit is contained in:
commit
0d04e63627
12 changed files with 170 additions and 65 deletions
|
@ -96,6 +96,7 @@ pub(super) fn lower(
|
||||||
expander,
|
expander,
|
||||||
name_to_pat_grouping: Default::default(),
|
name_to_pat_grouping: Default::default(),
|
||||||
is_lowering_inside_or_pat: false,
|
is_lowering_inside_or_pat: false,
|
||||||
|
is_lowering_assignee_expr: false,
|
||||||
}
|
}
|
||||||
.collect(params, body)
|
.collect(params, body)
|
||||||
}
|
}
|
||||||
|
@ -109,6 +110,7 @@ struct ExprCollector<'a> {
|
||||||
// a poor-mans union-find?
|
// a poor-mans union-find?
|
||||||
name_to_pat_grouping: FxHashMap<Name, Vec<PatId>>,
|
name_to_pat_grouping: FxHashMap<Name, Vec<PatId>>,
|
||||||
is_lowering_inside_or_pat: bool,
|
is_lowering_inside_or_pat: bool,
|
||||||
|
is_lowering_assignee_expr: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExprCollector<'_> {
|
impl ExprCollector<'_> {
|
||||||
|
@ -283,7 +285,10 @@ impl ExprCollector<'_> {
|
||||||
} else {
|
} else {
|
||||||
Box::default()
|
Box::default()
|
||||||
};
|
};
|
||||||
self.alloc_expr(Expr::Call { callee, args }, syntax_ptr)
|
self.alloc_expr(
|
||||||
|
Expr::Call { callee, args, is_assignee_expr: self.is_lowering_assignee_expr },
|
||||||
|
syntax_ptr,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
ast::Expr::MethodCallExpr(e) => {
|
ast::Expr::MethodCallExpr(e) => {
|
||||||
let receiver = self.collect_expr_opt(e.receiver());
|
let receiver = self.collect_expr_opt(e.receiver());
|
||||||
|
@ -359,6 +364,7 @@ impl ExprCollector<'_> {
|
||||||
ast::Expr::RecordExpr(e) => {
|
ast::Expr::RecordExpr(e) => {
|
||||||
let path =
|
let path =
|
||||||
e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
|
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 record_lit = if let Some(nfl) = e.record_expr_field_list() {
|
||||||
let fields = nfl
|
let fields = nfl
|
||||||
.fields()
|
.fields()
|
||||||
|
@ -378,9 +384,16 @@ impl ExprCollector<'_> {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let spread = nfl.spread().map(|s| self.collect_expr(s));
|
let spread = nfl.spread().map(|s| self.collect_expr(s));
|
||||||
Expr::RecordLit { path, fields, spread }
|
let ellipsis = nfl.dotdot_token().is_some();
|
||||||
|
Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr }
|
||||||
} else {
|
} else {
|
||||||
Expr::RecordLit { path, fields: Box::default(), spread: None }
|
Expr::RecordLit {
|
||||||
|
path,
|
||||||
|
fields: Box::default(),
|
||||||
|
spread: None,
|
||||||
|
ellipsis: false,
|
||||||
|
is_assignee_expr,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.alloc_expr(record_lit, syntax_ptr)
|
self.alloc_expr(record_lit, syntax_ptr)
|
||||||
|
@ -458,14 +471,21 @@ impl ExprCollector<'_> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ast::Expr::BinExpr(e) => {
|
ast::Expr::BinExpr(e) => {
|
||||||
let lhs = self.collect_expr_opt(e.lhs());
|
|
||||||
let rhs = self.collect_expr_opt(e.rhs());
|
|
||||||
let op = e.op_kind();
|
let op = e.op_kind();
|
||||||
|
if let Some(ast::BinaryOp::Assignment { op: None }) = op {
|
||||||
|
self.is_lowering_assignee_expr = true;
|
||||||
|
}
|
||||||
|
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)
|
self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
|
||||||
}
|
}
|
||||||
ast::Expr::TupleExpr(e) => {
|
ast::Expr::TupleExpr(e) => {
|
||||||
let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect();
|
let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect();
|
||||||
self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr)
|
self.alloc_expr(
|
||||||
|
Expr::Tuple { exprs, is_assignee_expr: self.is_lowering_assignee_expr },
|
||||||
|
syntax_ptr,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
ast::Expr::BoxExpr(e) => {
|
ast::Expr::BoxExpr(e) => {
|
||||||
let expr = self.collect_expr_opt(e.expr());
|
let expr = self.collect_expr_opt(e.expr());
|
||||||
|
@ -477,8 +497,14 @@ impl ExprCollector<'_> {
|
||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
ArrayExprKind::ElementList(e) => {
|
ArrayExprKind::ElementList(e) => {
|
||||||
let exprs = e.map(|expr| self.collect_expr(expr)).collect();
|
let elements = e.map(|expr| self.collect_expr(expr)).collect();
|
||||||
self.alloc_expr(Expr::Array(Array::ElementList(exprs)), syntax_ptr)
|
self.alloc_expr(
|
||||||
|
Expr::Array(Array::ElementList {
|
||||||
|
elements,
|
||||||
|
is_assignee_expr: self.is_lowering_assignee_expr,
|
||||||
|
}),
|
||||||
|
syntax_ptr,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
ArrayExprKind::Repeat { initializer, repeat } => {
|
ArrayExprKind::Repeat { initializer, repeat } => {
|
||||||
let initializer = self.collect_expr_opt(initializer);
|
let initializer = self.collect_expr_opt(initializer);
|
||||||
|
|
|
@ -110,6 +110,7 @@ pub enum Expr {
|
||||||
Call {
|
Call {
|
||||||
callee: ExprId,
|
callee: ExprId,
|
||||||
args: Box<[ExprId]>,
|
args: Box<[ExprId]>,
|
||||||
|
is_assignee_expr: bool,
|
||||||
},
|
},
|
||||||
MethodCall {
|
MethodCall {
|
||||||
receiver: ExprId,
|
receiver: ExprId,
|
||||||
|
@ -138,6 +139,8 @@ pub enum Expr {
|
||||||
path: Option<Box<Path>>,
|
path: Option<Box<Path>>,
|
||||||
fields: Box<[RecordLitField]>,
|
fields: Box<[RecordLitField]>,
|
||||||
spread: Option<ExprId>,
|
spread: Option<ExprId>,
|
||||||
|
ellipsis: bool,
|
||||||
|
is_assignee_expr: bool,
|
||||||
},
|
},
|
||||||
Field {
|
Field {
|
||||||
expr: ExprId,
|
expr: ExprId,
|
||||||
|
@ -196,6 +199,7 @@ pub enum Expr {
|
||||||
},
|
},
|
||||||
Tuple {
|
Tuple {
|
||||||
exprs: Box<[ExprId]>,
|
exprs: Box<[ExprId]>,
|
||||||
|
is_assignee_expr: bool,
|
||||||
},
|
},
|
||||||
Unsafe {
|
Unsafe {
|
||||||
body: ExprId,
|
body: ExprId,
|
||||||
|
@ -211,7 +215,7 @@ pub enum Expr {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum Array {
|
pub enum Array {
|
||||||
ElementList(Box<[ExprId]>),
|
ElementList { elements: Box<[ExprId]>, is_assignee_expr: bool },
|
||||||
Repeat { initializer: ExprId, repeat: ExprId },
|
Repeat { initializer: ExprId, repeat: ExprId },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,7 +289,7 @@ impl Expr {
|
||||||
f(*iterable);
|
f(*iterable);
|
||||||
f(*body);
|
f(*body);
|
||||||
}
|
}
|
||||||
Expr::Call { callee, args } => {
|
Expr::Call { callee, args, .. } => {
|
||||||
f(*callee);
|
f(*callee);
|
||||||
args.iter().copied().for_each(f);
|
args.iter().copied().for_each(f);
|
||||||
}
|
}
|
||||||
|
@ -339,9 +343,9 @@ impl Expr {
|
||||||
| Expr::Box { expr } => {
|
| Expr::Box { expr } => {
|
||||||
f(*expr);
|
f(*expr);
|
||||||
}
|
}
|
||||||
Expr::Tuple { exprs } => exprs.iter().copied().for_each(f),
|
Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
|
||||||
Expr::Array(a) => match a {
|
Expr::Array(a) => match a {
|
||||||
Array::ElementList(exprs) => exprs.iter().copied().for_each(f),
|
Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
|
||||||
Array::Repeat { initializer, repeat } => {
|
Array::Repeat { initializer, repeat } => {
|
||||||
f(*initializer);
|
f(*initializer);
|
||||||
f(*repeat)
|
f(*repeat)
|
||||||
|
|
|
@ -305,7 +305,10 @@ pub fn record_literal_missing_fields(
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> {
|
) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> {
|
||||||
let (fields, exhaustive) = match expr {
|
let (fields, exhaustive) = match expr {
|
||||||
Expr::RecordLit { path: _, fields, spread } => (fields, spread.is_none()),
|
Expr::RecordLit { fields, spread, ellipsis, is_assignee_expr, .. } => {
|
||||||
|
let exhaustive = if *is_assignee_expr { !*ellipsis } else { spread.is_none() };
|
||||||
|
(fields, exhaustive)
|
||||||
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -276,7 +276,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
|
|
||||||
closure_ty
|
closure_ty
|
||||||
}
|
}
|
||||||
Expr::Call { callee, args } => {
|
Expr::Call { callee, args, .. } => {
|
||||||
let callee_ty = self.infer_expr(*callee, &Expectation::none());
|
let callee_ty = self.infer_expr(*callee, &Expectation::none());
|
||||||
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone());
|
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone());
|
||||||
let mut res = None;
|
let mut res = None;
|
||||||
|
@ -421,7 +421,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
}
|
}
|
||||||
TyKind::Never.intern(Interner)
|
TyKind::Never.intern(Interner)
|
||||||
}
|
}
|
||||||
Expr::RecordLit { path, fields, spread } => {
|
Expr::RecordLit { path, fields, spread, .. } => {
|
||||||
let (ty, def_id) = self.resolve_variant(path.as_deref(), false);
|
let (ty, def_id) = self.resolve_variant(path.as_deref(), false);
|
||||||
if let Some(variant) = def_id {
|
if let Some(variant) = def_id {
|
||||||
self.write_variant_resolution(tgt_expr.into(), variant);
|
self.write_variant_resolution(tgt_expr.into(), variant);
|
||||||
|
@ -693,7 +693,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.err_ty()
|
self.err_ty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Tuple { exprs } => {
|
Expr::Tuple { exprs, .. } => {
|
||||||
let mut tys = match expected
|
let mut tys = match expected
|
||||||
.only_has_type(&mut self.table)
|
.only_has_type(&mut self.table)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -724,12 +724,12 @@ impl<'a> InferenceContext<'a> {
|
||||||
|
|
||||||
let expected = Expectation::has_type(elem_ty.clone());
|
let expected = Expectation::has_type(elem_ty.clone());
|
||||||
let len = match array {
|
let len = match array {
|
||||||
Array::ElementList(items) => {
|
Array::ElementList { elements, .. } => {
|
||||||
for &expr in items.iter() {
|
for &expr in elements.iter() {
|
||||||
let cur_elem_ty = self.infer_expr_inner(expr, &expected);
|
let cur_elem_ty = self.infer_expr_inner(expr, &expected);
|
||||||
coerce.coerce(self, Some(expr), &cur_elem_ty);
|
coerce.coerce(self, Some(expr), &cur_elem_ty);
|
||||||
}
|
}
|
||||||
consteval::usize_const(Some(items.len() as u128))
|
consteval::usize_const(Some(elements.len() as u128))
|
||||||
}
|
}
|
||||||
&Array::Repeat { initializer, repeat } => {
|
&Array::Repeat { initializer, repeat } => {
|
||||||
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
|
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
|
||||||
|
@ -850,7 +850,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
let rhs_ty = self.resolve_ty_shallow(rhs_ty);
|
let rhs_ty = self.resolve_ty_shallow(rhs_ty);
|
||||||
|
|
||||||
let ty = match &self.body[lhs] {
|
let ty = match &self.body[lhs] {
|
||||||
Expr::Tuple { exprs } => {
|
Expr::Tuple { exprs, .. } => {
|
||||||
// We don't consider multiple ellipses. This is analogous to
|
// We don't consider multiple ellipses. This is analogous to
|
||||||
// `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
|
// `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
|
||||||
let ellipsis = exprs.iter().position(|e| is_rest_expr(*e));
|
let ellipsis = exprs.iter().position(|e| is_rest_expr(*e));
|
||||||
|
@ -858,7 +858,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
|
|
||||||
self.infer_tuple_pat_like(&rhs_ty, (), ellipsis, &exprs)
|
self.infer_tuple_pat_like(&rhs_ty, (), ellipsis, &exprs)
|
||||||
}
|
}
|
||||||
Expr::Call { callee, args } => {
|
Expr::Call { callee, args, .. } => {
|
||||||
// Tuple structs
|
// Tuple structs
|
||||||
let path = match &self.body[*callee] {
|
let path = match &self.body[*callee] {
|
||||||
Expr::Path(path) => Some(path),
|
Expr::Path(path) => Some(path),
|
||||||
|
@ -872,7 +872,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
|
|
||||||
self.infer_tuple_struct_pat_like(path, &rhs_ty, (), lhs, ellipsis, &args)
|
self.infer_tuple_struct_pat_like(path, &rhs_ty, (), lhs, ellipsis, &args)
|
||||||
}
|
}
|
||||||
Expr::Array(Array::ElementList(elements)) => {
|
Expr::Array(Array::ElementList { elements, .. }) => {
|
||||||
let elem_ty = match rhs_ty.kind(Interner) {
|
let elem_ty = match rhs_ty.kind(Interner) {
|
||||||
TyKind::Array(st, _) => st.clone(),
|
TyKind::Array(st, _) => st.clone(),
|
||||||
_ => self.err_ty(),
|
_ => self.err_ty(),
|
||||||
|
|
|
@ -145,9 +145,11 @@ fn add_missing_impl_members_inner(
|
||||||
Some(cap) => {
|
Some(cap) => {
|
||||||
let mut cursor = Cursor::Before(first_new_item.syntax());
|
let mut cursor = Cursor::Before(first_new_item.syntax());
|
||||||
let placeholder;
|
let placeholder;
|
||||||
|
if let DefaultMethods::No = mode {
|
||||||
if let ast::AssocItem::Fn(func) = &first_new_item {
|
if let ast::AssocItem::Fn(func) = &first_new_item {
|
||||||
if try_gen_trait_body(ctx, func, &trait_, &impl_def).is_none() {
|
if try_gen_trait_body(ctx, func, &trait_, &impl_def).is_none() {
|
||||||
if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
|
if let Some(m) =
|
||||||
|
func.syntax().descendants().find_map(ast::MacroCall::cast)
|
||||||
{
|
{
|
||||||
if m.syntax().text() == "todo!()" {
|
if m.syntax().text() == "todo!()" {
|
||||||
placeholder = m;
|
placeholder = m;
|
||||||
|
@ -156,6 +158,7 @@ fn add_missing_impl_members_inner(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
builder.replace_snippet(
|
builder.replace_snippet(
|
||||||
cap,
|
cap,
|
||||||
target,
|
target,
|
||||||
|
|
|
@ -5,7 +5,7 @@ use syntax::{
|
||||||
ted,
|
ted,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Generate custom trait bodies where possible.
|
/// Generate custom trait bodies without default implementation where possible.
|
||||||
///
|
///
|
||||||
/// Returns `Option` so that we can use `?` rather than `if let Some`. Returning
|
/// Returns `Option` so that we can use `?` rather than `if let Some`. Returning
|
||||||
/// `None` means that generating a custom trait body failed, and the body will remain
|
/// `None` means that generating a custom trait body failed, and the body will remain
|
||||||
|
@ -28,6 +28,7 @@ pub(crate) fn gen_trait_fn_body(
|
||||||
|
|
||||||
/// Generate a `Clone` impl based on the fields and members of the target type.
|
/// Generate a `Clone` impl based on the fields and members of the target type.
|
||||||
fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
||||||
|
stdx::always!(func.name().map_or(false, |name| name.text() == "clone"));
|
||||||
fn gen_clone_call(target: ast::Expr) -> ast::Expr {
|
fn gen_clone_call(target: ast::Expr) -> ast::Expr {
|
||||||
let method = make::name_ref("clone");
|
let method = make::name_ref("clone");
|
||||||
make::expr_method_call(target, method, make::arg_list(None))
|
make::expr_method_call(target, method, make::arg_list(None))
|
||||||
|
@ -339,6 +340,7 @@ fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
||||||
|
|
||||||
/// Generate a `Hash` impl based on the fields and members of the target type.
|
/// Generate a `Hash` impl based on the fields and members of the target type.
|
||||||
fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
||||||
|
stdx::always!(func.name().map_or(false, |name| name.text() == "hash"));
|
||||||
fn gen_hash_call(target: ast::Expr) -> ast::Stmt {
|
fn gen_hash_call(target: ast::Expr) -> ast::Stmt {
|
||||||
let method = make::name_ref("hash");
|
let method = make::name_ref("hash");
|
||||||
let arg = make::expr_path(make::ext::ident_path("state"));
|
let arg = make::expr_path(make::ext::ident_path("state"));
|
||||||
|
@ -394,9 +396,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
||||||
|
|
||||||
/// Generate a `PartialEq` impl based on the fields and members of the target type.
|
/// Generate a `PartialEq` impl based on the fields and members of the target type.
|
||||||
fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
||||||
if func.name().map_or(false, |name| name.text() == "ne") {
|
stdx::always!(func.name().map_or(false, |name| name.text() == "eq"));
|
||||||
return None;
|
|
||||||
}
|
|
||||||
fn gen_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> {
|
fn gen_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> {
|
||||||
match expr {
|
match expr {
|
||||||
Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)),
|
Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)),
|
||||||
|
@ -573,6 +573,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
||||||
|
stdx::always!(func.name().map_or(false, |name| name.text() == "partial_cmp"));
|
||||||
fn gen_partial_eq_match(match_target: ast::Expr) -> Option<ast::Stmt> {
|
fn gen_partial_eq_match(match_target: ast::Expr) -> Option<ast::Stmt> {
|
||||||
let mut arms = vec![];
|
let mut arms = vec![];
|
||||||
|
|
||||||
|
@ -643,7 +644,7 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
|
||||||
make::block_expr(stmts.into_iter(), tail).indent(ast::edit::IndentLevel(1))
|
make::block_expr(stmts.into_iter(), tail).indent(ast::edit::IndentLevel(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
// No fields in the body means there's nothing to hash.
|
// No fields in the body means there's nothing to compare.
|
||||||
None => {
|
None => {
|
||||||
let expr = make::expr_literal("true").into();
|
let expr = make::expr_literal("true").into();
|
||||||
make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
|
make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
|
||||||
|
|
|
@ -121,7 +121,7 @@ pub(crate) fn render_field(
|
||||||
let mut item = CompletionItem::new(
|
let mut item = CompletionItem::new(
|
||||||
SymbolKind::Field,
|
SymbolKind::Field,
|
||||||
ctx.source_range(),
|
ctx.source_range(),
|
||||||
receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name).into()),
|
field_with_receiver(receiver.as_ref(), &name),
|
||||||
);
|
);
|
||||||
item.set_relevance(CompletionRelevance {
|
item.set_relevance(CompletionRelevance {
|
||||||
type_match: compute_type_match(ctx.completion, ty),
|
type_match: compute_type_match(ctx.completion, ty),
|
||||||
|
@ -132,7 +132,7 @@ pub(crate) fn render_field(
|
||||||
.set_documentation(field.docs(ctx.db()))
|
.set_documentation(field.docs(ctx.db()))
|
||||||
.set_deprecated(is_deprecated)
|
.set_deprecated(is_deprecated)
|
||||||
.lookup_by(name.clone());
|
.lookup_by(name.clone());
|
||||||
item.insert_text(escaped_name);
|
item.insert_text(field_with_receiver(receiver.as_ref(), &escaped_name));
|
||||||
if let Some(receiver) = &dot_access.receiver {
|
if let Some(receiver) = &dot_access.receiver {
|
||||||
if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) {
|
if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) {
|
||||||
if let Some(ref_match) = compute_ref_match(ctx.completion, ty) {
|
if let Some(ref_match) = compute_ref_match(ctx.completion, ty) {
|
||||||
|
@ -143,6 +143,11 @@ pub(crate) fn render_field(
|
||||||
item.build()
|
item.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn field_with_receiver(receiver: Option<&hir::Name>, field_name: &str) -> SmolStr {
|
||||||
|
receiver
|
||||||
|
.map_or_else(|| field_name.into(), |receiver| format!("{}.{}", receiver, field_name).into())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn render_tuple_field(
|
pub(crate) fn render_tuple_field(
|
||||||
ctx: RenderContext<'_>,
|
ctx: RenderContext<'_>,
|
||||||
receiver: Option<hir::Name>,
|
receiver: Option<hir::Name>,
|
||||||
|
@ -152,7 +157,7 @@ pub(crate) fn render_tuple_field(
|
||||||
let mut item = CompletionItem::new(
|
let mut item = CompletionItem::new(
|
||||||
SymbolKind::Field,
|
SymbolKind::Field,
|
||||||
ctx.source_range(),
|
ctx.source_range(),
|
||||||
receiver.map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)),
|
field_with_receiver(receiver.as_ref(), &field.to_string()),
|
||||||
);
|
);
|
||||||
item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string());
|
item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string());
|
||||||
item.build()
|
item.build()
|
||||||
|
@ -1873,6 +1878,35 @@ impl r#trait for r#struct { type t$0 }
|
||||||
struct r#struct {}
|
struct r#struct {}
|
||||||
trait r#trait { type r#type; }
|
trait r#trait { type r#type; }
|
||||||
impl r#trait for r#struct { type r#type = $0; }
|
impl r#trait for r#struct { type r#type = $0; }
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn field_access_includes_self() {
|
||||||
|
check_edit(
|
||||||
|
"length",
|
||||||
|
r#"
|
||||||
|
struct S {
|
||||||
|
length: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl S {
|
||||||
|
fn some_fn(&self) {
|
||||||
|
let l = len$0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct S {
|
||||||
|
length: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl S {
|
||||||
|
fn some_fn(&self) {
|
||||||
|
let l = self.length
|
||||||
|
}
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,6 +292,37 @@ fn x(a: S) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_record_expr_in_assignee_expr() {
|
||||||
|
check_diagnostics(
|
||||||
|
r"
|
||||||
|
struct S { s: usize, t: usize }
|
||||||
|
struct S2 { s: S, t: () }
|
||||||
|
struct T(S);
|
||||||
|
fn regular(a: S) {
|
||||||
|
let s;
|
||||||
|
S { s, .. } = a;
|
||||||
|
}
|
||||||
|
fn nested(a: S2) {
|
||||||
|
let s;
|
||||||
|
S2 { s: S { s, .. }, .. } = a;
|
||||||
|
}
|
||||||
|
fn in_tuple(a: (S,)) {
|
||||||
|
let s;
|
||||||
|
(S { s, .. },) = a;
|
||||||
|
}
|
||||||
|
fn in_array(a: [S;1]) {
|
||||||
|
let s;
|
||||||
|
[S { s, .. },] = a;
|
||||||
|
}
|
||||||
|
fn in_tuple_struct(a: T) {
|
||||||
|
let s;
|
||||||
|
T(S { s, .. }) = a;
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn range_mapping_out_of_macros() {
|
fn range_mapping_out_of_macros() {
|
||||||
check_fix(
|
check_fix(
|
||||||
|
|
|
@ -60,9 +60,9 @@ pub fn load_workspace(
|
||||||
|
|
||||||
let proc_macro_client = if load_config.with_proc_macro {
|
let proc_macro_client = if load_config.with_proc_macro {
|
||||||
let path = AbsPathBuf::assert(std::env::current_exe()?);
|
let path = AbsPathBuf::assert(std::env::current_exe()?);
|
||||||
Some(ProcMacroServer::spawn(path, &["proc-macro"]).unwrap())
|
Ok(ProcMacroServer::spawn(path, &["proc-macro"]).unwrap())
|
||||||
} else {
|
} else {
|
||||||
None
|
Err("proc macro server not started".to_owned())
|
||||||
};
|
};
|
||||||
|
|
||||||
let crate_graph = ws.to_crate_graph(
|
let crate_graph = ws.to_crate_graph(
|
||||||
|
@ -89,7 +89,7 @@ pub fn load_workspace(
|
||||||
if load_config.prefill_caches {
|
if load_config.prefill_caches {
|
||||||
host.analysis().parallel_prime_caches(1, |_| {})?;
|
host.analysis().parallel_prime_caches(1, |_| {})?;
|
||||||
}
|
}
|
||||||
Ok((host, vfs, proc_macro_client))
|
Ok((host, vfs, proc_macro_client.ok()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_crate_graph(
|
fn load_crate_graph(
|
||||||
|
|
|
@ -61,7 +61,7 @@ pub(crate) struct GlobalState {
|
||||||
pub(crate) proc_macro_changed: bool,
|
pub(crate) proc_macro_changed: bool,
|
||||||
pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
|
pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
|
||||||
pub(crate) source_root_config: SourceRootConfig,
|
pub(crate) source_root_config: SourceRootConfig,
|
||||||
pub(crate) proc_macro_client: Option<ProcMacroServer>,
|
pub(crate) proc_macro_clients: Vec<Result<ProcMacroServer, String>>,
|
||||||
|
|
||||||
pub(crate) flycheck: Vec<FlycheckHandle>,
|
pub(crate) flycheck: Vec<FlycheckHandle>,
|
||||||
pub(crate) flycheck_sender: Sender<flycheck::Message>,
|
pub(crate) flycheck_sender: Sender<flycheck::Message>,
|
||||||
|
@ -151,7 +151,7 @@ impl GlobalState {
|
||||||
proc_macro_changed: false,
|
proc_macro_changed: false,
|
||||||
last_reported_status: None,
|
last_reported_status: None,
|
||||||
source_root_config: SourceRootConfig::default(),
|
source_root_config: SourceRootConfig::default(),
|
||||||
proc_macro_client: None,
|
proc_macro_clients: vec![],
|
||||||
|
|
||||||
flycheck: Vec::new(),
|
flycheck: Vec::new(),
|
||||||
flycheck_sender,
|
flycheck_sender,
|
||||||
|
|
|
@ -44,7 +44,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> {
|
pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> {
|
||||||
state.proc_macro_client = None;
|
state.proc_macro_clients.clear();
|
||||||
state.proc_macro_changed = false;
|
state.proc_macro_changed = false;
|
||||||
state.fetch_workspaces_queue.request_op("reload workspace request".to_string());
|
state.fetch_workspaces_queue.request_op("reload workspace request".to_string());
|
||||||
state.fetch_build_data_queue.request_op("reload workspace request".to_string());
|
state.fetch_build_data_queue.request_op("reload workspace request".to_string());
|
||||||
|
|
|
@ -303,18 +303,21 @@ impl GlobalState {
|
||||||
let files_config = self.config.files();
|
let files_config = self.config.files();
|
||||||
let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude);
|
let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude);
|
||||||
|
|
||||||
if self.proc_macro_client.is_none() {
|
if self.proc_macro_clients.is_empty() {
|
||||||
if let Some((path, args)) = self.config.proc_macro_srv() {
|
if let Some((path, args)) = self.config.proc_macro_srv() {
|
||||||
match ProcMacroServer::spawn(path.clone(), args) {
|
self.proc_macro_clients = (0..self.workspaces.len())
|
||||||
Ok(it) => self.proc_macro_client = Some(it),
|
.map(|_| {
|
||||||
Err(err) => {
|
ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| {
|
||||||
tracing::error!(
|
let error = format!(
|
||||||
"Failed to run proc_macro_srv from path {}, error: {:?}",
|
"Failed to run proc_macro_srv from path {}, error: {:?}",
|
||||||
path.display(),
|
path.display(),
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
}
|
tracing::error!(error);
|
||||||
}
|
error
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,15 +334,7 @@ impl GlobalState {
|
||||||
|
|
||||||
// Create crate graph from all the workspaces
|
// Create crate graph from all the workspaces
|
||||||
let crate_graph = {
|
let crate_graph = {
|
||||||
let proc_macro_client = self.proc_macro_client.as_ref();
|
|
||||||
let dummy_replacements = self.config.dummy_replacements();
|
let dummy_replacements = self.config.dummy_replacements();
|
||||||
let mut load_proc_macro = move |crate_name: &str, path: &AbsPath| {
|
|
||||||
load_proc_macro(
|
|
||||||
proc_macro_client,
|
|
||||||
path,
|
|
||||||
dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let vfs = &mut self.vfs.write().0;
|
let vfs = &mut self.vfs.write().0;
|
||||||
let loader = &mut self.loader;
|
let loader = &mut self.loader;
|
||||||
|
@ -359,7 +354,15 @@ impl GlobalState {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut crate_graph = CrateGraph::default();
|
let mut crate_graph = CrateGraph::default();
|
||||||
for ws in self.workspaces.iter() {
|
for (idx, ws) in self.workspaces.iter().enumerate() {
|
||||||
|
let proc_macro_client = self.proc_macro_clients[idx].as_ref();
|
||||||
|
let mut load_proc_macro = move |crate_name: &str, path: &AbsPath| {
|
||||||
|
load_proc_macro(
|
||||||
|
proc_macro_client,
|
||||||
|
path,
|
||||||
|
dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(),
|
||||||
|
)
|
||||||
|
};
|
||||||
crate_graph.extend(ws.to_crate_graph(&mut load_proc_macro, &mut load));
|
crate_graph.extend(ws.to_crate_graph(&mut load_proc_macro, &mut load));
|
||||||
}
|
}
|
||||||
crate_graph
|
crate_graph
|
||||||
|
@ -536,14 +539,14 @@ impl SourceRootConfig {
|
||||||
/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
|
/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
|
||||||
/// with an identity dummy expander.
|
/// with an identity dummy expander.
|
||||||
pub(crate) fn load_proc_macro(
|
pub(crate) fn load_proc_macro(
|
||||||
server: Option<&ProcMacroServer>,
|
server: Result<&ProcMacroServer, &String>,
|
||||||
path: &AbsPath,
|
path: &AbsPath,
|
||||||
dummy_replace: &[Box<str>],
|
dummy_replace: &[Box<str>],
|
||||||
) -> ProcMacroLoadResult {
|
) -> ProcMacroLoadResult {
|
||||||
let res: Result<Vec<_>, String> = (|| {
|
let res: Result<Vec<_>, String> = (|| {
|
||||||
let dylib = MacroDylib::new(path.to_path_buf())
|
let dylib = MacroDylib::new(path.to_path_buf())
|
||||||
.map_err(|io| format!("Proc-macro dylib loading failed: {io}"))?;
|
.map_err(|io| format!("Proc-macro dylib loading failed: {io}"))?;
|
||||||
let server = server.ok_or_else(|| format!("Proc-macro server not started"))?;
|
let server = server.map_err(ToOwned::to_owned)?;
|
||||||
let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?;
|
let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?;
|
||||||
if vec.is_empty() {
|
if vec.is_empty() {
|
||||||
return Err("proc macro library returned no proc macros".to_string());
|
return Err("proc macro library returned no proc macros".to_string());
|
||||||
|
|
Loading…
Reference in a new issue