diff options
Diffstat (limited to 'crates')
45 files changed, 945 insertions, 734 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 6233bca83..d443b124c 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -52,7 +52,9 @@ use hir_def::{ | |||
52 | }; | 52 | }; |
53 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; | 53 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; |
54 | use hir_ty::{ | 54 | use hir_ty::{ |
55 | autoderef, could_unify, | 55 | autoderef, |
56 | consteval::ConstExt, | ||
57 | could_unify, | ||
56 | method_resolution::{self, def_crates, TyFingerprint}, | 58 | method_resolution::{self, def_crates, TyFingerprint}, |
57 | primitive::UintTy, | 59 | primitive::UintTy, |
58 | subst_prefix, | 60 | subst_prefix, |
@@ -1914,6 +1916,7 @@ impl Type { | |||
1914 | substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go) | 1916 | substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go) |
1915 | } | 1917 | } |
1916 | 1918 | ||
1919 | TyKind::Array(_ty, len) if len.is_unknown() => true, | ||
1917 | TyKind::Array(ty, _) | 1920 | TyKind::Array(ty, _) |
1918 | | TyKind::Slice(ty) | 1921 | | TyKind::Slice(ty) |
1919 | | TyKind::Raw(_, ty) | 1922 | | TyKind::Raw(_, ty) |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 9f278d35b..2a7e0205f 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -1006,23 +1006,27 @@ impl From<ast::BinOp> for BinaryOp { | |||
1006 | impl From<ast::LiteralKind> for Literal { | 1006 | impl From<ast::LiteralKind> for Literal { |
1007 | fn from(ast_lit_kind: ast::LiteralKind) -> Self { | 1007 | fn from(ast_lit_kind: ast::LiteralKind) -> Self { |
1008 | match ast_lit_kind { | 1008 | match ast_lit_kind { |
1009 | // FIXME: these should have actual values filled in, but unsure on perf impact | ||
1009 | LiteralKind::IntNumber(lit) => { | 1010 | LiteralKind::IntNumber(lit) => { |
1010 | if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { | 1011 | if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { |
1011 | return Literal::Float(Default::default(), builtin); | 1012 | return Literal::Float(Default::default(), builtin); |
1012 | } else if let builtin @ Some(_) = | 1013 | } else if let builtin @ Some(_) = |
1013 | lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it)) | 1014 | lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it)) |
1014 | { | 1015 | { |
1015 | Literal::Int(Default::default(), builtin) | 1016 | Literal::Int(lit.value().unwrap_or(0) as i128, builtin) |
1016 | } else { | 1017 | } else { |
1017 | let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(&it)); | 1018 | let builtin = lit.suffix().and_then(|it| BuiltinUint::from_suffix(&it)); |
1018 | Literal::Uint(Default::default(), builtin) | 1019 | Literal::Uint(lit.value().unwrap_or(0), builtin) |
1019 | } | 1020 | } |
1020 | } | 1021 | } |
1021 | LiteralKind::FloatNumber(lit) => { | 1022 | LiteralKind::FloatNumber(lit) => { |
1022 | let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it)); | 1023 | let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it)); |
1023 | Literal::Float(Default::default(), ty) | 1024 | Literal::Float(Default::default(), ty) |
1024 | } | 1025 | } |
1025 | LiteralKind::ByteString(_) => Literal::ByteString(Default::default()), | 1026 | LiteralKind::ByteString(bs) => { |
1027 | let text = bs.value().map(Vec::from).unwrap_or_else(Default::default); | ||
1028 | Literal::ByteString(text) | ||
1029 | } | ||
1026 | LiteralKind::String(_) => Literal::String(Default::default()), | 1030 | LiteralKind::String(_) => Literal::String(Default::default()), |
1027 | LiteralKind::Byte => Literal::Uint(Default::default(), Some(BuiltinUint::U8)), | 1031 | LiteralKind::Byte => Literal::Uint(Default::default(), Some(BuiltinUint::U8)), |
1028 | LiteralKind::Bool(val) => Literal::Bool(val), | 1032 | LiteralKind::Bool(val) => Literal::Bool(val), |
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs index 0c3b41080..2ba619d23 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs | |||
@@ -43,8 +43,8 @@ pub enum Literal { | |||
43 | ByteString(Vec<u8>), | 43 | ByteString(Vec<u8>), |
44 | Char(char), | 44 | Char(char), |
45 | Bool(bool), | 45 | Bool(bool), |
46 | Int(u64, Option<BuiltinInt>), | 46 | Int(i128, Option<BuiltinInt>), |
47 | Uint(u64, Option<BuiltinUint>), | 47 | Uint(u128, Option<BuiltinUint>), |
48 | Float(u64, Option<BuiltinFloat>), // FIXME: f64 is not Eq | 48 | Float(u64, Option<BuiltinFloat>), // FIXME: f64 is not Eq |
49 | } | 49 | } |
50 | 50 | ||
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs index ea29da5da..9e44547cb 100644 --- a/crates/hir_def/src/type_ref.rs +++ b/crates/hir_def/src/type_ref.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | //! be directly created from an ast::TypeRef, without further queries. | 2 | //! be directly created from an ast::TypeRef, without further queries. |
3 | 3 | ||
4 | use hir_expand::{name::Name, AstId, InFile}; | 4 | use hir_expand::{name::Name, AstId, InFile}; |
5 | use std::convert::TryInto; | ||
5 | use syntax::ast; | 6 | use syntax::ast; |
6 | 7 | ||
7 | use crate::{body::LowerCtx, path::Path}; | 8 | use crate::{body::LowerCtx, path::Path}; |
@@ -79,7 +80,9 @@ pub enum TypeRef { | |||
79 | Path(Path), | 80 | Path(Path), |
80 | RawPtr(Box<TypeRef>, Mutability), | 81 | RawPtr(Box<TypeRef>, Mutability), |
81 | Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability), | 82 | Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability), |
82 | Array(Box<TypeRef> /*, Expr*/), | 83 | // FIXME: for full const generics, the latter element (length) here is going to have to be an |
84 | // expression that is further lowered later in hir_ty. | ||
85 | Array(Box<TypeRef>, ConstScalar), | ||
83 | Slice(Box<TypeRef>), | 86 | Slice(Box<TypeRef>), |
84 | /// A fn pointer. Last element of the vector is the return type. | 87 | /// A fn pointer. Last element of the vector is the return type. |
85 | Fn(Vec<TypeRef>, bool /*varargs*/), | 88 | Fn(Vec<TypeRef>, bool /*varargs*/), |
@@ -140,7 +143,16 @@ impl TypeRef { | |||
140 | TypeRef::RawPtr(Box::new(inner_ty), mutability) | 143 | TypeRef::RawPtr(Box::new(inner_ty), mutability) |
141 | } | 144 | } |
142 | ast::Type::ArrayType(inner) => { | 145 | ast::Type::ArrayType(inner) => { |
143 | TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) | 146 | // FIXME: This is a hack. We should probably reuse the machinery of |
147 | // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the | ||
148 | // `hir_ty` level, which would allow knowing the type of: | ||
149 | // let v: [u8; 2 + 2] = [0u8; 4]; | ||
150 | let len = inner | ||
151 | .expr() | ||
152 | .map(ConstScalar::usize_from_literal_expr) | ||
153 | .unwrap_or(ConstScalar::Unknown); | ||
154 | |||
155 | TypeRef::Array(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty())), len) | ||
144 | } | 156 | } |
145 | ast::Type::SliceType(inner) => { | 157 | ast::Type::SliceType(inner) => { |
146 | TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) | 158 | TypeRef::Slice(Box::new(TypeRef::from_ast_opt(&ctx, inner.ty()))) |
@@ -212,7 +224,7 @@ impl TypeRef { | |||
212 | } | 224 | } |
213 | TypeRef::RawPtr(type_ref, _) | 225 | TypeRef::RawPtr(type_ref, _) |
214 | | TypeRef::Reference(type_ref, ..) | 226 | | TypeRef::Reference(type_ref, ..) |
215 | | TypeRef::Array(type_ref) | 227 | | TypeRef::Array(type_ref, _) |
216 | | TypeRef::Slice(type_ref) => go(&type_ref, f), | 228 | | TypeRef::Slice(type_ref) => go(&type_ref, f), |
217 | TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { | 229 | TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { |
218 | for bound in bounds { | 230 | for bound in bounds { |
@@ -298,3 +310,58 @@ impl TypeBound { | |||
298 | } | 310 | } |
299 | } | 311 | } |
300 | } | 312 | } |
313 | |||
314 | /// A concrete constant value | ||
315 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
316 | pub enum ConstScalar { | ||
317 | // for now, we only support the trivial case of constant evaluating the length of an array | ||
318 | // Note that this is u64 because the target usize may be bigger than our usize | ||
319 | Usize(u64), | ||
320 | |||
321 | /// Case of an unknown value that rustc might know but we don't | ||
322 | // FIXME: this is a hack to get around chalk not being able to represent unevaluatable | ||
323 | // constants | ||
324 | // https://github.com/rust-analyzer/rust-analyzer/pull/8813#issuecomment-840679177 | ||
325 | // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348 | ||
326 | Unknown, | ||
327 | } | ||
328 | |||
329 | impl std::fmt::Display for ConstScalar { | ||
330 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { | ||
331 | match self { | ||
332 | ConstScalar::Usize(us) => write!(fmt, "{}", us), | ||
333 | ConstScalar::Unknown => write!(fmt, "_"), | ||
334 | } | ||
335 | } | ||
336 | } | ||
337 | |||
338 | impl ConstScalar { | ||
339 | /// Gets a target usize out of the ConstScalar | ||
340 | pub fn as_usize(&self) -> Option<u64> { | ||
341 | match self { | ||
342 | &ConstScalar::Usize(us) => Some(us), | ||
343 | _ => None, | ||
344 | } | ||
345 | } | ||
346 | |||
347 | // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this | ||
348 | // parse stage. | ||
349 | fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar { | ||
350 | match expr { | ||
351 | ast::Expr::Literal(lit) => { | ||
352 | let lkind = lit.kind(); | ||
353 | match lkind { | ||
354 | ast::LiteralKind::IntNumber(num) | ||
355 | if num.suffix() == None || num.suffix() == Some("usize") => | ||
356 | { | ||
357 | num.value().and_then(|v| v.try_into().ok()) | ||
358 | } | ||
359 | _ => None, | ||
360 | } | ||
361 | } | ||
362 | _ => None, | ||
363 | } | ||
364 | .map(ConstScalar::Usize) | ||
365 | .unwrap_or(ConstScalar::Unknown) | ||
366 | } | ||
367 | } | ||
diff --git a/crates/hir_ty/src/consteval.rs b/crates/hir_ty/src/consteval.rs new file mode 100644 index 000000000..e3ceb3d62 --- /dev/null +++ b/crates/hir_ty/src/consteval.rs | |||
@@ -0,0 +1,56 @@ | |||
1 | //! Constant evaluation details | ||
2 | |||
3 | use std::convert::TryInto; | ||
4 | |||
5 | use hir_def::{ | ||
6 | builtin_type::BuiltinUint, | ||
7 | expr::{Expr, Literal}, | ||
8 | type_ref::ConstScalar, | ||
9 | }; | ||
10 | |||
11 | use crate::{Const, ConstData, ConstValue, Interner, TyKind}; | ||
12 | |||
13 | /// Extension trait for [`Const`] | ||
14 | pub trait ConstExt { | ||
15 | /// Is a [`Const`] unknown? | ||
16 | fn is_unknown(&self) -> bool; | ||
17 | } | ||
18 | |||
19 | impl ConstExt for Const { | ||
20 | fn is_unknown(&self) -> bool { | ||
21 | match self.data(&Interner).value { | ||
22 | // interned Unknown | ||
23 | chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { | ||
24 | interned: ConstScalar::Unknown, | ||
25 | }) => true, | ||
26 | |||
27 | // interned concrete anything else | ||
28 | chalk_ir::ConstValue::Concrete(..) => false, | ||
29 | |||
30 | _ => { | ||
31 | log::error!("is_unknown was called on a non-concrete constant value! {:?}", self); | ||
32 | true | ||
33 | } | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | |||
38 | // FIXME: support more than just evaluating literals | ||
39 | pub fn eval_usize(expr: &Expr) -> Option<u64> { | ||
40 | match expr { | ||
41 | Expr::Literal(Literal::Uint(v, None)) | ||
42 | | Expr::Literal(Literal::Uint(v, Some(BuiltinUint::Usize))) => (*v).try_into().ok(), | ||
43 | _ => None, | ||
44 | } | ||
45 | } | ||
46 | |||
47 | /// Interns a possibly-unknown target usize | ||
48 | pub fn usize_const(value: Option<u64>) -> Const { | ||
49 | ConstData { | ||
50 | ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner), | ||
51 | value: ConstValue::Concrete(chalk_ir::ConcreteConst { | ||
52 | interned: value.map(|value| ConstScalar::Usize(value)).unwrap_or(ConstScalar::Unknown), | ||
53 | }), | ||
54 | } | ||
55 | .intern(&Interner) | ||
56 | } | ||
diff --git a/crates/hir_ty/src/consts.rs b/crates/hir_ty/src/consts.rs deleted file mode 100644 index 0044b1cff..000000000 --- a/crates/hir_ty/src/consts.rs +++ /dev/null | |||
@@ -1,21 +0,0 @@ | |||
1 | //! Handling of concrete const values | ||
2 | |||
3 | /// A concrete constant value | ||
4 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
5 | pub enum ConstScalar { | ||
6 | // for now, we only support the trivial case of constant evaluating the length of an array | ||
7 | // Note that this is u64 because the target usize may be bigger than our usize | ||
8 | Usize(u64), | ||
9 | |||
10 | /// Case of an unknown value that rustc might know but we don't | ||
11 | Unknown, | ||
12 | } | ||
13 | |||
14 | impl std::fmt::Display for ConstScalar { | ||
15 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { | ||
16 | match self { | ||
17 | ConstScalar::Usize(us) => write!(fmt, "{}", us), | ||
18 | ConstScalar::Unknown => write!(fmt, "_"), | ||
19 | } | ||
20 | } | ||
21 | } | ||
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 8a4296697..7bbd1a1f7 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs | |||
@@ -962,11 +962,10 @@ impl HirDisplay for TypeRef { | |||
962 | write!(f, "{}", mutability)?; | 962 | write!(f, "{}", mutability)?; |
963 | inner.hir_fmt(f)?; | 963 | inner.hir_fmt(f)?; |
964 | } | 964 | } |
965 | TypeRef::Array(inner) => { | 965 | TypeRef::Array(inner, len) => { |
966 | write!(f, "[")?; | 966 | write!(f, "[")?; |
967 | inner.hir_fmt(f)?; | 967 | inner.hir_fmt(f)?; |
968 | // FIXME: Array length? | 968 | write!(f, "; {}]", len)?; |
969 | write!(f, "; _]")?; | ||
970 | } | 969 | } |
971 | TypeRef::Slice(inner) => { | 970 | TypeRef::Slice(inner) => { |
972 | write!(f, "[")?; | 971 | write!(f, "[")?; |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 2178ffd07..b6b5a1b75 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use std::iter::{repeat, repeat_with}; | 3 | use std::iter::{repeat, repeat_with}; |
4 | use std::{mem, sync::Arc}; | 4 | use std::{mem, sync::Arc}; |
5 | 5 | ||
6 | use chalk_ir::{cast::Cast, fold::Shift, ConstData, Mutability, TyVariableKind}; | 6 | use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind}; |
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, | 8 | expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, |
9 | path::{GenericArg, GenericArgs}, | 9 | path::{GenericArg, GenericArgs}, |
@@ -15,9 +15,7 @@ use stdx::always; | |||
15 | use syntax::ast::RangeOp; | 15 | use syntax::ast::RangeOp; |
16 | 16 | ||
17 | use crate::{ | 17 | use crate::{ |
18 | autoderef, | 18 | autoderef, consteval, |
19 | consts::ConstScalar, | ||
20 | dummy_usize_const, | ||
21 | lower::lower_to_chalk_mutability, | 19 | lower::lower_to_chalk_mutability, |
22 | mapping::from_chalk, | 20 | mapping::from_chalk, |
23 | method_resolution, op, | 21 | method_resolution, op, |
@@ -25,7 +23,7 @@ use crate::{ | |||
25 | static_lifetime, to_chalk_trait_id, | 23 | static_lifetime, to_chalk_trait_id, |
26 | traits::FnTrait, | 24 | traits::FnTrait, |
27 | utils::{generics, Generics}, | 25 | utils::{generics, Generics}, |
28 | AdtId, Binders, CallableDefId, ConstValue, FnPointer, FnSig, FnSubst, InEnvironment, Interner, | 26 | AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner, |
29 | ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, | 27 | ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, |
30 | }; | 28 | }; |
31 | 29 | ||
@@ -724,7 +722,7 @@ impl<'a> InferenceContext<'a> { | |||
724 | for expr in items.iter() { | 722 | for expr in items.iter() { |
725 | self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone())); | 723 | self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone())); |
726 | } | 724 | } |
727 | Some(items.len()) | 725 | Some(items.len() as u64) |
728 | } | 726 | } |
729 | Array::Repeat { initializer, repeat } => { | 727 | Array::Repeat { initializer, repeat } => { |
730 | self.infer_expr_coerce( | 728 | self.infer_expr_coerce( |
@@ -737,20 +735,13 @@ impl<'a> InferenceContext<'a> { | |||
737 | TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), | 735 | TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), |
738 | ), | 736 | ), |
739 | ); | 737 | ); |
740 | // FIXME: support length for Repeat array expressions | 738 | |
741 | None | 739 | let repeat_expr = &self.body.exprs[*repeat]; |
740 | consteval::eval_usize(repeat_expr) | ||
742 | } | 741 | } |
743 | }; | 742 | }; |
744 | 743 | ||
745 | let cd = ConstData { | 744 | TyKind::Array(elem_ty, consteval::usize_const(len)).intern(&Interner) |
746 | ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), | ||
747 | value: ConstValue::Concrete(chalk_ir::ConcreteConst { | ||
748 | interned: len | ||
749 | .map(|len| ConstScalar::Usize(len as u64)) | ||
750 | .unwrap_or(ConstScalar::Unknown), | ||
751 | }), | ||
752 | }; | ||
753 | TyKind::Array(elem_ty, cd.intern(&Interner)).intern(&Interner) | ||
754 | } | 745 | } |
755 | Expr::Literal(lit) => match lit { | 746 | Expr::Literal(lit) => match lit { |
756 | Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner), | 747 | Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner), |
@@ -758,11 +749,12 @@ impl<'a> InferenceContext<'a> { | |||
758 | TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(&Interner)) | 749 | TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(&Interner)) |
759 | .intern(&Interner) | 750 | .intern(&Interner) |
760 | } | 751 | } |
761 | Literal::ByteString(..) => { | 752 | Literal::ByteString(bs) => { |
762 | let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner); | 753 | let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner); |
763 | 754 | ||
764 | let array_type = | 755 | let len = consteval::usize_const(Some(bs.len() as u64)); |
765 | TyKind::Array(byte_type, dummy_usize_const()).intern(&Interner); | 756 | |
757 | let array_type = TyKind::Array(byte_type, len).intern(&Interner); | ||
766 | TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner) | 758 | TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner) |
767 | } | 759 | } |
768 | Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner), | 760 | Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner), |
diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs index 4cbc9cd4f..7b4119747 100644 --- a/crates/hir_ty/src/interner.rs +++ b/crates/hir_ty/src/interner.rs | |||
@@ -1,11 +1,12 @@ | |||
1 | //! Implementation of the Chalk `Interner` trait, which allows customizing the | 1 | //! Implementation of the Chalk `Interner` trait, which allows customizing the |
2 | //! representation of the various objects Chalk deals with (types, goals etc.). | 2 | //! representation of the various objects Chalk deals with (types, goals etc.). |
3 | 3 | ||
4 | use crate::{chalk_db, consts::ConstScalar, tls, GenericArg}; | 4 | use crate::{chalk_db, tls, GenericArg}; |
5 | use base_db::salsa::InternId; | 5 | use base_db::salsa::InternId; |
6 | use chalk_ir::{Goal, GoalData}; | 6 | use chalk_ir::{Goal, GoalData}; |
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | intern::{impl_internable, InternStorage, Internable, Interned}, | 8 | intern::{impl_internable, InternStorage, Internable, Interned}, |
9 | type_ref::ConstScalar, | ||
9 | TypeAliasId, | 10 | TypeAliasId, |
10 | }; | 11 | }; |
11 | use smallvec::SmallVec; | 12 | use smallvec::SmallVec; |
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index d23eff513..15b61bedc 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -10,9 +10,9 @@ mod autoderef; | |||
10 | mod builder; | 10 | mod builder; |
11 | mod chalk_db; | 11 | mod chalk_db; |
12 | mod chalk_ext; | 12 | mod chalk_ext; |
13 | pub mod consteval; | ||
13 | mod infer; | 14 | mod infer; |
14 | mod interner; | 15 | mod interner; |
15 | mod consts; | ||
16 | mod lower; | 16 | mod lower; |
17 | mod mapping; | 17 | mod mapping; |
18 | mod op; | 18 | mod op; |
@@ -38,9 +38,13 @@ use chalk_ir::{ | |||
38 | interner::HasInterner, | 38 | interner::HasInterner, |
39 | UintTy, | 39 | UintTy, |
40 | }; | 40 | }; |
41 | use hir_def::{expr::ExprId, type_ref::Rawness, TypeParamId}; | 41 | use hir_def::{ |
42 | expr::ExprId, | ||
43 | type_ref::{ConstScalar, Rawness}, | ||
44 | TypeParamId, | ||
45 | }; | ||
42 | 46 | ||
43 | use crate::{consts::ConstScalar, db::HirDatabase, display::HirDisplay, utils::generics}; | 47 | use crate::{db::HirDatabase, display::HirDisplay, utils::generics}; |
44 | 48 | ||
45 | pub use autoderef::autoderef; | 49 | pub use autoderef::autoderef; |
46 | pub use builder::TyBuilder; | 50 | pub use builder::TyBuilder; |
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index 9751b45e4..bd8bb6028 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -29,8 +29,8 @@ use stdx::impl_from; | |||
29 | use syntax::ast; | 29 | use syntax::ast; |
30 | 30 | ||
31 | use crate::{ | 31 | use crate::{ |
32 | consteval, | ||
32 | db::HirDatabase, | 33 | db::HirDatabase, |
33 | dummy_usize_const, | ||
34 | mapping::ToChalk, | 34 | mapping::ToChalk, |
35 | static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, | 35 | static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, |
36 | utils::{ | 36 | utils::{ |
@@ -172,11 +172,12 @@ impl<'a> TyLoweringContext<'a> { | |||
172 | let inner_ty = self.lower_ty(inner); | 172 | let inner_ty = self.lower_ty(inner); |
173 | TyKind::Raw(lower_to_chalk_mutability(*mutability), inner_ty).intern(&Interner) | 173 | TyKind::Raw(lower_to_chalk_mutability(*mutability), inner_ty).intern(&Interner) |
174 | } | 174 | } |
175 | TypeRef::Array(inner) => { | 175 | TypeRef::Array(inner, len) => { |
176 | let inner_ty = self.lower_ty(inner); | 176 | let inner_ty = self.lower_ty(inner); |
177 | // FIXME: we don't have length info here because we don't store an expression for | 177 | |
178 | // the length | 178 | let const_len = consteval::usize_const(len.as_usize()); |
179 | TyKind::Array(inner_ty, dummy_usize_const()).intern(&Interner) | 179 | |
180 | TyKind::Array(inner_ty, const_len).intern(&Interner) | ||
180 | } | 181 | } |
181 | TypeRef::Slice(inner) => { | 182 | TypeRef::Slice(inner) => { |
182 | let inner_ty = self.lower_ty(inner); | 183 | let inner_ty = self.lower_ty(inner); |
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index aad3d610e..190471069 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs | |||
@@ -64,42 +64,42 @@ fn coerce_places() { | |||
64 | 81..92 '{ loop {} }': T | 64 | 81..92 '{ loop {} }': T |
65 | 83..90 'loop {}': ! | 65 | 83..90 'loop {}': ! |
66 | 88..90 '{}': () | 66 | 88..90 '{}': () |
67 | 121..132 '{ loop {} }': *mut [T; _] | 67 | 121..132 '{ loop {} }': *mut [T; 2] |
68 | 123..130 'loop {}': ! | 68 | 123..130 'loop {}': ! |
69 | 128..130 '{}': () | 69 | 128..130 '{}': () |
70 | 159..172 '{ gen() }': *mut [U] | 70 | 159..172 '{ gen() }': *mut [U] |
71 | 165..168 'gen': fn gen<U>() -> *mut [U; _] | 71 | 165..168 'gen': fn gen<U>() -> *mut [U; 2] |
72 | 165..170 'gen()': *mut [U; _] | 72 | 165..170 'gen()': *mut [U; 2] |
73 | 185..419 '{ ...rr); }': () | 73 | 185..419 '{ ...rr); }': () |
74 | 195..198 'arr': &[u8; _] | 74 | 195..198 'arr': &[u8; 1] |
75 | 211..215 '&[1]': &[u8; 1] | 75 | 211..215 '&[1]': &[u8; 1] |
76 | 212..215 '[1]': [u8; 1] | 76 | 212..215 '[1]': [u8; 1] |
77 | 213..214 '1': u8 | 77 | 213..214 '1': u8 |
78 | 226..227 'a': &[u8] | 78 | 226..227 'a': &[u8] |
79 | 236..239 'arr': &[u8; _] | 79 | 236..239 'arr': &[u8; 1] |
80 | 249..250 'b': u8 | 80 | 249..250 'b': u8 |
81 | 253..254 'f': fn f<u8>(&[u8]) -> u8 | 81 | 253..254 'f': fn f<u8>(&[u8]) -> u8 |
82 | 253..259 'f(arr)': u8 | 82 | 253..259 'f(arr)': u8 |
83 | 255..258 'arr': &[u8; _] | 83 | 255..258 'arr': &[u8; 1] |
84 | 269..270 'c': &[u8] | 84 | 269..270 'c': &[u8] |
85 | 279..286 '{ arr }': &[u8] | 85 | 279..286 '{ arr }': &[u8] |
86 | 281..284 'arr': &[u8; _] | 86 | 281..284 'arr': &[u8; 1] |
87 | 296..297 'd': u8 | 87 | 296..297 'd': u8 |
88 | 300..301 'g': fn g<u8>(S<&[u8]>) -> u8 | 88 | 300..301 'g': fn g<u8>(S<&[u8]>) -> u8 |
89 | 300..315 'g(S { a: arr })': u8 | 89 | 300..315 'g(S { a: arr })': u8 |
90 | 302..314 'S { a: arr }': S<&[u8]> | 90 | 302..314 'S { a: arr }': S<&[u8]> |
91 | 309..312 'arr': &[u8; _] | 91 | 309..312 'arr': &[u8; 1] |
92 | 325..326 'e': [&[u8]; _] | 92 | 325..326 'e': [&[u8]; 1] |
93 | 340..345 '[arr]': [&[u8]; 1] | 93 | 340..345 '[arr]': [&[u8]; 1] |
94 | 341..344 'arr': &[u8; _] | 94 | 341..344 'arr': &[u8; 1] |
95 | 355..356 'f': [&[u8]; _] | 95 | 355..356 'f': [&[u8]; 2] |
96 | 370..378 '[arr; 2]': [&[u8]; _] | 96 | 370..378 '[arr; 2]': [&[u8]; 2] |
97 | 371..374 'arr': &[u8; _] | 97 | 371..374 'arr': &[u8; 1] |
98 | 376..377 '2': usize | 98 | 376..377 '2': usize |
99 | 388..389 'g': (&[u8], &[u8]) | 99 | 388..389 'g': (&[u8], &[u8]) |
100 | 406..416 '(arr, arr)': (&[u8], &[u8]) | 100 | 406..416 '(arr, arr)': (&[u8], &[u8]) |
101 | 407..410 'arr': &[u8; _] | 101 | 407..410 'arr': &[u8; 1] |
102 | 412..415 'arr': &[u8; _] | 102 | 412..415 'arr': &[u8; 1] |
103 | "#]], | 103 | "#]], |
104 | ); | 104 | ); |
105 | } | 105 | } |
@@ -159,7 +159,7 @@ fn infer_custom_coerce_unsized() { | |||
159 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} | 159 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} |
160 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} | 160 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} |
161 | "#, | 161 | "#, |
162 | expect![[r" | 162 | expect![[r#" |
163 | 257..258 'x': A<[T]> | 163 | 257..258 'x': A<[T]> |
164 | 278..283 '{ x }': A<[T]> | 164 | 278..283 '{ x }': A<[T]> |
165 | 280..281 'x': A<[T]> | 165 | 280..281 'x': A<[T]> |
@@ -169,23 +169,23 @@ fn infer_custom_coerce_unsized() { | |||
169 | 333..334 'x': C<[T]> | 169 | 333..334 'x': C<[T]> |
170 | 354..359 '{ x }': C<[T]> | 170 | 354..359 '{ x }': C<[T]> |
171 | 356..357 'x': C<[T]> | 171 | 356..357 'x': C<[T]> |
172 | 369..370 'a': A<[u8; _]> | 172 | 369..370 'a': A<[u8; 2]> |
173 | 384..385 'b': B<[u8; _]> | 173 | 384..385 'b': B<[u8; 2]> |
174 | 399..400 'c': C<[u8; _]> | 174 | 399..400 'c': C<[u8; 2]> |
175 | 414..480 '{ ...(c); }': () | 175 | 414..480 '{ ...(c); }': () |
176 | 424..425 'd': A<[{unknown}]> | 176 | 424..425 'd': A<[{unknown}]> |
177 | 428..432 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]> | 177 | 428..432 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]> |
178 | 428..435 'foo1(a)': A<[{unknown}]> | 178 | 428..435 'foo1(a)': A<[{unknown}]> |
179 | 433..434 'a': A<[u8; _]> | 179 | 433..434 'a': A<[u8; 2]> |
180 | 445..446 'e': B<[u8]> | 180 | 445..446 'e': B<[u8]> |
181 | 449..453 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]> | 181 | 449..453 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]> |
182 | 449..456 'foo2(b)': B<[u8]> | 182 | 449..456 'foo2(b)': B<[u8]> |
183 | 454..455 'b': B<[u8; _]> | 183 | 454..455 'b': B<[u8; 2]> |
184 | 466..467 'f': C<[u8]> | 184 | 466..467 'f': C<[u8]> |
185 | 470..474 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]> | 185 | 470..474 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]> |
186 | 470..477 'foo3(c)': C<[u8]> | 186 | 470..477 'foo3(c)': C<[u8]> |
187 | 475..476 'c': C<[u8; _]> | 187 | 475..476 'c': C<[u8; 2]> |
188 | "]], | 188 | "#]], |
189 | ); | 189 | ); |
190 | } | 190 | } |
191 | 191 | ||
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index a0a7b4d0b..787647e9f 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs | |||
@@ -345,19 +345,19 @@ fn infer_pattern_match_arr() { | |||
345 | "#, | 345 | "#, |
346 | expect![[r#" | 346 | expect![[r#" |
347 | 10..179 '{ ... } }': () | 347 | 10..179 '{ ... } }': () |
348 | 20..23 'arr': [f64; _] | 348 | 20..23 'arr': [f64; 2] |
349 | 36..46 '[0.0, 1.0]': [f64; 2] | 349 | 36..46 '[0.0, 1.0]': [f64; 2] |
350 | 37..40 '0.0': f64 | 350 | 37..40 '0.0': f64 |
351 | 42..45 '1.0': f64 | 351 | 42..45 '1.0': f64 |
352 | 52..177 'match ... }': () | 352 | 52..177 'match ... }': () |
353 | 58..61 'arr': [f64; _] | 353 | 58..61 'arr': [f64; 2] |
354 | 72..80 '[1.0, a]': [f64; _] | 354 | 72..80 '[1.0, a]': [f64; 2] |
355 | 73..76 '1.0': f64 | 355 | 73..76 '1.0': f64 |
356 | 73..76 '1.0': f64 | 356 | 73..76 '1.0': f64 |
357 | 78..79 'a': f64 | 357 | 78..79 'a': f64 |
358 | 84..110 '{ ... }': () | 358 | 84..110 '{ ... }': () |
359 | 98..99 'a': f64 | 359 | 98..99 'a': f64 |
360 | 120..126 '[b, c]': [f64; _] | 360 | 120..126 '[b, c]': [f64; 2] |
361 | 121..122 'b': f64 | 361 | 121..122 'b': f64 |
362 | 124..125 'c': f64 | 362 | 124..125 'c': f64 |
363 | 130..171 '{ ... }': () | 363 | 130..171 '{ ... }': () |
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 8b09f2e4a..a9cd42186 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs | |||
@@ -488,23 +488,34 @@ fn infer_literals() { | |||
488 | mod foo {} | 488 | mod foo {} |
489 | "#; | 489 | "#; |
490 | br#"yolo"#; | 490 | br#"yolo"#; |
491 | let a = b"a\x20b\ | ||
492 | c"; | ||
493 | let b = br"g\ | ||
494 | h"; | ||
495 | let c = br#"x"\"yb"#; | ||
491 | } | 496 | } |
492 | "##, | 497 | "##, |
493 | expect![[r##" | 498 | expect![[r##" |
494 | 10..216 '{ ...o"#; }': () | 499 | 18..478 '{ ... }': () |
495 | 16..20 '5i32': i32 | 500 | 32..36 '5i32': i32 |
496 | 26..30 '5f32': f32 | 501 | 50..54 '5f32': f32 |
497 | 36..40 '5f64': f64 | 502 | 68..72 '5f64': f64 |
498 | 46..53 '"hello"': &str | 503 | 86..93 '"hello"': &str |
499 | 59..67 'b"bytes"': &[u8; _] | 504 | 107..115 'b"bytes"': &[u8; 5] |
500 | 73..76 ''c'': char | 505 | 129..132 ''c'': char |
501 | 82..86 'b'b'': u8 | 506 | 146..150 'b'b'': u8 |
502 | 92..96 '3.14': f64 | 507 | 164..168 '3.14': f64 |
503 | 102..106 '5000': i32 | 508 | 182..186 '5000': i32 |
504 | 112..117 'false': bool | 509 | 200..205 'false': bool |
505 | 123..127 'true': bool | 510 | 219..223 'true': bool |
506 | 133..197 'r#" ... "#': &str | 511 | 237..333 'r#" ... "#': &str |
507 | 203..213 'br#"yolo"#': &[u8; _] | 512 | 347..357 'br#"yolo"#': &[u8; 4] |
513 | 375..376 'a': &[u8; 4] | ||
514 | 379..403 'b"a\x2... c"': &[u8; 4] | ||
515 | 421..422 'b': &[u8; 4] | ||
516 | 425..433 'br"g\ h"': &[u8; 4] | ||
517 | 451..452 'c': &[u8; 6] | ||
518 | 455..467 'br#"x"\"yb"#': &[u8; 6] | ||
508 | "##]], | 519 | "##]], |
509 | ); | 520 | ); |
510 | } | 521 | } |
@@ -1260,12 +1271,14 @@ fn infer_array() { | |||
1260 | 1271 | ||
1261 | let b = [a, ["b"]]; | 1272 | let b = [a, ["b"]]; |
1262 | let x: [u8; 0] = []; | 1273 | let x: [u8; 0] = []; |
1274 | // FIXME: requires const evaluation/taking type from rhs somehow | ||
1275 | let y: [u8; 2+2] = [1,2,3,4]; | ||
1263 | } | 1276 | } |
1264 | "#, | 1277 | "#, |
1265 | expect![[r#" | 1278 | expect![[r#" |
1266 | 8..9 'x': &str | 1279 | 8..9 'x': &str |
1267 | 17..18 'y': isize | 1280 | 17..18 'y': isize |
1268 | 27..292 '{ ... []; }': () | 1281 | 27..395 '{ ...,4]; }': () |
1269 | 37..38 'a': [&str; 1] | 1282 | 37..38 'a': [&str; 1] |
1270 | 41..44 '[x]': [&str; 1] | 1283 | 41..44 '[x]': [&str; 1] |
1271 | 42..43 'x': &str | 1284 | 42..43 'x': &str |
@@ -1313,8 +1326,14 @@ fn infer_array() { | |||
1313 | 255..256 'a': [&str; 1] | 1326 | 255..256 'a': [&str; 1] |
1314 | 258..263 '["b"]': [&str; 1] | 1327 | 258..263 '["b"]': [&str; 1] |
1315 | 259..262 '"b"': &str | 1328 | 259..262 '"b"': &str |
1316 | 274..275 'x': [u8; _] | 1329 | 274..275 'x': [u8; 0] |
1317 | 287..289 '[]': [u8; 0] | 1330 | 287..289 '[]': [u8; 0] |
1331 | 368..369 'y': [u8; _] | ||
1332 | 383..392 '[1,2,3,4]': [u8; 4] | ||
1333 | 384..385 '1': u8 | ||
1334 | 386..387 '2': u8 | ||
1335 | 388..389 '3': u8 | ||
1336 | 390..391 '4': u8 | ||
1318 | "#]], | 1337 | "#]], |
1319 | ); | 1338 | ); |
1320 | } | 1339 | } |
@@ -2409,38 +2428,38 @@ fn infer_operator_overload() { | |||
2409 | 320..422 '{ ... }': V2 | 2428 | 320..422 '{ ... }': V2 |
2410 | 334..335 'x': f32 | 2429 | 334..335 'x': f32 |
2411 | 338..342 'self': V2 | 2430 | 338..342 'self': V2 |
2412 | 338..344 'self.0': [f32; _] | 2431 | 338..344 'self.0': [f32; 2] |
2413 | 338..347 'self.0[0]': {unknown} | 2432 | 338..347 'self.0[0]': {unknown} |
2414 | 338..358 'self.0...s.0[0]': f32 | 2433 | 338..358 'self.0...s.0[0]': f32 |
2415 | 345..346 '0': i32 | 2434 | 345..346 '0': i32 |
2416 | 350..353 'rhs': V2 | 2435 | 350..353 'rhs': V2 |
2417 | 350..355 'rhs.0': [f32; _] | 2436 | 350..355 'rhs.0': [f32; 2] |
2418 | 350..358 'rhs.0[0]': {unknown} | 2437 | 350..358 'rhs.0[0]': {unknown} |
2419 | 356..357 '0': i32 | 2438 | 356..357 '0': i32 |
2420 | 372..373 'y': f32 | 2439 | 372..373 'y': f32 |
2421 | 376..380 'self': V2 | 2440 | 376..380 'self': V2 |
2422 | 376..382 'self.0': [f32; _] | 2441 | 376..382 'self.0': [f32; 2] |
2423 | 376..385 'self.0[1]': {unknown} | 2442 | 376..385 'self.0[1]': {unknown} |
2424 | 376..396 'self.0...s.0[1]': f32 | 2443 | 376..396 'self.0...s.0[1]': f32 |
2425 | 383..384 '1': i32 | 2444 | 383..384 '1': i32 |
2426 | 388..391 'rhs': V2 | 2445 | 388..391 'rhs': V2 |
2427 | 388..393 'rhs.0': [f32; _] | 2446 | 388..393 'rhs.0': [f32; 2] |
2428 | 388..396 'rhs.0[1]': {unknown} | 2447 | 388..396 'rhs.0[1]': {unknown} |
2429 | 394..395 '1': i32 | 2448 | 394..395 '1': i32 |
2430 | 406..408 'V2': V2([f32; _]) -> V2 | 2449 | 406..408 'V2': V2([f32; 2]) -> V2 |
2431 | 406..416 'V2([x, y])': V2 | 2450 | 406..416 'V2([x, y])': V2 |
2432 | 409..415 '[x, y]': [f32; 2] | 2451 | 409..415 '[x, y]': [f32; 2] |
2433 | 410..411 'x': f32 | 2452 | 410..411 'x': f32 |
2434 | 413..414 'y': f32 | 2453 | 413..414 'y': f32 |
2435 | 436..519 '{ ... vb; }': () | 2454 | 436..519 '{ ... vb; }': () |
2436 | 446..448 'va': V2 | 2455 | 446..448 'va': V2 |
2437 | 451..453 'V2': V2([f32; _]) -> V2 | 2456 | 451..453 'V2': V2([f32; 2]) -> V2 |
2438 | 451..465 'V2([0.0, 1.0])': V2 | 2457 | 451..465 'V2([0.0, 1.0])': V2 |
2439 | 454..464 '[0.0, 1.0]': [f32; 2] | 2458 | 454..464 '[0.0, 1.0]': [f32; 2] |
2440 | 455..458 '0.0': f32 | 2459 | 455..458 '0.0': f32 |
2441 | 460..463 '1.0': f32 | 2460 | 460..463 '1.0': f32 |
2442 | 475..477 'vb': V2 | 2461 | 475..477 'vb': V2 |
2443 | 480..482 'V2': V2([f32; _]) -> V2 | 2462 | 480..482 'V2': V2([f32; 2]) -> V2 |
2444 | 480..494 'V2([0.0, 1.0])': V2 | 2463 | 480..494 'V2([0.0, 1.0])': V2 |
2445 | 483..493 '[0.0, 1.0]': [f32; 2] | 2464 | 483..493 '[0.0, 1.0]': [f32; 2] |
2446 | 484..487 '0.0': f32 | 2465 | 484..487 '0.0': f32 |
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 47a1455fd..f80cf9879 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs | |||
@@ -3474,3 +3474,100 @@ fn main(){ | |||
3474 | "#]], | 3474 | "#]], |
3475 | ) | 3475 | ) |
3476 | } | 3476 | } |
3477 | |||
3478 | #[test] | ||
3479 | fn array_length() { | ||
3480 | check_infer( | ||
3481 | r#" | ||
3482 | trait T { | ||
3483 | type Output; | ||
3484 | fn do_thing(&self) -> Self::Output; | ||
3485 | } | ||
3486 | |||
3487 | impl T for [u8; 4] { | ||
3488 | type Output = usize; | ||
3489 | fn do_thing(&self) -> Self::Output { | ||
3490 | 2 | ||
3491 | } | ||
3492 | } | ||
3493 | |||
3494 | impl T for [u8; 2] { | ||
3495 | type Output = u8; | ||
3496 | fn do_thing(&self) -> Self::Output { | ||
3497 | 2 | ||
3498 | } | ||
3499 | } | ||
3500 | |||
3501 | fn main() { | ||
3502 | let v = [0u8; 2]; | ||
3503 | let v2 = v.do_thing(); | ||
3504 | let v3 = [0u8; 4]; | ||
3505 | let v4 = v3.do_thing(); | ||
3506 | } | ||
3507 | "#, | ||
3508 | expect![[r#" | ||
3509 | 44..48 'self': &Self | ||
3510 | 133..137 'self': &[u8; 4] | ||
3511 | 155..172 '{ ... }': usize | ||
3512 | 165..166 '2': usize | ||
3513 | 236..240 'self': &[u8; 2] | ||
3514 | 258..275 '{ ... }': u8 | ||
3515 | 268..269 '2': u8 | ||
3516 | 289..392 '{ ...g(); }': () | ||
3517 | 299..300 'v': [u8; 2] | ||
3518 | 303..311 '[0u8; 2]': [u8; 2] | ||
3519 | 304..307 '0u8': u8 | ||
3520 | 309..310 '2': usize | ||
3521 | 321..323 'v2': u8 | ||
3522 | 326..327 'v': [u8; 2] | ||
3523 | 326..338 'v.do_thing()': u8 | ||
3524 | 348..350 'v3': [u8; 4] | ||
3525 | 353..361 '[0u8; 4]': [u8; 4] | ||
3526 | 354..357 '0u8': u8 | ||
3527 | 359..360 '4': usize | ||
3528 | 371..373 'v4': usize | ||
3529 | 376..378 'v3': [u8; 4] | ||
3530 | 376..389 'v3.do_thing()': usize | ||
3531 | "#]], | ||
3532 | ) | ||
3533 | } | ||
3534 | |||
3535 | // FIXME: We should infer the length of the returned array :) | ||
3536 | #[test] | ||
3537 | fn const_generics() { | ||
3538 | check_infer( | ||
3539 | r#" | ||
3540 | trait T { | ||
3541 | type Output; | ||
3542 | fn do_thing(&self) -> Self::Output; | ||
3543 | } | ||
3544 | |||
3545 | impl<const L: usize> T for [u8; L] { | ||
3546 | type Output = [u8; L]; | ||
3547 | fn do_thing(&self) -> Self::Output { | ||
3548 | *self | ||
3549 | } | ||
3550 | } | ||
3551 | |||
3552 | fn main() { | ||
3553 | let v = [0u8; 2]; | ||
3554 | let v2 = v.do_thing(); | ||
3555 | } | ||
3556 | "#, | ||
3557 | expect![[r#" | ||
3558 | 44..48 'self': &Self | ||
3559 | 151..155 'self': &[u8; _] | ||
3560 | 173..194 '{ ... }': [u8; _] | ||
3561 | 183..188 '*self': [u8; _] | ||
3562 | 184..188 'self': &[u8; _] | ||
3563 | 208..260 '{ ...g(); }': () | ||
3564 | 218..219 'v': [u8; 2] | ||
3565 | 222..230 '[0u8; 2]': [u8; 2] | ||
3566 | 223..226 '0u8': u8 | ||
3567 | 228..229 '2': usize | ||
3568 | 240..242 'v2': [u8; _] | ||
3569 | 245..246 'v': [u8; 2] | ||
3570 | 245..257 'v.do_thing()': [u8; _] | ||
3571 | "#]], | ||
3572 | ) | ||
3573 | } | ||
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 273d8cfbb..d5fba6740 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -579,7 +579,7 @@ fn test_fn() { | |||
579 | struct TestStruct { one: i32, two: i64 } | 579 | struct TestStruct { one: i32, two: i64 } |
580 | 580 | ||
581 | fn test_fn() { | 581 | fn test_fn() { |
582 | let s = TestStruct { one: (), two: ()}; | 582 | let s = TestStruct { one: (), two: () }; |
583 | } | 583 | } |
584 | "#, | 584 | "#, |
585 | ); | 585 | ); |
@@ -599,7 +599,7 @@ impl TestStruct { | |||
599 | struct TestStruct { one: i32 } | 599 | struct TestStruct { one: i32 } |
600 | 600 | ||
601 | impl TestStruct { | 601 | impl TestStruct { |
602 | fn test_fn() { let s = Self { one: ()}; } | 602 | fn test_fn() { let s = Self { one: () }; } |
603 | } | 603 | } |
604 | "#, | 604 | "#, |
605 | ); | 605 | ); |
@@ -792,7 +792,7 @@ fn main() { | |||
792 | pub struct Foo { pub a: i32, pub b: i32 } | 792 | pub struct Foo { pub a: i32, pub b: i32 } |
793 | "#, | 793 | "#, |
794 | r#" | 794 | r#" |
795 | fn some(, b: ()) {} | 795 | fn some(, b: () ) {} |
796 | fn items() {} | 796 | fn items() {} |
797 | fn here() {} | 797 | fn here() {} |
798 | 798 | ||
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 15821500f..695b59e27 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -100,11 +100,12 @@ impl DiagnosticWithFix for MissingFields { | |||
100 | let root = sema.db.parse_or_expand(self.file)?; | 100 | let root = sema.db.parse_or_expand(self.file)?; |
101 | let field_list_parent = self.field_list_parent.to_node(&root); | 101 | let field_list_parent = self.field_list_parent.to_node(&root); |
102 | let old_field_list = field_list_parent.record_expr_field_list()?; | 102 | let old_field_list = field_list_parent.record_expr_field_list()?; |
103 | let mut new_field_list = old_field_list.clone(); | 103 | let new_field_list = old_field_list.clone_for_update(); |
104 | for f in self.missed_fields.iter() { | 104 | for f in self.missed_fields.iter() { |
105 | let field = | 105 | let field = |
106 | make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit())); | 106 | make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit())) |
107 | new_field_list = new_field_list.append_field(&field); | 107 | .clone_for_update(); |
108 | new_field_list.add_field(field); | ||
108 | } | 109 | } |
109 | 110 | ||
110 | let edit = { | 111 | let edit = { |
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs index 61dcbb399..c67ccd1a9 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs | |||
@@ -4,7 +4,7 @@ use ide_assists::utils::extract_trivial_expression; | |||
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use syntax::{ | 5 | use syntax::{ |
6 | algo::non_trivia_sibling, | 6 | algo::non_trivia_sibling, |
7 | ast::{self, AstNode, AstToken}, | 7 | ast::{self, AstNode, AstToken, IsString}, |
8 | Direction, NodeOrToken, SourceFile, | 8 | Direction, NodeOrToken, SourceFile, |
9 | SyntaxKind::{self, USE_TREE, WHITESPACE}, | 9 | SyntaxKind::{self, USE_TREE, WHITESPACE}, |
10 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, | 10 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, |
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs index 261dcc255..011c8cc55 100644 --- a/crates/ide/src/matching_brace.rs +++ b/crates/ide/src/matching_brace.rs | |||
@@ -19,14 +19,10 @@ use syntax::{ | |||
19 | pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { | 19 | pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { |
20 | const BRACES: &[SyntaxKind] = | 20 | const BRACES: &[SyntaxKind] = |
21 | &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]]; | 21 | &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]]; |
22 | let (brace_token, brace_idx) = file | 22 | let (brace_token, brace_idx) = file.syntax().token_at_offset(offset).find_map(|node| { |
23 | .syntax() | 23 | let idx = BRACES.iter().position(|&brace| brace == node.kind())?; |
24 | .token_at_offset(offset) | 24 | Some((node, idx)) |
25 | .filter_map(|node| { | 25 | })?; |
26 | let idx = BRACES.iter().position(|&brace| brace == node.kind())?; | ||
27 | Some((node, idx)) | ||
28 | }) | ||
29 | .next()?; | ||
30 | let parent = brace_token.parent()?; | 26 | let parent = brace_token.parent()?; |
31 | if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { | 27 | if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { |
32 | cov_mark::hit!(pipes_not_braces); | 28 | cov_mark::hit!(pipes_not_braces); |
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index 99365c8a7..9b1f48044 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | use hir::Semantics; | 1 | use hir::Semantics; |
2 | use ide_db::base_db::{CrateId, FileId, FilePosition}; | 2 | use ide_db::{ |
3 | use ide_db::RootDatabase; | 3 | base_db::{CrateId, FileId, FilePosition}, |
4 | RootDatabase, | ||
5 | }; | ||
4 | use itertools::Itertools; | 6 | use itertools::Itertools; |
5 | use syntax::{ | 7 | use syntax::{ |
6 | algo::find_node_at_offset, | 8 | algo::find_node_at_offset, |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index bc221d599..4269d339e 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -6,7 +6,7 @@ use either::Either; | |||
6 | use hir::{InFile, Semantics}; | 6 | use hir::{InFile, Semantics}; |
7 | use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind}; | 7 | use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind}; |
8 | use syntax::{ | 8 | use syntax::{ |
9 | ast::{self, AstNode}, | 9 | ast::{self, AstNode, IsString}, |
10 | AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, | 10 | AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, |
11 | }; | 11 | }; |
12 | 12 | ||
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 8d83ba206..921a956e6 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -37,13 +37,25 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
37 | 37 | ||
38 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } | 38 | .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } |
39 | </style> | 39 | </style> |
40 | <pre><code><span class="comment documentation">/// ```</span> | 40 | <pre><code><span class="comment documentation">//! This is a module to test doc injection.</span> |
41 | <span class="comment documentation">//! ```</span> | ||
42 | <span class="comment documentation">//! </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">test</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span> | ||
43 | <span class="comment documentation">//! ```</span> | ||
44 | |||
45 | <span class="comment documentation">/// ```</span> | ||
41 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="semicolon injected">;</span> | 46 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="semicolon injected">;</span> |
42 | <span class="comment documentation">/// ```</span> | 47 | <span class="comment documentation">/// ```</span> |
43 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span> | 48 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span> |
44 | <span class="field declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span><span class="comma">,</span> | 49 | <span class="field declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span><span class="comma">,</span> |
45 | <span class="brace">}</span> | 50 | <span class="brace">}</span> |
46 | 51 | ||
52 | <span class="comment documentation">/// This is an impl with a code block.</span> | ||
53 | <span class="comment documentation">///</span> | ||
54 | <span class="comment documentation">/// ```</span> | ||
55 | <span class="comment documentation">/// </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span> | ||
56 | <span class="comment documentation">///</span> | ||
57 | <span class="comment documentation">/// </span><span class="brace injected">}</span> | ||
58 | <span class="comment documentation">/// ```</span> | ||
47 | <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span> | 59 | <span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span> |
48 | <span class="comment documentation">/// ```</span> | 60 | <span class="comment documentation">/// ```</span> |
49 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Call me</span> | 61 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Call me</span> |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index e05971983..8c8878d36 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -524,6 +524,11 @@ fn main() { | |||
524 | fn test_highlight_doc_comment() { | 524 | fn test_highlight_doc_comment() { |
525 | check_highlighting( | 525 | check_highlighting( |
526 | r#" | 526 | r#" |
527 | //! This is a module to test doc injection. | ||
528 | //! ``` | ||
529 | //! fn test() {} | ||
530 | //! ``` | ||
531 | |||
527 | /// ``` | 532 | /// ``` |
528 | /// let _ = "early doctests should not go boom"; | 533 | /// let _ = "early doctests should not go boom"; |
529 | /// ``` | 534 | /// ``` |
@@ -531,6 +536,13 @@ struct Foo { | |||
531 | bar: bool, | 536 | bar: bool, |
532 | } | 537 | } |
533 | 538 | ||
539 | /// This is an impl with a code block. | ||
540 | /// | ||
541 | /// ``` | ||
542 | /// fn foo() { | ||
543 | /// | ||
544 | /// } | ||
545 | /// ``` | ||
534 | impl Foo { | 546 | impl Foo { |
535 | /// ``` | 547 | /// ``` |
536 | /// let _ = "Call me | 548 | /// let _ = "Call me |
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs index 682f0ff5e..20754a02a 100644 --- a/crates/ide_assists/src/assist_context.rs +++ b/crates/ide_assists/src/assist_context.rs | |||
@@ -238,8 +238,8 @@ impl AssistBuilder { | |||
238 | } | 238 | } |
239 | } | 239 | } |
240 | 240 | ||
241 | pub(crate) fn make_ast_mut<N: AstNode>(&mut self, node: N) -> N { | 241 | pub(crate) fn make_mut<N: AstNode>(&mut self, node: N) -> N { |
242 | N::cast(self.make_mut(node.syntax().clone())).unwrap() | 242 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(node.syntax())).make_mut(&node) |
243 | } | 243 | } |
244 | /// Returns a copy of the `node`, suitable for mutation. | 244 | /// Returns a copy of the `node`, suitable for mutation. |
245 | /// | 245 | /// |
@@ -251,7 +251,7 @@ impl AssistBuilder { | |||
251 | /// The typical pattern for an assist is to find specific nodes in the read | 251 | /// The typical pattern for an assist is to find specific nodes in the read |
252 | /// phase, and then get their mutable couterparts using `make_mut` in the | 252 | /// phase, and then get their mutable couterparts using `make_mut` in the |
253 | /// mutable state. | 253 | /// mutable state. |
254 | pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode { | 254 | pub(crate) fn make_syntax_mut(&mut self, node: SyntaxNode) -> SyntaxNode { |
255 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) | 255 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) |
256 | } | 256 | } |
257 | 257 | ||
diff --git a/crates/ide_assists/src/handlers/add_explicit_type.rs b/crates/ide_assists/src/handlers/add_explicit_type.rs index 62db31952..36589203d 100644 --- a/crates/ide_assists/src/handlers/add_explicit_type.rs +++ b/crates/ide_assists/src/handlers/add_explicit_type.rs | |||
@@ -198,6 +198,34 @@ fn main() { | |||
198 | ) | 198 | ) |
199 | } | 199 | } |
200 | 200 | ||
201 | /// https://github.com/rust-analyzer/rust-analyzer/issues/2922 | ||
202 | #[test] | ||
203 | fn regression_issue_2922() { | ||
204 | check_assist( | ||
205 | add_explicit_type, | ||
206 | r#" | ||
207 | fn main() { | ||
208 | let $0v = [0.0; 2]; | ||
209 | } | ||
210 | "#, | ||
211 | r#" | ||
212 | fn main() { | ||
213 | let v: [f64; 2] = [0.0; 2]; | ||
214 | } | ||
215 | "#, | ||
216 | ); | ||
217 | // note: this may break later if we add more consteval. it just needs to be something that our | ||
218 | // consteval engine doesn't understand | ||
219 | check_assist_not_applicable( | ||
220 | add_explicit_type, | ||
221 | r#" | ||
222 | fn main() { | ||
223 | let $0l = [0.0; 2+2]; | ||
224 | } | ||
225 | "#, | ||
226 | ); | ||
227 | } | ||
228 | |||
201 | #[test] | 229 | #[test] |
202 | fn default_generics_should_not_be_added() { | 230 | fn default_generics_should_not_be_added() { |
203 | check_assist( | 231 | check_assist( |
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index 506cc292c..dda5a6631 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs | |||
@@ -102,8 +102,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
102 | range, | 102 | range, |
103 | |builder| { | 103 | |builder| { |
104 | let scope = match scope.clone() { | 104 | let scope = match scope.clone() { |
105 | ImportScope::File(it) => ImportScope::File(builder.make_ast_mut(it)), | 105 | ImportScope::File(it) => ImportScope::File(builder.make_mut(it)), |
106 | ImportScope::Module(it) => ImportScope::Module(builder.make_ast_mut(it)), | 106 | ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)), |
107 | }; | 107 | }; |
108 | insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use); | 108 | insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use); |
109 | }, | 109 | }, |
diff --git a/crates/ide_assists/src/handlers/expand_glob_import.rs b/crates/ide_assists/src/handlers/expand_glob_import.rs index da8d245e5..79cb08d69 100644 --- a/crates/ide_assists/src/handlers/expand_glob_import.rs +++ b/crates/ide_assists/src/handlers/expand_glob_import.rs | |||
@@ -61,7 +61,7 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
61 | "Expand glob import", | 61 | "Expand glob import", |
62 | target.text_range(), | 62 | target.text_range(), |
63 | |builder| { | 63 | |builder| { |
64 | let use_tree = builder.make_ast_mut(use_tree); | 64 | let use_tree = builder.make_mut(use_tree); |
65 | 65 | ||
66 | let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs); | 66 | let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs); |
67 | let expanded = make::use_tree_list(names_to_import.iter().map(|n| { | 67 | let expanded = make::use_tree_list(names_to_import.iter().map(|n| { |
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs index 8e2178391..007aba23d 100644 --- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -70,7 +70,7 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
70 | continue; | 70 | continue; |
71 | } | 71 | } |
72 | builder.edit_file(file_id); | 72 | builder.edit_file(file_id); |
73 | let source_file = builder.make_ast_mut(ctx.sema.parse(file_id)); | 73 | let source_file = builder.make_mut(ctx.sema.parse(file_id)); |
74 | let processed = process_references( | 74 | let processed = process_references( |
75 | ctx, | 75 | ctx, |
76 | &mut visited_modules_set, | 76 | &mut visited_modules_set, |
@@ -84,8 +84,8 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
84 | }); | 84 | }); |
85 | } | 85 | } |
86 | builder.edit_file(ctx.frange.file_id); | 86 | builder.edit_file(ctx.frange.file_id); |
87 | let source_file = builder.make_ast_mut(ctx.sema.parse(ctx.frange.file_id)); | 87 | let source_file = builder.make_mut(ctx.sema.parse(ctx.frange.file_id)); |
88 | let variant = builder.make_ast_mut(variant.clone()); | 88 | let variant = builder.make_mut(variant.clone()); |
89 | if let Some(references) = def_file_references { | 89 | if let Some(references) = def_file_references { |
90 | let processed = process_references( | 90 | let processed = process_references( |
91 | ctx, | 91 | ctx, |
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index be927cc1c..58b001050 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs | |||
@@ -71,6 +71,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
71 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) | 71 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) |
72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) | 72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
74 | .map(|it| it.clone_for_update()) | ||
74 | .collect::<Vec<_>>(); | 75 | .collect::<Vec<_>>(); |
75 | if Some(enum_def) | 76 | if Some(enum_def) |
76 | == FamousDefs(&ctx.sema, Some(module.krate())) | 77 | == FamousDefs(&ctx.sema, Some(module.krate())) |
@@ -99,6 +100,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
99 | }) | 100 | }) |
100 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) | 101 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
101 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 102 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
103 | .map(|it| it.clone_for_update()) | ||
102 | .collect() | 104 | .collect() |
103 | } else { | 105 | } else { |
104 | return None; | 106 | return None; |
@@ -114,10 +116,20 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
114 | "Fill match arms", | 116 | "Fill match arms", |
115 | target, | 117 | target, |
116 | |builder| { | 118 | |builder| { |
117 | let new_arm_list = match_arm_list.remove_placeholder(); | 119 | let new_match_arm_list = match_arm_list.clone_for_update(); |
118 | let n_old_arms = new_arm_list.arms().count(); | 120 | |
119 | let new_arm_list = new_arm_list.append_arms(missing_arms); | 121 | let catch_all_arm = new_match_arm_list |
120 | let first_new_arm = new_arm_list.arms().nth(n_old_arms); | 122 | .arms() |
123 | .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_)))); | ||
124 | if let Some(arm) = catch_all_arm { | ||
125 | arm.remove() | ||
126 | } | ||
127 | let mut first_new_arm = None; | ||
128 | for arm in missing_arms { | ||
129 | first_new_arm.get_or_insert_with(|| arm.clone()); | ||
130 | new_match_arm_list.add_arm(arm); | ||
131 | } | ||
132 | |||
121 | let old_range = ctx.sema.original_range(match_arm_list.syntax()).range; | 133 | let old_range = ctx.sema.original_range(match_arm_list.syntax()).range; |
122 | match (first_new_arm, ctx.config.snippet_cap) { | 134 | match (first_new_arm, ctx.config.snippet_cap) { |
123 | (Some(first_new_arm), Some(cap)) => { | 135 | (Some(first_new_arm), Some(cap)) => { |
@@ -131,10 +143,10 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
131 | } | 143 | } |
132 | None => Cursor::Before(first_new_arm.syntax()), | 144 | None => Cursor::Before(first_new_arm.syntax()), |
133 | }; | 145 | }; |
134 | let snippet = render_snippet(cap, new_arm_list.syntax(), cursor); | 146 | let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor); |
135 | builder.replace_snippet(cap, old_range, snippet); | 147 | builder.replace_snippet(cap, old_range, snippet); |
136 | } | 148 | } |
137 | _ => builder.replace(old_range, new_arm_list.to_string()), | 149 | _ => builder.replace(old_range, new_match_arm_list.to_string()), |
138 | } | 150 | } |
139 | }, | 151 | }, |
140 | ) | 152 | ) |
@@ -263,18 +275,18 @@ mod tests { | |||
263 | check_assist_not_applicable( | 275 | check_assist_not_applicable( |
264 | fill_match_arms, | 276 | fill_match_arms, |
265 | r#" | 277 | r#" |
266 | enum A { | 278 | enum A { |
267 | As, | 279 | As, |
268 | Bs{x:i32, y:Option<i32>}, | 280 | Bs{x:i32, y:Option<i32>}, |
269 | Cs(i32, Option<i32>), | 281 | Cs(i32, Option<i32>), |
270 | } | 282 | } |
271 | fn main() { | 283 | fn main() { |
272 | match A::As$0 { | 284 | match A::As$0 { |
273 | A::As, | 285 | A::As, |
274 | A::Bs{x,y:Some(_)} => {} | 286 | A::Bs{x,y:Some(_)} => {} |
275 | A::Cs(_, Some(_)) => {} | 287 | A::Cs(_, Some(_)) => {} |
276 | } | 288 | } |
277 | } | 289 | } |
278 | "#, | 290 | "#, |
279 | ); | 291 | ); |
280 | } | 292 | } |
@@ -284,13 +296,13 @@ mod tests { | |||
284 | check_assist_not_applicable( | 296 | check_assist_not_applicable( |
285 | fill_match_arms, | 297 | fill_match_arms, |
286 | r#" | 298 | r#" |
287 | fn foo(a: bool) { | 299 | fn foo(a: bool) { |
288 | match a$0 { | 300 | match a$0 { |
289 | true => {} | 301 | true => {} |
290 | false => {} | 302 | false => {} |
291 | } | 303 | } |
292 | } | 304 | } |
293 | "#, | 305 | "#, |
294 | ) | 306 | ) |
295 | } | 307 | } |
296 | 308 | ||
@@ -301,11 +313,11 @@ mod tests { | |||
301 | check_assist_not_applicable( | 313 | check_assist_not_applicable( |
302 | fill_match_arms, | 314 | fill_match_arms, |
303 | r#" | 315 | r#" |
304 | fn main() { | 316 | fn main() { |
305 | match (0, false)$0 { | 317 | match (0, false)$0 { |
306 | } | 318 | } |
307 | } | 319 | } |
308 | "#, | 320 | "#, |
309 | ); | 321 | ); |
310 | } | 322 | } |
311 | 323 | ||
@@ -314,19 +326,19 @@ mod tests { | |||
314 | check_assist( | 326 | check_assist( |
315 | fill_match_arms, | 327 | fill_match_arms, |
316 | r#" | 328 | r#" |
317 | fn foo(a: bool) { | 329 | fn foo(a: bool) { |
318 | match a$0 { | 330 | match a$0 { |
319 | } | 331 | } |
320 | } | 332 | } |
321 | "#, | 333 | "#, |
322 | r#" | 334 | r#" |
323 | fn foo(a: bool) { | 335 | fn foo(a: bool) { |
324 | match a { | 336 | match a { |
325 | $0true => {} | 337 | $0true => {} |
326 | false => {} | 338 | false => {} |
327 | } | 339 | } |
328 | } | 340 | } |
329 | "#, | 341 | "#, |
330 | ) | 342 | ) |
331 | } | 343 | } |
332 | 344 | ||
@@ -335,20 +347,20 @@ mod tests { | |||
335 | check_assist( | 347 | check_assist( |
336 | fill_match_arms, | 348 | fill_match_arms, |
337 | r#" | 349 | r#" |
338 | fn foo(a: bool) { | 350 | fn foo(a: bool) { |
339 | match a$0 { | 351 | match a$0 { |
340 | true => {} | 352 | true => {} |
341 | } | 353 | } |
342 | } | 354 | } |
343 | "#, | 355 | "#, |
344 | r#" | 356 | r#" |
345 | fn foo(a: bool) { | 357 | fn foo(a: bool) { |
346 | match a { | 358 | match a { |
347 | true => {} | 359 | true => {} |
348 | $0false => {} | 360 | $0false => {} |
349 | } | 361 | } |
350 | } | 362 | } |
351 | "#, | 363 | "#, |
352 | ) | 364 | ) |
353 | } | 365 | } |
354 | 366 | ||
@@ -357,15 +369,15 @@ mod tests { | |||
357 | check_assist_not_applicable( | 369 | check_assist_not_applicable( |
358 | fill_match_arms, | 370 | fill_match_arms, |
359 | r#" | 371 | r#" |
360 | fn foo(a: bool) { | 372 | fn foo(a: bool) { |
361 | match (a, a)$0 { | 373 | match (a, a)$0 { |
362 | (true, true) => {} | 374 | (true, true) => {} |
363 | (true, false) => {} | 375 | (true, false) => {} |
364 | (false, true) => {} | 376 | (false, true) => {} |
365 | (false, false) => {} | 377 | (false, false) => {} |
366 | } | 378 | } |
367 | } | 379 | } |
368 | "#, | 380 | "#, |
369 | ) | 381 | ) |
370 | } | 382 | } |
371 | 383 | ||
@@ -374,21 +386,21 @@ mod tests { | |||
374 | check_assist( | 386 | check_assist( |
375 | fill_match_arms, | 387 | fill_match_arms, |
376 | r#" | 388 | r#" |
377 | fn foo(a: bool) { | 389 | fn foo(a: bool) { |
378 | match (a, a)$0 { | 390 | match (a, a)$0 { |
379 | } | 391 | } |
380 | } | 392 | } |
381 | "#, | 393 | "#, |
382 | r#" | 394 | r#" |
383 | fn foo(a: bool) { | 395 | fn foo(a: bool) { |
384 | match (a, a) { | 396 | match (a, a) { |
385 | $0(true, true) => {} | 397 | $0(true, true) => {} |
386 | (true, false) => {} | 398 | (true, false) => {} |
387 | (false, true) => {} | 399 | (false, true) => {} |
388 | (false, false) => {} | 400 | (false, false) => {} |
389 | } | 401 | } |
390 | } | 402 | } |
391 | "#, | 403 | "#, |
392 | ) | 404 | ) |
393 | } | 405 | } |
394 | 406 | ||
@@ -397,22 +409,22 @@ mod tests { | |||
397 | check_assist( | 409 | check_assist( |
398 | fill_match_arms, | 410 | fill_match_arms, |
399 | r#" | 411 | r#" |
400 | fn foo(a: bool) { | 412 | fn foo(a: bool) { |
401 | match (a, a)$0 { | 413 | match (a, a)$0 { |
402 | (false, true) => {} | 414 | (false, true) => {} |
403 | } | 415 | } |
404 | } | 416 | } |
405 | "#, | 417 | "#, |
406 | r#" | 418 | r#" |
407 | fn foo(a: bool) { | 419 | fn foo(a: bool) { |
408 | match (a, a) { | 420 | match (a, a) { |
409 | (false, true) => {} | 421 | (false, true) => {} |
410 | $0(true, true) => {} | 422 | $0(true, true) => {} |
411 | (true, false) => {} | 423 | (true, false) => {} |
412 | (false, false) => {} | 424 | (false, false) => {} |
413 | } | 425 | } |
414 | } | 426 | } |
415 | "#, | 427 | "#, |
416 | ) | 428 | ) |
417 | } | 429 | } |
418 | 430 | ||
@@ -421,32 +433,32 @@ mod tests { | |||
421 | check_assist( | 433 | check_assist( |
422 | fill_match_arms, | 434 | fill_match_arms, |
423 | r#" | 435 | r#" |
424 | enum A { | 436 | enum A { |
425 | As, | 437 | As, |
426 | Bs { x: i32, y: Option<i32> }, | 438 | Bs { x: i32, y: Option<i32> }, |
427 | Cs(i32, Option<i32>), | 439 | Cs(i32, Option<i32>), |
428 | } | 440 | } |
429 | fn main() { | 441 | fn main() { |
430 | match A::As$0 { | 442 | match A::As$0 { |
431 | A::Bs { x, y: Some(_) } => {} | 443 | A::Bs { x, y: Some(_) } => {} |
432 | A::Cs(_, Some(_)) => {} | 444 | A::Cs(_, Some(_)) => {} |
433 | } | 445 | } |
434 | } | 446 | } |
435 | "#, | 447 | "#, |
436 | r#" | 448 | r#" |
437 | enum A { | 449 | enum A { |
438 | As, | 450 | As, |
439 | Bs { x: i32, y: Option<i32> }, | 451 | Bs { x: i32, y: Option<i32> }, |
440 | Cs(i32, Option<i32>), | 452 | Cs(i32, Option<i32>), |
441 | } | 453 | } |
442 | fn main() { | 454 | fn main() { |
443 | match A::As { | 455 | match A::As { |
444 | A::Bs { x, y: Some(_) } => {} | 456 | A::Bs { x, y: Some(_) } => {} |
445 | A::Cs(_, Some(_)) => {} | 457 | A::Cs(_, Some(_)) => {} |
446 | $0A::As => {} | 458 | $0A::As => {} |
447 | } | 459 | } |
448 | } | 460 | } |
449 | "#, | 461 | "#, |
450 | ); | 462 | ); |
451 | } | 463 | } |
452 | 464 | ||
@@ -593,30 +605,30 @@ fn main() { | |||
593 | check_assist( | 605 | check_assist( |
594 | fill_match_arms, | 606 | fill_match_arms, |
595 | r#" | 607 | r#" |
596 | enum A { One, Two } | 608 | enum A { One, Two } |
597 | enum B { One, Two } | 609 | enum B { One, Two } |
598 | 610 | ||
599 | fn main() { | 611 | fn main() { |
600 | let a = A::One; | 612 | let a = A::One; |
601 | let b = B::One; | 613 | let b = B::One; |
602 | match (a$0, b) {} | 614 | match (a$0, b) {} |
603 | } | 615 | } |
604 | "#, | 616 | "#, |
605 | r#" | 617 | r#" |
606 | enum A { One, Two } | 618 | enum A { One, Two } |
607 | enum B { One, Two } | 619 | enum B { One, Two } |
608 | 620 | ||
609 | fn main() { | 621 | fn main() { |
610 | let a = A::One; | 622 | let a = A::One; |
611 | let b = B::One; | 623 | let b = B::One; |
612 | match (a, b) { | 624 | match (a, b) { |
613 | $0(A::One, B::One) => {} | 625 | $0(A::One, B::One) => {} |
614 | (A::One, B::Two) => {} | 626 | (A::One, B::Two) => {} |
615 | (A::Two, B::One) => {} | 627 | (A::Two, B::One) => {} |
616 | (A::Two, B::Two) => {} | 628 | (A::Two, B::Two) => {} |
617 | } | 629 | } |
618 | } | 630 | } |
619 | "#, | 631 | "#, |
620 | ); | 632 | ); |
621 | } | 633 | } |
622 | 634 | ||
@@ -625,30 +637,30 @@ fn main() { | |||
625 | check_assist( | 637 | check_assist( |
626 | fill_match_arms, | 638 | fill_match_arms, |
627 | r#" | 639 | r#" |
628 | enum A { One, Two } | 640 | enum A { One, Two } |
629 | enum B { One, Two } | 641 | enum B { One, Two } |
630 | 642 | ||
631 | fn main() { | 643 | fn main() { |
632 | let a = A::One; | 644 | let a = A::One; |
633 | let b = B::One; | 645 | let b = B::One; |
634 | match (&a$0, &b) {} | 646 | match (&a$0, &b) {} |
635 | } | 647 | } |
636 | "#, | 648 | "#, |
637 | r#" | 649 | r#" |
638 | enum A { One, Two } | 650 | enum A { One, Two } |
639 | enum B { One, Two } | 651 | enum B { One, Two } |
640 | 652 | ||
641 | fn main() { | 653 | fn main() { |
642 | let a = A::One; | 654 | let a = A::One; |
643 | let b = B::One; | 655 | let b = B::One; |
644 | match (&a, &b) { | 656 | match (&a, &b) { |
645 | $0(A::One, B::One) => {} | 657 | $0(A::One, B::One) => {} |
646 | (A::One, B::Two) => {} | 658 | (A::One, B::Two) => {} |
647 | (A::Two, B::One) => {} | 659 | (A::Two, B::One) => {} |
648 | (A::Two, B::Two) => {} | 660 | (A::Two, B::Two) => {} |
649 | } | 661 | } |
650 | } | 662 | } |
651 | "#, | 663 | "#, |
652 | ); | 664 | ); |
653 | } | 665 | } |
654 | 666 | ||
@@ -737,20 +749,20 @@ fn main() { | |||
737 | check_assist_not_applicable( | 749 | check_assist_not_applicable( |
738 | fill_match_arms, | 750 | fill_match_arms, |
739 | r#" | 751 | r#" |
740 | enum A { One, Two } | 752 | enum A { One, Two } |
741 | enum B { One, Two } | 753 | enum B { One, Two } |
742 | 754 | ||
743 | fn main() { | 755 | fn main() { |
744 | let a = A::One; | 756 | let a = A::One; |
745 | let b = B::One; | 757 | let b = B::One; |
746 | match (a$0, b) { | 758 | match (a$0, b) { |
747 | (A::Two, B::One) => {} | 759 | (A::Two, B::One) => {} |
748 | (A::One, B::One) => {} | 760 | (A::One, B::One) => {} |
749 | (A::One, B::Two) => {} | 761 | (A::One, B::Two) => {} |
750 | (A::Two, B::Two) => {} | 762 | (A::Two, B::Two) => {} |
751 | } | 763 | } |
752 | } | 764 | } |
753 | "#, | 765 | "#, |
754 | ); | 766 | ); |
755 | } | 767 | } |
756 | 768 | ||
@@ -759,25 +771,25 @@ fn main() { | |||
759 | check_assist( | 771 | check_assist( |
760 | fill_match_arms, | 772 | fill_match_arms, |
761 | r#" | 773 | r#" |
762 | enum A { One, Two } | 774 | enum A { One, Two } |
763 | 775 | ||
764 | fn main() { | 776 | fn main() { |
765 | let a = A::One; | 777 | let a = A::One; |
766 | match (a$0, ) { | 778 | match (a$0, ) { |
767 | } | 779 | } |
768 | } | 780 | } |
769 | "#, | 781 | "#, |
770 | r#" | 782 | r#" |
771 | enum A { One, Two } | 783 | enum A { One, Two } |
772 | 784 | ||
773 | fn main() { | 785 | fn main() { |
774 | let a = A::One; | 786 | let a = A::One; |
775 | match (a, ) { | 787 | match (a, ) { |
776 | $0(A::One,) => {} | 788 | $0(A::One,) => {} |
777 | (A::Two,) => {} | 789 | (A::Two,) => {} |
778 | } | 790 | } |
779 | } | 791 | } |
780 | "#, | 792 | "#, |
781 | ); | 793 | ); |
782 | } | 794 | } |
783 | 795 | ||
@@ -786,47 +798,47 @@ fn main() { | |||
786 | check_assist( | 798 | check_assist( |
787 | fill_match_arms, | 799 | fill_match_arms, |
788 | r#" | 800 | r#" |
789 | enum A { As } | 801 | enum A { As } |
790 | 802 | ||
791 | fn foo(a: &A) { | 803 | fn foo(a: &A) { |
792 | match a$0 { | 804 | match a$0 { |
793 | } | 805 | } |
794 | } | 806 | } |
795 | "#, | 807 | "#, |
796 | r#" | 808 | r#" |
797 | enum A { As } | 809 | enum A { As } |
798 | 810 | ||
799 | fn foo(a: &A) { | 811 | fn foo(a: &A) { |
800 | match a { | 812 | match a { |
801 | $0A::As => {} | 813 | $0A::As => {} |
802 | } | 814 | } |
803 | } | 815 | } |
804 | "#, | 816 | "#, |
805 | ); | 817 | ); |
806 | 818 | ||
807 | check_assist( | 819 | check_assist( |
808 | fill_match_arms, | 820 | fill_match_arms, |
809 | r#" | 821 | r#" |
810 | enum A { | 822 | enum A { |
811 | Es { x: usize, y: usize } | 823 | Es { x: usize, y: usize } |
812 | } | 824 | } |
813 | 825 | ||
814 | fn foo(a: &mut A) { | 826 | fn foo(a: &mut A) { |
815 | match a$0 { | 827 | match a$0 { |
816 | } | 828 | } |
817 | } | 829 | } |
818 | "#, | 830 | "#, |
819 | r#" | 831 | r#" |
820 | enum A { | 832 | enum A { |
821 | Es { x: usize, y: usize } | 833 | Es { x: usize, y: usize } |
822 | } | 834 | } |
823 | 835 | ||
824 | fn foo(a: &mut A) { | 836 | fn foo(a: &mut A) { |
825 | match a { | 837 | match a { |
826 | $0A::Es { x, y } => {} | 838 | $0A::Es { x, y } => {} |
827 | } | 839 | } |
828 | } | 840 | } |
829 | "#, | 841 | "#, |
830 | ); | 842 | ); |
831 | } | 843 | } |
832 | 844 | ||
@@ -835,12 +847,12 @@ fn main() { | |||
835 | check_assist_target( | 847 | check_assist_target( |
836 | fill_match_arms, | 848 | fill_match_arms, |
837 | r#" | 849 | r#" |
838 | enum E { X, Y } | 850 | enum E { X, Y } |
839 | 851 | ||
840 | fn main() { | 852 | fn main() { |
841 | match E::X$0 {} | 853 | match E::X$0 {} |
842 | } | 854 | } |
843 | "#, | 855 | "#, |
844 | "match E::X {}", | 856 | "match E::X {}", |
845 | ); | 857 | ); |
846 | } | 858 | } |
@@ -850,24 +862,24 @@ fn main() { | |||
850 | check_assist( | 862 | check_assist( |
851 | fill_match_arms, | 863 | fill_match_arms, |
852 | r#" | 864 | r#" |
853 | enum E { X, Y } | 865 | enum E { X, Y } |
854 | 866 | ||
855 | fn main() { | 867 | fn main() { |
856 | match E::X { | 868 | match E::X { |
857 | $0_ => {} | 869 | $0_ => {} |
858 | } | 870 | } |
859 | } | 871 | } |
860 | "#, | 872 | "#, |
861 | r#" | 873 | r#" |
862 | enum E { X, Y } | 874 | enum E { X, Y } |
863 | 875 | ||
864 | fn main() { | 876 | fn main() { |
865 | match E::X { | 877 | match E::X { |
866 | $0E::X => {} | 878 | $0E::X => {} |
867 | E::Y => {} | 879 | E::Y => {} |
868 | } | 880 | } |
869 | } | 881 | } |
870 | "#, | 882 | "#, |
871 | ); | 883 | ); |
872 | } | 884 | } |
873 | 885 | ||
@@ -876,26 +888,26 @@ fn main() { | |||
876 | check_assist( | 888 | check_assist( |
877 | fill_match_arms, | 889 | fill_match_arms, |
878 | r#" | 890 | r#" |
879 | mod foo { pub enum E { X, Y } } | 891 | mod foo { pub enum E { X, Y } } |
880 | use foo::E::X; | 892 | use foo::E::X; |
881 | 893 | ||
882 | fn main() { | 894 | fn main() { |
883 | match X { | 895 | match X { |
884 | $0 | 896 | $0 |
885 | } | 897 | } |
886 | } | 898 | } |
887 | "#, | 899 | "#, |
888 | r#" | 900 | r#" |
889 | mod foo { pub enum E { X, Y } } | 901 | mod foo { pub enum E { X, Y } } |
890 | use foo::E::X; | 902 | use foo::E::X; |
891 | 903 | ||
892 | fn main() { | 904 | fn main() { |
893 | match X { | 905 | match X { |
894 | $0X => {} | 906 | $0X => {} |
895 | foo::E::Y => {} | 907 | foo::E::Y => {} |
896 | } | 908 | } |
897 | } | 909 | } |
898 | "#, | 910 | "#, |
899 | ); | 911 | ); |
900 | } | 912 | } |
901 | 913 | ||
@@ -904,26 +916,26 @@ fn main() { | |||
904 | check_assist( | 916 | check_assist( |
905 | fill_match_arms, | 917 | fill_match_arms, |
906 | r#" | 918 | r#" |
907 | enum A { One, Two } | 919 | enum A { One, Two } |
908 | fn foo(a: A) { | 920 | fn foo(a: A) { |
909 | match a { | 921 | match a { |
910 | // foo bar baz$0 | 922 | // foo bar baz$0 |
911 | A::One => {} | 923 | A::One => {} |
912 | // This is where the rest should be | 924 | // This is where the rest should be |
913 | } | 925 | } |
914 | } | 926 | } |
915 | "#, | 927 | "#, |
916 | r#" | 928 | r#" |
917 | enum A { One, Two } | 929 | enum A { One, Two } |
918 | fn foo(a: A) { | 930 | fn foo(a: A) { |
919 | match a { | 931 | match a { |
920 | // foo bar baz | 932 | // foo bar baz |
921 | A::One => {} | 933 | A::One => {} |
922 | // This is where the rest should be | 934 | $0A::Two => {} |
923 | $0A::Two => {} | 935 | // This is where the rest should be |
924 | } | 936 | } |
925 | } | 937 | } |
926 | "#, | 938 | "#, |
927 | ); | 939 | ); |
928 | } | 940 | } |
929 | 941 | ||
@@ -932,23 +944,23 @@ fn main() { | |||
932 | check_assist( | 944 | check_assist( |
933 | fill_match_arms, | 945 | fill_match_arms, |
934 | r#" | 946 | r#" |
935 | enum A { One, Two } | 947 | enum A { One, Two } |
936 | fn foo(a: A) { | 948 | fn foo(a: A) { |
937 | match a { | 949 | match a { |
938 | // foo bar baz$0 | 950 | // foo bar baz$0 |
939 | } | 951 | } |
940 | } | 952 | } |
941 | "#, | 953 | "#, |
942 | r#" | 954 | r#" |
943 | enum A { One, Two } | 955 | enum A { One, Two } |
944 | fn foo(a: A) { | 956 | fn foo(a: A) { |
945 | match a { | 957 | match a { |
946 | // foo bar baz | 958 | $0A::One => {} |
947 | $0A::One => {} | 959 | A::Two => {} |
948 | A::Two => {} | 960 | // foo bar baz |
949 | } | 961 | } |
950 | } | 962 | } |
951 | "#, | 963 | "#, |
952 | ); | 964 | ); |
953 | } | 965 | } |
954 | 966 | ||
@@ -957,22 +969,22 @@ fn main() { | |||
957 | check_assist( | 969 | check_assist( |
958 | fill_match_arms, | 970 | fill_match_arms, |
959 | r#" | 971 | r#" |
960 | enum A { One, Two, } | 972 | enum A { One, Two, } |
961 | fn foo(a: A) { | 973 | fn foo(a: A) { |
962 | match a$0 { | 974 | match a$0 { |
963 | _ => (), | 975 | _ => (), |
964 | } | 976 | } |
965 | } | 977 | } |
966 | "#, | 978 | "#, |
967 | r#" | 979 | r#" |
968 | enum A { One, Two, } | 980 | enum A { One, Two, } |
969 | fn foo(a: A) { | 981 | fn foo(a: A) { |
970 | match a { | 982 | match a { |
971 | $0A::One => {} | 983 | $0A::One => {} |
972 | A::Two => {} | 984 | A::Two => {} |
973 | } | 985 | } |
974 | } | 986 | } |
975 | "#, | 987 | "#, |
976 | ); | 988 | ); |
977 | } | 989 | } |
978 | 990 | ||
@@ -1016,7 +1028,8 @@ enum Test { | |||
1016 | fn foo(t: Test) { | 1028 | fn foo(t: Test) { |
1017 | m!(match t$0 {}); | 1029 | m!(match t$0 {}); |
1018 | }"#, | 1030 | }"#, |
1019 | r#"macro_rules! m { ($expr:expr) => {$expr}} | 1031 | r#" |
1032 | macro_rules! m { ($expr:expr) => {$expr}} | ||
1020 | enum Test { | 1033 | enum Test { |
1021 | A, | 1034 | A, |
1022 | B, | 1035 | B, |
diff --git a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs index 68bc15120..16f8f9d70 100644 --- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs | |||
@@ -84,8 +84,8 @@ fn generate_fn_def_assist( | |||
84 | } | 84 | } |
85 | }; | 85 | }; |
86 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { | 86 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { |
87 | let fn_def = builder.make_ast_mut(fn_def); | 87 | let fn_def = builder.make_mut(fn_def); |
88 | let lifetime = builder.make_ast_mut(lifetime); | 88 | let lifetime = builder.make_mut(lifetime); |
89 | let loc_needing_lifetime = | 89 | let loc_needing_lifetime = |
90 | loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); | 90 | loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); |
91 | 91 | ||
@@ -107,8 +107,8 @@ fn generate_impl_def_assist( | |||
107 | ) -> Option<()> { | 107 | ) -> Option<()> { |
108 | let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; | 108 | let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; |
109 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { | 109 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { |
110 | let impl_def = builder.make_ast_mut(impl_def); | 110 | let impl_def = builder.make_mut(impl_def); |
111 | let lifetime = builder.make_ast_mut(lifetime); | 111 | let lifetime = builder.make_mut(lifetime); |
112 | 112 | ||
113 | impl_def.get_or_create_generic_param_list().add_generic_param( | 113 | impl_def.get_or_create_generic_param_list().add_generic_param( |
114 | make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(), | 114 | make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(), |
@@ -141,8 +141,8 @@ enum NeedsLifetime { | |||
141 | impl NeedsLifetime { | 141 | impl NeedsLifetime { |
142 | fn make_mut(self, builder: &mut AssistBuilder) -> Self { | 142 | fn make_mut(self, builder: &mut AssistBuilder) -> Self { |
143 | match self { | 143 | match self { |
144 | Self::SelfParam(it) => Self::SelfParam(builder.make_ast_mut(it)), | 144 | Self::SelfParam(it) => Self::SelfParam(builder.make_mut(it)), |
145 | Self::RefType(it) => Self::RefType(builder.make_ast_mut(it)), | 145 | Self::RefType(it) => Self::RefType(builder.make_mut(it)), |
146 | } | 146 | } |
147 | } | 147 | } |
148 | 148 | ||
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs index 3cd090737..31854840c 100644 --- a/crates/ide_assists/src/handlers/merge_imports.rs +++ b/crates/ide_assists/src/handlers/merge_imports.rs | |||
@@ -47,16 +47,16 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<() | |||
47 | target, | 47 | target, |
48 | |builder| { | 48 | |builder| { |
49 | if let Some((to_replace, replacement, to_remove)) = imports { | 49 | if let Some((to_replace, replacement, to_remove)) = imports { |
50 | let to_replace = builder.make_ast_mut(to_replace); | 50 | let to_replace = builder.make_mut(to_replace); |
51 | let to_remove = builder.make_ast_mut(to_remove); | 51 | let to_remove = builder.make_mut(to_remove); |
52 | 52 | ||
53 | ted::replace(to_replace.syntax(), replacement.syntax()); | 53 | ted::replace(to_replace.syntax(), replacement.syntax()); |
54 | to_remove.remove(); | 54 | to_remove.remove(); |
55 | } | 55 | } |
56 | 56 | ||
57 | if let Some((to_replace, replacement, to_remove)) = uses { | 57 | if let Some((to_replace, replacement, to_remove)) = uses { |
58 | let to_replace = builder.make_ast_mut(to_replace); | 58 | let to_replace = builder.make_mut(to_replace); |
59 | let to_remove = builder.make_ast_mut(to_remove); | 59 | let to_remove = builder.make_mut(to_remove); |
60 | 60 | ||
61 | ted::replace(to_replace.syntax(), replacement.syntax()); | 61 | ted::replace(to_replace.syntax(), replacement.syntax()); |
62 | to_remove.remove() | 62 | to_remove.remove() |
diff --git a/crates/ide_assists/src/handlers/move_bounds.rs b/crates/ide_assists/src/handlers/move_bounds.rs index fa3f76609..d89d11bdf 100644 --- a/crates/ide_assists/src/handlers/move_bounds.rs +++ b/crates/ide_assists/src/handlers/move_bounds.rs | |||
@@ -36,8 +36,8 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext | |||
36 | "Move to where clause", | 36 | "Move to where clause", |
37 | target, | 37 | target, |
38 | |edit| { | 38 | |edit| { |
39 | let type_param_list = edit.make_ast_mut(type_param_list); | 39 | let type_param_list = edit.make_mut(type_param_list); |
40 | let parent = edit.make_mut(parent); | 40 | let parent = edit.make_syntax_mut(parent); |
41 | 41 | ||
42 | let where_clause: ast::WhereClause = match_ast! { | 42 | let where_clause: ast::WhereClause = match_ast! { |
43 | match parent { | 43 | match parent { |
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs index 3128faa68..f07b8a6c0 100644 --- a/crates/ide_assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs | |||
@@ -74,10 +74,10 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
74 | let assignments: Vec<_> = collector | 74 | let assignments: Vec<_> = collector |
75 | .assignments | 75 | .assignments |
76 | .into_iter() | 76 | .into_iter() |
77 | .map(|(stmt, rhs)| (edit.make_ast_mut(stmt), rhs.clone_for_update())) | 77 | .map(|(stmt, rhs)| (edit.make_mut(stmt), rhs.clone_for_update())) |
78 | .collect(); | 78 | .collect(); |
79 | 79 | ||
80 | let tgt = edit.make_ast_mut(tgt); | 80 | let tgt = edit.make_mut(tgt); |
81 | 81 | ||
82 | for (stmt, rhs) in assignments { | 82 | for (stmt, rhs) in assignments { |
83 | let mut stmt = stmt.syntax().clone(); | 83 | let mut stmt = stmt.syntax().clone(); |
diff --git a/crates/ide_assists/src/handlers/raw_string.rs b/crates/ide_assists/src/handlers/raw_string.rs index d0f1613f3..d98a55ae4 100644 --- a/crates/ide_assists/src/handlers/raw_string.rs +++ b/crates/ide_assists/src/handlers/raw_string.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::borrow::Cow; | 1 | use std::borrow::Cow; |
2 | 2 | ||
3 | use syntax::{ast, AstToken, TextRange, TextSize}; | 3 | use syntax::{ast, ast::IsString, AstToken, TextRange, TextSize}; |
4 | 4 | ||
5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
6 | 6 | ||
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs index e90bbdbcf..933acead1 100644 --- a/crates/ide_assists/src/handlers/reorder_fields.rs +++ b/crates/ide_assists/src/handlers/reorder_fields.rs | |||
@@ -70,10 +70,10 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
70 | target, | 70 | target, |
71 | |builder| match fields { | 71 | |builder| match fields { |
72 | Either::Left((sorted, field_list)) => { | 72 | Either::Left((sorted, field_list)) => { |
73 | replace(builder.make_ast_mut(field_list).fields(), sorted) | 73 | replace(builder.make_mut(field_list).fields(), sorted) |
74 | } | 74 | } |
75 | Either::Right((sorted, field_list)) => { | 75 | Either::Right((sorted, field_list)) => { |
76 | replace(builder.make_ast_mut(field_list).fields(), sorted) | 76 | replace(builder.make_mut(field_list).fields(), sorted) |
77 | } | 77 | } |
78 | }, | 78 | }, |
79 | ) | 79 | ) |
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs index 54a9a468e..5a6a9f158 100644 --- a/crates/ide_assists/src/handlers/reorder_impl.rs +++ b/crates/ide_assists/src/handlers/reorder_impl.rs | |||
@@ -79,8 +79,7 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
79 | "Sort methods", | 79 | "Sort methods", |
80 | target, | 80 | target, |
81 | |builder| { | 81 | |builder| { |
82 | let methods = | 82 | let methods = methods.into_iter().map(|fn_| builder.make_mut(fn_)).collect::<Vec<_>>(); |
83 | methods.into_iter().map(|fn_| builder.make_ast_mut(fn_)).collect::<Vec<_>>(); | ||
84 | methods | 83 | methods |
85 | .into_iter() | 84 | .into_iter() |
86 | .zip(sorted) | 85 | .zip(sorted) |
diff --git a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs index 15420aedf..540a905cc 100644 --- a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs +++ b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs | |||
@@ -32,8 +32,8 @@ pub(crate) fn replace_impl_trait_with_generic( | |||
32 | "Replace impl trait with generic", | 32 | "Replace impl trait with generic", |
33 | target, | 33 | target, |
34 | |edit| { | 34 | |edit| { |
35 | let impl_trait_type = edit.make_ast_mut(impl_trait_type); | 35 | let impl_trait_type = edit.make_mut(impl_trait_type); |
36 | let fn_ = edit.make_ast_mut(fn_); | 36 | let fn_ = edit.make_mut(fn_); |
37 | 37 | ||
38 | let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type); | 38 | let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type); |
39 | 39 | ||
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs index 99ba79860..39f5eb4ff 100644 --- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -40,7 +40,7 @@ pub(crate) fn replace_qualified_name_with_use( | |||
40 | |builder| { | 40 | |builder| { |
41 | // Now that we've brought the name into scope, re-qualify all paths that could be | 41 | // Now that we've brought the name into scope, re-qualify all paths that could be |
42 | // affected (that is, all paths inside the node we added the `use` to). | 42 | // affected (that is, all paths inside the node we added the `use` to). |
43 | let syntax = builder.make_mut(syntax.clone()); | 43 | let syntax = builder.make_syntax_mut(syntax.clone()); |
44 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { | 44 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { |
45 | shorten_paths(&syntax, &path.clone_for_update()); | 45 | shorten_paths(&syntax, &path.clone_for_update()); |
46 | insert_use(import_scope, path, ctx.config.insert_use); | 46 | insert_use(import_scope, path, ctx.config.insert_use); |
diff --git a/crates/ide_assists/src/handlers/replace_string_with_char.rs b/crates/ide_assists/src/handlers/replace_string_with_char.rs index 634b9c0b7..0800d291e 100644 --- a/crates/ide_assists/src/handlers/replace_string_with_char.rs +++ b/crates/ide_assists/src/handlers/replace_string_with_char.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use syntax::{ast, AstToken, SyntaxKind::STRING}; | 1 | use syntax::{ast, ast::IsString, AstToken, SyntaxKind::STRING}; |
2 | 2 | ||
3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
4 | 4 | ||
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index b635e0ca3..61b667104 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -9,22 +9,21 @@ use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKin | |||
9 | pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { | 9 | pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { |
10 | // complete keyword "crate" in use stmt | 10 | // complete keyword "crate" in use stmt |
11 | let source_range = ctx.source_range(); | 11 | let source_range = ctx.source_range(); |
12 | let kw_completion = move |text: &str| { | ||
13 | let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, text); | ||
14 | item.kind(CompletionItemKind::Keyword).insert_text(text); | ||
15 | item | ||
16 | }; | ||
12 | 17 | ||
13 | if ctx.use_item_syntax.is_some() { | 18 | if ctx.use_item_syntax.is_some() { |
14 | if ctx.path_qual.is_none() { | 19 | if ctx.path_qual.is_none() { |
15 | let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "crate::"); | 20 | kw_completion("crate::").add_to(acc); |
16 | item.kind(CompletionItemKind::Keyword).insert_text("crate::"); | ||
17 | item.add_to(acc); | ||
18 | } | 21 | } |
19 | let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "self"); | 22 | kw_completion("self").add_to(acc); |
20 | item.kind(CompletionItemKind::Keyword); | ||
21 | item.add_to(acc); | ||
22 | if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) | 23 | if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) |
23 | .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) | 24 | .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) |
24 | { | 25 | { |
25 | let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "super::"); | 26 | kw_completion("super::").add_to(acc); |
26 | item.kind(CompletionItemKind::Keyword).insert_text("super::"); | ||
27 | item.add_to(acc); | ||
28 | } | 27 | } |
29 | } | 28 | } |
30 | 29 | ||
@@ -32,9 +31,8 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC | |||
32 | if let Some(receiver) = &ctx.dot_receiver { | 31 | if let Some(receiver) = &ctx.dot_receiver { |
33 | if let Some(ty) = ctx.sema.type_of_expr(receiver) { | 32 | if let Some(ty) = ctx.sema.type_of_expr(receiver) { |
34 | if ty.impls_future(ctx.db) { | 33 | if ty.impls_future(ctx.db) { |
35 | let mut item = | 34 | let mut item = kw_completion("await"); |
36 | CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await"); | 35 | item.detail("expr.await"); |
37 | item.kind(CompletionItemKind::Keyword).detail("expr.await").insert_text("await"); | ||
38 | item.add_to(acc); | 36 | item.add_to(acc); |
39 | } | 37 | } |
40 | }; | 38 | }; |
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 4b5f5c571..61952377f 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -10,12 +10,8 @@ use arrayvec::ArrayVec; | |||
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | algo, | 12 | algo, |
13 | ast::{ | 13 | ast::{self, make, AstNode}, |
14 | self, | 14 | ted, AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxKind, |
15 | make::{self, tokens}, | ||
16 | AstNode, | ||
17 | }, | ||
18 | ted, AstToken, Direction, InsertPosition, NodeOrToken, SmolStr, SyntaxElement, SyntaxKind, | ||
19 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, | 15 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, |
20 | SyntaxNode, SyntaxToken, T, | 16 | SyntaxNode, SyntaxToken, T, |
21 | }; | 17 | }; |
@@ -29,114 +25,6 @@ impl ast::BinExpr { | |||
29 | } | 25 | } |
30 | } | 26 | } |
31 | 27 | ||
32 | fn make_multiline<N>(node: N) -> N | ||
33 | where | ||
34 | N: AstNode + Clone, | ||
35 | { | ||
36 | let l_curly = match node.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) { | ||
37 | Some(it) => it, | ||
38 | None => return node, | ||
39 | }; | ||
40 | let sibling = match l_curly.next_sibling_or_token() { | ||
41 | Some(it) => it, | ||
42 | None => return node, | ||
43 | }; | ||
44 | let existing_ws = match sibling.as_token() { | ||
45 | None => None, | ||
46 | Some(tok) if tok.kind() != WHITESPACE => None, | ||
47 | Some(ws) => { | ||
48 | if ws.text().contains('\n') { | ||
49 | return node; | ||
50 | } | ||
51 | Some(ws.clone()) | ||
52 | } | ||
53 | }; | ||
54 | |||
55 | let indent = leading_indent(node.syntax()).unwrap_or_default(); | ||
56 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
57 | let to_insert = iter::once(ws.ws().into()); | ||
58 | match existing_ws { | ||
59 | None => node.insert_children(InsertPosition::After(l_curly), to_insert), | ||
60 | Some(ws) => node.replace_children(single_node(ws), to_insert), | ||
61 | } | ||
62 | } | ||
63 | |||
64 | impl ast::RecordExprFieldList { | ||
65 | #[must_use] | ||
66 | pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList { | ||
67 | self.insert_field(InsertPosition::Last, field) | ||
68 | } | ||
69 | |||
70 | #[must_use] | ||
71 | pub fn insert_field( | ||
72 | &self, | ||
73 | position: InsertPosition<&'_ ast::RecordExprField>, | ||
74 | field: &ast::RecordExprField, | ||
75 | ) -> ast::RecordExprFieldList { | ||
76 | let is_multiline = self.syntax().text().contains_char('\n'); | ||
77 | let ws; | ||
78 | let space = if is_multiline { | ||
79 | ws = tokens::WsBuilder::new(&format!( | ||
80 | "\n{} ", | ||
81 | leading_indent(self.syntax()).unwrap_or_default() | ||
82 | )); | ||
83 | ws.ws() | ||
84 | } else { | ||
85 | tokens::single_space() | ||
86 | }; | ||
87 | |||
88 | let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new(); | ||
89 | to_insert.push(space.into()); | ||
90 | to_insert.push(field.syntax().clone().into()); | ||
91 | to_insert.push(make::token(T![,]).into()); | ||
92 | |||
93 | macro_rules! after_l_curly { | ||
94 | () => {{ | ||
95 | let anchor = match self.l_curly_token() { | ||
96 | Some(it) => it.into(), | ||
97 | None => return self.clone(), | ||
98 | }; | ||
99 | InsertPosition::After(anchor) | ||
100 | }}; | ||
101 | } | ||
102 | |||
103 | macro_rules! after_field { | ||
104 | ($anchor:expr) => { | ||
105 | if let Some(comma) = $anchor | ||
106 | .syntax() | ||
107 | .siblings_with_tokens(Direction::Next) | ||
108 | .find(|it| it.kind() == T![,]) | ||
109 | { | ||
110 | InsertPosition::After(comma) | ||
111 | } else { | ||
112 | to_insert.insert(0, make::token(T![,]).into()); | ||
113 | InsertPosition::After($anchor.syntax().clone().into()) | ||
114 | } | ||
115 | }; | ||
116 | } | ||
117 | |||
118 | let position = match position { | ||
119 | InsertPosition::First => after_l_curly!(), | ||
120 | InsertPosition::Last => { | ||
121 | if !is_multiline { | ||
122 | // don't insert comma before curly | ||
123 | to_insert.pop(); | ||
124 | } | ||
125 | match self.fields().last() { | ||
126 | Some(it) => after_field!(it), | ||
127 | None => after_l_curly!(), | ||
128 | } | ||
129 | } | ||
130 | InsertPosition::Before(anchor) => { | ||
131 | InsertPosition::Before(anchor.syntax().clone().into()) | ||
132 | } | ||
133 | InsertPosition::After(anchor) => after_field!(anchor), | ||
134 | }; | ||
135 | |||
136 | self.insert_children(position, to_insert) | ||
137 | } | ||
138 | } | ||
139 | |||
140 | impl ast::Path { | 28 | impl ast::Path { |
141 | #[must_use] | 29 | #[must_use] |
142 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { | 30 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { |
@@ -214,79 +102,6 @@ impl ast::UseTree { | |||
214 | } | 102 | } |
215 | } | 103 | } |
216 | 104 | ||
217 | impl ast::MatchArmList { | ||
218 | #[must_use] | ||
219 | pub fn append_arms(&self, items: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList { | ||
220 | let mut res = self.clone(); | ||
221 | res = res.strip_if_only_whitespace(); | ||
222 | if !res.syntax().text().contains_char('\n') { | ||
223 | res = make_multiline(res); | ||
224 | } | ||
225 | items.into_iter().for_each(|it| res = res.append_arm(it)); | ||
226 | res | ||
227 | } | ||
228 | |||
229 | fn strip_if_only_whitespace(&self) -> ast::MatchArmList { | ||
230 | let mut iter = self.syntax().children_with_tokens().skip_while(|it| it.kind() != T!['{']); | ||
231 | iter.next(); // Eat the curly | ||
232 | let mut inner = iter.take_while(|it| it.kind() != T!['}']); | ||
233 | if !inner.clone().all(|it| it.kind() == WHITESPACE) { | ||
234 | return self.clone(); | ||
235 | } | ||
236 | let start = match inner.next() { | ||
237 | Some(s) => s, | ||
238 | None => return self.clone(), | ||
239 | }; | ||
240 | let end = match inner.last() { | ||
241 | Some(s) => s, | ||
242 | None => start.clone(), | ||
243 | }; | ||
244 | self.replace_children(start..=end, &mut iter::empty()) | ||
245 | } | ||
246 | |||
247 | #[must_use] | ||
248 | pub fn remove_placeholder(&self) -> ast::MatchArmList { | ||
249 | let placeholder = | ||
250 | self.arms().find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_)))); | ||
251 | if let Some(placeholder) = placeholder { | ||
252 | self.remove_arm(&placeholder) | ||
253 | } else { | ||
254 | self.clone() | ||
255 | } | ||
256 | } | ||
257 | |||
258 | #[must_use] | ||
259 | fn remove_arm(&self, arm: &ast::MatchArm) -> ast::MatchArmList { | ||
260 | let start = arm.syntax().clone(); | ||
261 | let end = if let Some(comma) = start | ||
262 | .siblings_with_tokens(Direction::Next) | ||
263 | .skip(1) | ||
264 | .find(|it| !it.kind().is_trivia()) | ||
265 | .filter(|it| it.kind() == T![,]) | ||
266 | { | ||
267 | comma | ||
268 | } else { | ||
269 | start.clone().into() | ||
270 | }; | ||
271 | self.replace_children(start.into()..=end, None) | ||
272 | } | ||
273 | |||
274 | #[must_use] | ||
275 | pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList { | ||
276 | let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) { | ||
277 | Some(t) => t, | ||
278 | None => return self.clone(), | ||
279 | }; | ||
280 | let position = InsertPosition::Before(r_curly); | ||
281 | let arm_ws = tokens::WsBuilder::new(" "); | ||
282 | let match_indent = &leading_indent(self.syntax()).unwrap_or_default(); | ||
283 | let match_ws = tokens::WsBuilder::new(&format!("\n{}", match_indent)); | ||
284 | let to_insert: ArrayVec<SyntaxElement, 3> = | ||
285 | [arm_ws.ws().into(), item.syntax().clone().into(), match_ws.ws().into()].into(); | ||
286 | self.insert_children(position, to_insert) | ||
287 | } | ||
288 | } | ||
289 | |||
290 | #[must_use] | 105 | #[must_use] |
291 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { | 106 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { |
292 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() | 107 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() |
@@ -413,22 +228,6 @@ impl IndentLevel { | |||
413 | } | 228 | } |
414 | } | 229 | } |
415 | 230 | ||
416 | // FIXME: replace usages with IndentLevel above | ||
417 | fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> { | ||
418 | for token in prev_tokens(node.first_token()?) { | ||
419 | if let Some(ws) = ast::Whitespace::cast(token.clone()) { | ||
420 | let ws_text = ws.text(); | ||
421 | if let Some(pos) = ws_text.rfind('\n') { | ||
422 | return Some(ws_text[pos + 1..].into()); | ||
423 | } | ||
424 | } | ||
425 | if token.text().contains('\n') { | ||
426 | break; | ||
427 | } | ||
428 | } | ||
429 | None | ||
430 | } | ||
431 | |||
432 | fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { | 231 | fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { |
433 | iter::successors(Some(token), |token| token.prev_token()) | 232 | iter::successors(Some(token), |token| token.prev_token()) |
434 | } | 233 | } |
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index ca777d057..14624c682 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs | |||
@@ -13,7 +13,7 @@ use crate::{ | |||
13 | make, GenericParamsOwner, | 13 | make, GenericParamsOwner, |
14 | }, | 14 | }, |
15 | ted::{self, Position}, | 15 | ted::{self, Position}, |
16 | AstNode, AstToken, Direction, | 16 | AstNode, AstToken, Direction, SyntaxNode, |
17 | }; | 17 | }; |
18 | 18 | ||
19 | use super::NameOwner; | 19 | use super::NameOwner; |
@@ -297,7 +297,7 @@ impl ast::AssocItemList { | |||
297 | ), | 297 | ), |
298 | None => match self.l_curly_token() { | 298 | None => match self.l_curly_token() { |
299 | Some(l_curly) => { | 299 | Some(l_curly) => { |
300 | self.normalize_ws_between_braces(); | 300 | normalize_ws_between_braces(self.syntax()); |
301 | (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") | 301 | (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") |
302 | } | 302 | } |
303 | None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), | 303 | None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), |
@@ -309,25 +309,6 @@ impl ast::AssocItemList { | |||
309 | ]; | 309 | ]; |
310 | ted::insert_all(position, elements); | 310 | ted::insert_all(position, elements); |
311 | } | 311 | } |
312 | |||
313 | fn normalize_ws_between_braces(&self) -> Option<()> { | ||
314 | let l = self.l_curly_token()?; | ||
315 | let r = self.r_curly_token()?; | ||
316 | let indent = IndentLevel::from_node(self.syntax()); | ||
317 | |||
318 | match l.next_sibling_or_token() { | ||
319 | Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => { | ||
320 | if ws.next_sibling_or_token()?.into_token()? == r { | ||
321 | ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent))); | ||
322 | } | ||
323 | } | ||
324 | Some(ws) if ws.kind() == T!['}'] => { | ||
325 | ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent))); | ||
326 | } | ||
327 | _ => (), | ||
328 | } | ||
329 | Some(()) | ||
330 | } | ||
331 | } | 312 | } |
332 | 313 | ||
333 | impl ast::Fn { | 314 | impl ast::Fn { |
@@ -346,6 +327,113 @@ impl ast::Fn { | |||
346 | } | 327 | } |
347 | } | 328 | } |
348 | 329 | ||
330 | impl ast::MatchArm { | ||
331 | pub fn remove(&self) { | ||
332 | if let Some(sibling) = self.syntax().prev_sibling_or_token() { | ||
333 | if sibling.kind() == SyntaxKind::WHITESPACE { | ||
334 | ted::remove(sibling); | ||
335 | } | ||
336 | } | ||
337 | if let Some(sibling) = self.syntax().next_sibling_or_token() { | ||
338 | if sibling.kind() == T![,] { | ||
339 | ted::remove(sibling); | ||
340 | } | ||
341 | } | ||
342 | ted::remove(self.syntax()); | ||
343 | } | ||
344 | } | ||
345 | |||
346 | impl ast::MatchArmList { | ||
347 | pub fn add_arm(&self, arm: ast::MatchArm) { | ||
348 | normalize_ws_between_braces(self.syntax()); | ||
349 | let position = match self.arms().last() { | ||
350 | Some(last_arm) => { | ||
351 | let curly = last_arm | ||
352 | .syntax() | ||
353 | .siblings_with_tokens(Direction::Next) | ||
354 | .find(|it| it.kind() == T![,]); | ||
355 | Position::after(curly.unwrap_or_else(|| last_arm.syntax().clone().into())) | ||
356 | } | ||
357 | None => match self.l_curly_token() { | ||
358 | Some(it) => Position::after(it), | ||
359 | None => Position::last_child_of(self.syntax()), | ||
360 | }, | ||
361 | }; | ||
362 | let indent = IndentLevel::from_node(self.syntax()) + 1; | ||
363 | let elements = vec![ | ||
364 | make::tokens::whitespace(&format!("\n{}", indent)).into(), | ||
365 | arm.syntax().clone().into(), | ||
366 | ]; | ||
367 | ted::insert_all(position, elements); | ||
368 | } | ||
369 | } | ||
370 | |||
371 | impl ast::RecordExprFieldList { | ||
372 | pub fn add_field(&self, field: ast::RecordExprField) { | ||
373 | let is_multiline = self.syntax().text().contains_char('\n'); | ||
374 | let whitespace = if is_multiline { | ||
375 | let indent = IndentLevel::from_node(self.syntax()) + 1; | ||
376 | make::tokens::whitespace(&format!("\n{}", indent)) | ||
377 | } else { | ||
378 | make::tokens::single_space() | ||
379 | }; | ||
380 | |||
381 | let position = match self.fields().last() { | ||
382 | Some(last_field) => { | ||
383 | let comma = match last_field | ||
384 | .syntax() | ||
385 | .siblings_with_tokens(Direction::Next) | ||
386 | .filter_map(|it| it.into_token()) | ||
387 | .find(|it| it.kind() == T![,]) | ||
388 | { | ||
389 | Some(it) => it, | ||
390 | None => { | ||
391 | let comma = ast::make::token(T![,]); | ||
392 | ted::insert(Position::after(last_field.syntax()), &comma); | ||
393 | comma | ||
394 | } | ||
395 | }; | ||
396 | Position::after(comma) | ||
397 | } | ||
398 | None => match self.l_curly_token() { | ||
399 | Some(it) => Position::after(it), | ||
400 | None => Position::last_child_of(self.syntax()), | ||
401 | }, | ||
402 | }; | ||
403 | |||
404 | ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]); | ||
405 | if is_multiline { | ||
406 | ted::insert(Position::after(field.syntax()), ast::make::token(T![,])); | ||
407 | } | ||
408 | } | ||
409 | } | ||
410 | |||
411 | fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { | ||
412 | let l = node | ||
413 | .children_with_tokens() | ||
414 | .filter_map(|it| it.into_token()) | ||
415 | .find(|it| it.kind() == T!['{'])?; | ||
416 | let r = node | ||
417 | .children_with_tokens() | ||
418 | .filter_map(|it| it.into_token()) | ||
419 | .find(|it| it.kind() == T!['}'])?; | ||
420 | |||
421 | let indent = IndentLevel::from_node(node); | ||
422 | |||
423 | match l.next_sibling_or_token() { | ||
424 | Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => { | ||
425 | if ws.next_sibling_or_token()?.into_token()? == r { | ||
426 | ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent))); | ||
427 | } | ||
428 | } | ||
429 | Some(ws) if ws.kind() == T!['}'] => { | ||
430 | ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent))); | ||
431 | } | ||
432 | _ => (), | ||
433 | } | ||
434 | Some(()) | ||
435 | } | ||
436 | |||
349 | #[cfg(test)] | 437 | #[cfg(test)] |
350 | mod tests { | 438 | mod tests { |
351 | use std::fmt; | 439 | use std::fmt; |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 29d25a58a..4b1e1ccee 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -143,6 +143,30 @@ impl QuoteOffsets { | |||
143 | } | 143 | } |
144 | } | 144 | } |
145 | 145 | ||
146 | pub trait IsString: AstToken { | ||
147 | fn quote_offsets(&self) -> Option<QuoteOffsets> { | ||
148 | let text = self.text(); | ||
149 | let offsets = QuoteOffsets::new(text)?; | ||
150 | let o = self.syntax().text_range().start(); | ||
151 | let offsets = QuoteOffsets { | ||
152 | quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), | ||
153 | contents: offsets.contents + o, | ||
154 | }; | ||
155 | Some(offsets) | ||
156 | } | ||
157 | fn text_range_between_quotes(&self) -> Option<TextRange> { | ||
158 | self.quote_offsets().map(|it| it.contents) | ||
159 | } | ||
160 | fn open_quote_text_range(&self) -> Option<TextRange> { | ||
161 | self.quote_offsets().map(|it| it.quotes.0) | ||
162 | } | ||
163 | fn close_quote_text_range(&self) -> Option<TextRange> { | ||
164 | self.quote_offsets().map(|it| it.quotes.1) | ||
165 | } | ||
166 | } | ||
167 | |||
168 | impl IsString for ast::String {} | ||
169 | |||
146 | impl ast::String { | 170 | impl ast::String { |
147 | pub fn is_raw(&self) -> bool { | 171 | pub fn is_raw(&self) -> bool { |
148 | self.text().starts_with('r') | 172 | self.text().starts_with('r') |
@@ -187,32 +211,49 @@ impl ast::String { | |||
187 | (false, false) => Some(Cow::Owned(buf)), | 211 | (false, false) => Some(Cow::Owned(buf)), |
188 | } | 212 | } |
189 | } | 213 | } |
190 | |||
191 | pub fn quote_offsets(&self) -> Option<QuoteOffsets> { | ||
192 | let text = self.text(); | ||
193 | let offsets = QuoteOffsets::new(text)?; | ||
194 | let o = self.syntax().text_range().start(); | ||
195 | let offsets = QuoteOffsets { | ||
196 | quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), | ||
197 | contents: offsets.contents + o, | ||
198 | }; | ||
199 | Some(offsets) | ||
200 | } | ||
201 | pub fn text_range_between_quotes(&self) -> Option<TextRange> { | ||
202 | self.quote_offsets().map(|it| it.contents) | ||
203 | } | ||
204 | pub fn open_quote_text_range(&self) -> Option<TextRange> { | ||
205 | self.quote_offsets().map(|it| it.quotes.0) | ||
206 | } | ||
207 | pub fn close_quote_text_range(&self) -> Option<TextRange> { | ||
208 | self.quote_offsets().map(|it| it.quotes.1) | ||
209 | } | ||
210 | } | 214 | } |
211 | 215 | ||
216 | impl IsString for ast::ByteString {} | ||
217 | |||
212 | impl ast::ByteString { | 218 | impl ast::ByteString { |
213 | pub fn is_raw(&self) -> bool { | 219 | pub fn is_raw(&self) -> bool { |
214 | self.text().starts_with("br") | 220 | self.text().starts_with("br") |
215 | } | 221 | } |
222 | |||
223 | pub fn value(&self) -> Option<Cow<'_, [u8]>> { | ||
224 | if self.is_raw() { | ||
225 | let text = self.text(); | ||
226 | let text = | ||
227 | &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | ||
228 | return Some(Cow::Borrowed(text.as_bytes())); | ||
229 | } | ||
230 | |||
231 | let text = self.text(); | ||
232 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | ||
233 | |||
234 | let mut buf: Vec<u8> = Vec::new(); | ||
235 | let mut text_iter = text.chars(); | ||
236 | let mut has_error = false; | ||
237 | unescape_literal(text, Mode::ByteStr, &mut |char_range, unescaped_char| match ( | ||
238 | unescaped_char, | ||
239 | buf.capacity() == 0, | ||
240 | ) { | ||
241 | (Ok(c), false) => buf.push(c as u8), | ||
242 | (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), | ||
243 | (Ok(c), true) => { | ||
244 | buf.reserve_exact(text.len()); | ||
245 | buf.extend_from_slice(&text[..char_range.start].as_bytes()); | ||
246 | buf.push(c as u8); | ||
247 | } | ||
248 | (Err(_), _) => has_error = true, | ||
249 | }); | ||
250 | |||
251 | match (has_error, buf.capacity() == 0) { | ||
252 | (true, _) => None, | ||
253 | (false, true) => Some(Cow::Borrowed(text.as_bytes())), | ||
254 | (false, false) => Some(Cow::Owned(buf)), | ||
255 | } | ||
256 | } | ||
216 | } | 257 | } |
217 | 258 | ||
218 | #[derive(Debug)] | 259 | #[derive(Debug)] |
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs index 1934204ea..d63ec080b 100644 --- a/crates/syntax/src/parsing/text_tree_sink.rs +++ b/crates/syntax/src/parsing/text_tree_sink.rs | |||
@@ -147,8 +147,8 @@ fn n_attached_trivias<'a>( | |||
147 | trivias: impl Iterator<Item = (SyntaxKind, &'a str)>, | 147 | trivias: impl Iterator<Item = (SyntaxKind, &'a str)>, |
148 | ) -> usize { | 148 | ) -> usize { |
149 | match kind { | 149 | match kind { |
150 | MACRO_CALL | MACRO_RULES | MACRO_DEF | CONST | TYPE_ALIAS | STRUCT | UNION | ENUM | 150 | CONST | ENUM | FN | IMPL | MACRO_CALL | MACRO_DEF | MACRO_RULES | MODULE | RECORD_FIELD |
151 | | VARIANT | FN | TRAIT | MODULE | RECORD_FIELD | STATIC | USE => { | 151 | | STATIC | STRUCT | TRAIT | TUPLE_FIELD | TYPE_ALIAS | UNION | USE | VARIANT => { |
152 | let mut res = 0; | 152 | let mut res = 0; |
153 | let mut trivias = trivias.enumerate().peekable(); | 153 | let mut trivias = trivias.enumerate().peekable(); |
154 | 154 | ||
diff --git a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast index 50ab52d32..5e50b4e0b 100644 --- a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast +++ b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast | |||
@@ -127,9 +127,9 @@ [email protected] | |||
127 | [email protected] "\n" | 127 | [email protected] "\n" |
128 | [email protected] "}" | 128 | [email protected] "}" |
129 | [email protected] "\n\n" | 129 | [email protected] "\n\n" |
130 | COMMENT@541..601 "// https://github.com ..." | 130 | IMPL@541..763 |
131 | WHITESPACE@601..602 "\n" | 131 | COMMENT@541..601 "// https://github.com ..." |
132 | IMPL@602..763 | 132 | WHITESPACE@601..602 "\n" |
133 | [email protected] "impl" | 133 | [email protected] "impl" |
134 | [email protected] " " | 134 | [email protected] " " |
135 | [email protected] | 135 | [email protected] |