aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/lib.rs5
-rw-r--r--crates/hir_def/src/body/lower.rs10
-rw-r--r--crates/hir_def/src/expr.rs4
-rw-r--r--crates/hir_def/src/type_ref.rs73
-rw-r--r--crates/hir_ty/src/consteval.rs56
-rw-r--r--crates/hir_ty/src/consts.rs21
-rw-r--r--crates/hir_ty/src/display.rs5
-rw-r--r--crates/hir_ty/src/infer/expr.rs32
-rw-r--r--crates/hir_ty/src/interner.rs3
-rw-r--r--crates/hir_ty/src/lib.rs10
-rw-r--r--crates/hir_ty/src/lower.rs11
-rw-r--r--crates/hir_ty/src/tests/coercion.rs46
-rw-r--r--crates/hir_ty/src/tests/patterns.rs8
-rw-r--r--crates/hir_ty/src/tests/simple.rs65
-rw-r--r--crates/hir_ty/src/tests/traits.rs97
-rw-r--r--crates/ide/src/diagnostics.rs6
-rw-r--r--crates/ide/src/diagnostics/fixes.rs7
-rw-r--r--crates/ide/src/join_lines.rs2
-rw-r--r--crates/ide/src/matching_brace.rs12
-rw-r--r--crates/ide/src/parent_module.rs6
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html14
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html4
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs16
-rw-r--r--crates/ide_assists/src/assist_context.rs6
-rw-r--r--crates/ide_assists/src/handlers/add_explicit_type.rs28
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs4
-rw-r--r--crates/ide_assists/src/handlers/expand_glob_import.rs2
-rw-r--r--crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs6
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs633
-rw-r--r--crates/ide_assists/src/handlers/introduce_named_lifetime.rs12
-rw-r--r--crates/ide_assists/src/handlers/merge_imports.rs8
-rw-r--r--crates/ide_assists/src/handlers/move_bounds.rs4
-rw-r--r--crates/ide_assists/src/handlers/pull_assignment_up.rs4
-rw-r--r--crates/ide_assists/src/handlers/raw_string.rs2
-rw-r--r--crates/ide_assists/src/handlers/reorder_fields.rs4
-rw-r--r--crates/ide_assists/src/handlers/reorder_impl.rs3
-rw-r--r--crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs4
-rw-r--r--crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_string_with_char.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs2
-rw-r--r--crates/syntax/src/ast/edit.rs205
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs130
-rw-r--r--crates/syntax/src/ast/token_ext.rs81
-rw-r--r--crates/syntax/src/parsing/text_tree_sink.rs4
-rw-r--r--crates/syntax/test_data/parser/ok/0045_block_attrs.rast6
46 files changed, 940 insertions, 727 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};
53use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; 53use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
54use hir_ty::{ 54use 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 {
1006impl From<ast::LiteralKind> for Literal { 1006impl 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
4use hir_expand::{name::Name, AstId, InFile}; 4use hir_expand::{name::Name, AstId, InFile};
5use std::convert::TryInto;
5use syntax::ast; 6use syntax::ast;
6 7
7use crate::{body::LowerCtx, path::Path}; 8use 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)]
316pub 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
329impl 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
338impl 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
3use std::convert::TryInto;
4
5use hir_def::{
6 builtin_type::BuiltinUint,
7 expr::{Expr, Literal},
8 type_ref::ConstScalar,
9};
10
11use crate::{Const, ConstData, ConstValue, Interner, TyKind};
12
13/// Extension trait for [`Const`]
14pub trait ConstExt {
15 /// Is a [`Const`] unknown?
16 fn is_unknown(&self) -> bool;
17}
18
19impl 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
39pub 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
48pub 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)]
5pub 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
14impl 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 @@
3use std::iter::{repeat, repeat_with}; 3use std::iter::{repeat, repeat_with};
4use std::{mem, sync::Arc}; 4use std::{mem, sync::Arc};
5 5
6use chalk_ir::{cast::Cast, fold::Shift, ConstData, Mutability, TyVariableKind}; 6use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
7use hir_def::{ 7use 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;
15use syntax::ast::RangeOp; 15use syntax::ast::RangeOp;
16 16
17use crate::{ 17use 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
4use crate::{chalk_db, consts::ConstScalar, tls, GenericArg}; 4use crate::{chalk_db, tls, GenericArg};
5use base_db::salsa::InternId; 5use base_db::salsa::InternId;
6use chalk_ir::{Goal, GoalData}; 6use chalk_ir::{Goal, GoalData};
7use hir_def::{ 7use 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};
11use smallvec::SmallVec; 12use 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;
10mod builder; 10mod builder;
11mod chalk_db; 11mod chalk_db;
12mod chalk_ext; 12mod chalk_ext;
13pub mod consteval;
13mod infer; 14mod infer;
14mod interner; 15mod interner;
15mod consts;
16mod lower; 16mod lower;
17mod mapping; 17mod mapping;
18mod op; 18mod op;
@@ -38,9 +38,13 @@ use chalk_ir::{
38 interner::HasInterner, 38 interner::HasInterner,
39 UintTy, 39 UintTy,
40}; 40};
41use hir_def::{expr::ExprId, type_ref::Rawness, TypeParamId}; 41use hir_def::{
42 expr::ExprId,
43 type_ref::{ConstScalar, Rawness},
44 TypeParamId,
45};
42 46
43use crate::{consts::ConstScalar, db::HirDatabase, display::HirDisplay, utils::generics}; 47use crate::{db::HirDatabase, display::HirDisplay, utils::generics};
44 48
45pub use autoderef::autoderef; 49pub use autoderef::autoderef;
46pub use builder::TyBuilder; 50pub 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;
29use syntax::ast; 29use syntax::ast;
30 30
31use crate::{ 31use 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\
494h";
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]
3479fn array_length() {
3480 check_infer(
3481 r#"
3482trait T {
3483 type Output;
3484 fn do_thing(&self) -> Self::Output;
3485}
3486
3487impl T for [u8; 4] {
3488 type Output = usize;
3489 fn do_thing(&self) -> Self::Output {
3490 2
3491 }
3492}
3493
3494impl T for [u8; 2] {
3495 type Output = u8;
3496 fn do_thing(&self) -> Self::Output {
3497 2
3498 }
3499}
3500
3501fn 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]
3537fn const_generics() {
3538 check_infer(
3539 r#"
3540trait T {
3541 type Output;
3542 fn do_thing(&self) -> Self::Output;
3543}
3544
3545impl<const L: usize> T for [u8; L] {
3546 type Output = [u8; L];
3547 fn do_thing(&self) -> Self::Output {
3548 *self
3549 }
3550}
3551
3552fn 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() {
579struct TestStruct { one: i32, two: i64 } 579struct TestStruct { one: i32, two: i64 }
580 580
581fn test_fn() { 581fn 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 {
599struct TestStruct { one: i32 } 599struct TestStruct { one: i32 }
600 600
601impl TestStruct { 601impl 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() {
792pub struct Foo { pub a: i32, pub b: i32 } 792pub struct Foo { pub a: i32, pub b: i32 }
793"#, 793"#,
794 r#" 794 r#"
795fn some(, b: ()) {} 795fn some(, b: () ) {}
796fn items() {} 796fn items() {}
797fn here() {} 797fn 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;
4use itertools::Itertools; 4use itertools::Itertools;
5use syntax::{ 5use 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::{
19pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { 19pub(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 @@
1use hir::Semantics; 1use hir::Semantics;
2use ide_db::base_db::{CrateId, FileId, FilePosition}; 2use ide_db::{
3use ide_db::RootDatabase; 3 base_db::{CrateId, FileId, FilePosition},
4 RootDatabase,
5};
4use itertools::Itertools; 6use itertools::Itertools;
5use syntax::{ 7use 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;
6use hir::{InFile, Semantics}; 6use hir::{InFile, Semantics};
7use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind}; 7use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind};
8use syntax::{ 8use 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/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 33bc6b0f3..0d325f3f3 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -66,11 +66,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
66 <span class="keyword">pub</span> <span class="field declaration">y</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span> 66 <span class="keyword">pub</span> <span class="field declaration">y</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span>
67<span class="brace">}</span> 67<span class="brace">}</span>
68 68
69<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span> 69<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="keyword">where</span> <span class="type_param">Self</span><span class="colon">:</span> <span class="brace">{</span>
70 <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span> 70 <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span>
71<span class="brace">}</span> 71<span class="brace">}</span>
72 72
73<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span> 73<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="keyword">where</span> <span class="self_type">Self</span><span class="colon">:</span> <span class="brace">{</span>
74 <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span> 74 <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
75 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 75 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
76 <span class="brace">}</span> 76 <span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 32f2d9038..8c8878d36 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -39,11 +39,11 @@ struct Foo {
39 pub y: i32, 39 pub y: i32,
40} 40}
41 41
42trait Bar { 42trait Bar where Self: {
43 fn bar(&self) -> i32; 43 fn bar(&self) -> i32;
44} 44}
45 45
46impl Bar for Foo { 46impl Bar for Foo where Self: {
47 fn bar(&self) -> i32 { 47 fn bar(&self) -> i32 {
48 self.x 48 self.x
49 } 49 }
@@ -524,6 +524,11 @@ fn main() {
524fn test_highlight_doc_comment() { 524fn 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/// ```
534impl Foo { 546impl 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#"
207fn main() {
208 let $0v = [0.0; 2];
209}
210"#,
211 r#"
212fn 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#"
222fn 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 { 278enum 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() { 283fn 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) { 299fn 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() { 316fn 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) { 329fn 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) { 335fn 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) { 350fn 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) { 357fn 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) { 372fn 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) { 389fn 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) { 395fn 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) { 412fn 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) { 419fn 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 { 436enum 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() { 441fn 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 { 449enum 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() { 454fn 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 } 608enum A { One, Two }
597 enum B { One, Two } 609enum B { One, Two }
598 610
599 fn main() { 611fn 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 } 618enum A { One, Two }
607 enum B { One, Two } 619enum B { One, Two }
608 620
609 fn main() { 621fn 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 } 640enum A { One, Two }
629 enum B { One, Two } 641enum B { One, Two }
630 642
631 fn main() { 643fn 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 } 650enum A { One, Two }
639 enum B { One, Two } 651enum B { One, Two }
640 652
641 fn main() { 653fn 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 } 752enum A { One, Two }
741 enum B { One, Two } 753enum B { One, Two }
742 754
743 fn main() { 755fn 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 } 774enum A { One, Two }
763 775
764 fn main() { 776fn 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 } 783enum A { One, Two }
772 784
773 fn main() { 785fn 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 } 801enum A { As }
790 802
791 fn foo(a: &A) { 803fn 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 } 809enum A { As }
798 810
799 fn foo(a: &A) { 811fn 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 { 822enum A {
811 Es { x: usize, y: usize } 823 Es { x: usize, y: usize }
812 } 824}
813 825
814 fn foo(a: &mut A) { 826fn 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 { 832enum A {
821 Es { x: usize, y: usize } 833 Es { x: usize, y: usize }
822 } 834}
823 835
824 fn foo(a: &mut A) { 836fn 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 } 850enum E { X, Y }
839 851
840 fn main() { 852fn 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 } 865enum E { X, Y }
854 866
855 fn main() { 867fn 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 } 874enum E { X, Y }
863 875
864 fn main() { 876fn 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 } } 891mod foo { pub enum E { X, Y } }
880 use foo::E::X; 892use foo::E::X;
881 893
882 fn main() { 894fn 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 } } 901mod foo { pub enum E { X, Y } }
890 use foo::E::X; 902use foo::E::X;
891 903
892 fn main() { 904fn 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 } 919enum A { One, Two }
908 fn foo(a: A) { 920fn 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 } 929enum A { One, Two }
918 fn foo(a: A) { 930fn 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 } 947enum A { One, Two }
936 fn foo(a: A) { 948fn 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 } 955enum A { One, Two }
944 fn foo(a: A) { 956fn 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, } 972enum A { One, Two, }
961 fn foo(a: A) { 973fn 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, } 980enum A { One, Two, }
969 fn foo(a: A) { 981fn 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 {
1016fn foo(t: Test) { 1028fn 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#"
1032macro_rules! m { ($expr:expr) => {$expr}}
1020enum Test { 1033enum 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 {
141impl NeedsLifetime { 141impl 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 @@
1use std::borrow::Cow; 1use std::borrow::Cow;
2 2
3use syntax::{ast, AstToken, TextRange, TextSize}; 3use syntax::{ast, ast::IsString, AstToken, TextRange, TextSize};
4 4
5use crate::{AssistContext, AssistId, AssistKind, Assists}; 5use 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 @@
1use syntax::{ast, AstToken, SyntaxKind::STRING}; 1use syntax::{ast, ast::IsString, AstToken, SyntaxKind::STRING};
2 2
3use crate::{AssistContext, AssistId, AssistKind, Assists}; 3use crate::{AssistContext, AssistId, AssistKind, Assists};
4 4
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 8ce4aa242..73dcba694 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -422,7 +422,7 @@ fn semantic_token_type_and_modifiers(
422 let type_ = match highlight.tag { 422 let type_ = match highlight.tag {
423 HlTag::Symbol(symbol) => match symbol { 423 HlTag::Symbol(symbol) => match symbol {
424 SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE, 424 SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE,
425 SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE, 425 SymbolKind::Impl => semantic_tokens::TYPE_ALIAS,
426 SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, 426 SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY,
427 SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, 427 SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER,
428 SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER, 428 SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER,
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
11use crate::{ 11use 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
32fn make_multiline<N>(node: N) -> N
33where
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
64impl 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
140impl ast::Path { 28impl 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
217impl 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]
291pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { 106pub 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
417fn 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
432fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { 231fn 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
19use super::NameOwner; 19use 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
333impl ast::Fn { 314impl ast::Fn {
@@ -346,6 +327,113 @@ impl ast::Fn {
346 } 327 }
347} 328}
348 329
330impl 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
346impl 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
371impl 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
411fn 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)]
350mod tests { 438mod 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
146pub 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
168impl IsString for ast::String {}
169
146impl ast::String { 170impl 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
216impl IsString for ast::ByteString {}
217
212impl ast::ByteString { 218impl 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]