Merge #10699

10699: internal: Make CompletionItem `label` and `lookup` fields `SmolStr`s r=Veykril a=Veykril

This replaces a bunch of String clones with SmolStr clones, though also makes a few parts a bit more expensive(mainly things involving `format!`ted strings as labels).


Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index 417ba60..f867a90 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -66,24 +66,21 @@
         }
     }
 
-    pub fn get_cfg_keys(&self) -> Vec<&SmolStr> {
-        self.enabled
-            .iter()
-            .map(|x| match x {
-                CfgAtom::Flag(key) => key,
-                CfgAtom::KeyValue { key, .. } => key,
-            })
-            .collect()
+    pub fn get_cfg_keys(&self) -> impl Iterator<Item = &SmolStr> {
+        self.enabled.iter().map(|x| match x {
+            CfgAtom::Flag(key) => key,
+            CfgAtom::KeyValue { key, .. } => key,
+        })
     }
 
-    pub fn get_cfg_values(&self, cfg_key: &str) -> Vec<&SmolStr> {
-        self.enabled
-            .iter()
-            .filter_map(|x| match x {
-                CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value),
-                _ => None,
-            })
-            .collect()
+    pub fn get_cfg_values<'a>(
+        &'a self,
+        cfg_key: &'a str,
+    ) -> impl Iterator<Item = &'a SmolStr> + 'a {
+        self.enabled.iter().filter_map(move |x| match x {
+            CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value),
+            _ => None,
+        })
     }
 }
 
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index 2482418..e86f38a 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -104,7 +104,7 @@
                 let mut item = CompletionItem::new(
                     CompletionItemKind::Attribute,
                     ctx.source_range(),
-                    name.to_string(),
+                    name.to_smol_str(),
                 );
                 if let Some(docs) = mac.docs(ctx.sema.db) {
                     item.documentation(docs);
diff --git a/crates/ide_completion/src/completions/attribute/cfg.rs b/crates/ide_completion/src/completions/attribute/cfg.rs
index c83e171..1a10531 100644
--- a/crates/ide_completion/src/completions/attribute/cfg.rs
+++ b/crates/ide_completion/src/completions/attribute/cfg.rs
@@ -31,13 +31,11 @@
         Some("target_endian") => ["little", "big"].into_iter().for_each(add_completion),
         Some(name) => {
             if let Some(krate) = ctx.krate {
-                krate.potential_cfg(ctx.db).get_cfg_values(&name).iter().for_each(|s| {
-                    let mut item = CompletionItem::new(
-                        CompletionItemKind::Attribute,
-                        ctx.source_range(),
-                        s.as_str(),
-                    );
-                    item.insert_text(format!(r#""{}""#, s));
+                krate.potential_cfg(ctx.db).get_cfg_values(&name).cloned().for_each(|s| {
+                    let insert_text = format!(r#""{}""#, s);
+                    let mut item =
+                        CompletionItem::new(CompletionItemKind::Attribute, ctx.source_range(), s);
+                    item.insert_text(insert_text);
 
                     acc.add(item.build());
                 })
@@ -45,12 +43,9 @@
         }
         None => {
             if let Some(krate) = ctx.krate {
-                krate.potential_cfg(ctx.db).get_cfg_keys().iter().for_each(|s| {
-                    let item = CompletionItem::new(
-                        CompletionItemKind::Attribute,
-                        ctx.source_range(),
-                        s.as_str(),
-                    );
+                krate.potential_cfg(ctx.db).get_cfg_keys().cloned().for_each(|s| {
+                    let item =
+                        CompletionItem::new(CompletionItemKind::Attribute, ctx.source_range(), s);
                     acc.add(item.build());
                 })
             }
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs
index 33b8481..b903231 100644
--- a/crates/ide_completion/src/completions/attribute/derive.rs
+++ b/crates/ide_completion/src/completions/attribute/derive.rs
@@ -3,7 +3,7 @@
 use ide_db::helpers::{import_assets::ImportAssets, insert_use::ImportScope, FamousDefs};
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
-use syntax::{ast, SyntaxKind};
+use syntax::{ast, SmolStr, SyntaxKind};
 
 use crate::{
     completions::flyimport::compute_fuzzy_completion_order_key,
@@ -30,7 +30,6 @@
         }
 
         let name = name.to_smol_str();
-        let label;
         let (label, lookup) = match core.zip(mac.module(ctx.db).map(|it| it.krate())) {
             // show derive dependencies for `core`/`std` derives
             Some((core, mac_krate)) if core == mac_krate => {
@@ -48,13 +47,13 @@
                         },
                     ));
                     let lookup = components.join(", ");
-                    label = components.iter().rev().join(", ");
-                    (label.as_str(), Some(lookup))
+                    let label = Itertools::intersperse(components.into_iter().rev(), ", ");
+                    (SmolStr::from_iter(label), Some(lookup))
                 } else {
-                    (&*name, None)
+                    (name, None)
                 }
             }
-            _ => (&*name, None),
+            _ => (name, None),
         };
 
         let mut item =
@@ -68,7 +67,7 @@
         item.add_to(acc);
     }
 
-    flyimport_attribute(ctx, acc);
+    flyimport_attribute(acc, ctx);
 }
 
 fn get_derives_in_scope(ctx: &CompletionContext) -> Vec<(hir::Name, MacroDef)> {
@@ -83,7 +82,7 @@
     result
 }
 
-fn flyimport_attribute(ctx: &CompletionContext, acc: &mut Completions) -> Option<()> {
+fn flyimport_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
     if ctx.token.kind() != SyntaxKind::IDENT {
         return None;
     };
@@ -115,7 +114,7 @@
                 let mut item = CompletionItem::new(
                     CompletionItemKind::Attribute,
                     ctx.source_range(),
-                    mac.name(ctx.db)?.to_string(),
+                    mac.name(ctx.db)?.to_smol_str(),
                 );
                 item.add_import(ImportEdit { import, scope: import_scope.clone() });
                 if let Some(docs) = mac.docs(ctx.db) {
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs
index e910c1d..5acda46 100644
--- a/crates/ide_completion/src/completions/fn_param.rs
+++ b/crates/ide_completion/src/completions/fn_param.rs
@@ -30,6 +30,7 @@
         }
         func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
             if let Some(pat) = param.pat() {
+                // FIXME: We should be able to turn these into SmolStr without having to allocate a String
                 let text = param.syntax().text().to_string();
                 let lookup = pat.syntax().text().to_string();
                 params.entry(text).or_insert(lookup);
@@ -59,7 +60,7 @@
 
     let self_completion_items = ["self", "&self", "mut self", "&mut self"];
     if ctx.impl_def.is_some() && me?.param_list()?.params().next().is_none() {
-        self_completion_items.iter().for_each(|self_item| {
+        self_completion_items.into_iter().for_each(|self_item| {
             add_new_item_to_acc(ctx, acc, self_item.to_string(), self_item.to_string())
         });
     }
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index afb8df2..36d25d5 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -27,7 +27,7 @@
         return;
     }
 
-    let mut add_keyword = |kw, snippet| add_keyword(ctx, acc, kw, snippet);
+    let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet);
 
     let expects_assoc_item = ctx.expects_assoc_item();
     let has_block_expr_parent = ctx.has_block_expr_parent();
@@ -157,7 +157,7 @@
     )
 }
 
-fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
+fn add_keyword(acc: &mut Completions, ctx: &CompletionContext, kw: &str, snippet: &str) {
     let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw);
 
     match ctx.config.snippet_cap {
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index e9d39ed..385bf59 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -93,7 +93,7 @@
                 if ctx.in_use_tree() {
                     if let hir::ScopeDef::Unknown = def {
                         if let Some(ast::NameLike::NameRef(name_ref)) = ctx.name_syntax.as_ref() {
-                            if name_ref.syntax().text() == name.to_string().as_str() {
+                            if name_ref.syntax().text() == name.to_smol_str().as_str() {
                                 // for `use self::foo$0`, don't suggest `foo` as a completion
                                 cov_mark::hit!(dont_complete_current_use);
                                 continue;
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index fc6ef58..46da7cf 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -133,7 +133,7 @@
     func: hir::Function,
     impl_def: hir::Impl,
 ) {
-    let fn_name = func.name(ctx.db).to_string();
+    let fn_name = func.name(ctx.db).to_smol_str();
 
     let label = if func.assoc_fn_params(ctx.db).is_empty() {
         format!("fn {}()", fn_name)
@@ -205,12 +205,12 @@
     ctx: &CompletionContext,
     type_alias: hir::TypeAlias,
 ) {
-    let alias_name = type_alias.name(ctx.db).to_string();
+    let alias_name = type_alias.name(ctx.db).to_smol_str();
 
     let snippet = format!("type {} = ", alias_name);
 
     let range = replacement_range(ctx, type_def_node);
-    let mut item = CompletionItem::new(SymbolKind::TypeAlias, ctx.source_range(), snippet.clone());
+    let mut item = CompletionItem::new(SymbolKind::TypeAlias, ctx.source_range(), &snippet);
     item.text_edit(TextEdit::replace(range, snippet))
         .lookup_by(alias_name)
         .set_documentation(type_alias.docs(ctx.db));
@@ -224,7 +224,7 @@
     const_: hir::Const,
     impl_def: hir::Impl,
 ) {
-    let const_name = const_.name(ctx.db).map(|n| n.to_string());
+    let const_name = const_.name(ctx.db).map(|n| n.to_smol_str());
 
     if let Some(const_name) = const_name {
         if let Some(source) = const_.source(ctx.db) {
@@ -238,8 +238,7 @@
                 let snippet = make_const_compl_syntax(&transformed_const);
 
                 let range = replacement_range(ctx, const_def_node);
-                let mut item =
-                    CompletionItem::new(SymbolKind::Const, ctx.source_range(), snippet.clone());
+                let mut item = CompletionItem::new(SymbolKind::Const, ctx.source_range(), &snippet);
                 item.text_edit(TextEdit::replace(range, snippet))
                     .lookup_by(const_name)
                     .set_documentation(const_.docs(ctx.db));
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 3f0accf..ebe227d 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -12,8 +12,8 @@
     SymbolKind,
 };
 use smallvec::SmallVec;
-use stdx::{format_to, impl_from, never};
-use syntax::{algo, TextRange};
+use stdx::{impl_from, never};
+use syntax::{algo, SmolStr, TextRange};
 use text_edit::TextEdit;
 
 /// `CompletionItem` describes a single completion variant in the editor pop-up.
@@ -22,7 +22,7 @@
 #[derive(Clone)]
 pub struct CompletionItem {
     /// Label in the completion pop up which identifies completion.
-    label: String,
+    label: SmolStr,
     /// Range of identifier that is being completed.
     ///
     /// It should be used primarily for UI, but we also use this to convert
@@ -46,7 +46,7 @@
     ///
     /// That is, in `foo.bar$0` lookup of `abracadabra` will be accepted (it
     /// contains `bar` sub sequence), and `quux` will rejected.
-    lookup: Option<String>,
+    lookup: Option<SmolStr>,
 
     /// Additional info to show in the UI pop up.
     detail: Option<String>,
@@ -268,7 +268,7 @@
     pub(crate) fn new(
         kind: impl Into<CompletionItemKind>,
         source_range: TextRange,
-        label: impl Into<String>,
+        label: impl Into<SmolStr>,
     ) -> Builder {
         let label = label.into();
         Builder {
@@ -379,13 +379,13 @@
 pub(crate) struct Builder {
     source_range: TextRange,
     imports_to_add: SmallVec<[ImportEdit; 1]>,
-    trait_name: Option<String>,
-    label: String,
+    trait_name: Option<SmolStr>,
+    label: SmolStr,
     insert_text: Option<String>,
     is_snippet: bool,
     detail: Option<String>,
     documentation: Option<Documentation>,
-    lookup: Option<String>,
+    lookup: Option<SmolStr>,
     kind: CompletionItemKind,
     text_edit: Option<TextEdit>,
     deprecated: bool,
@@ -400,25 +400,21 @@
 
         let mut label = self.label;
         let mut lookup = self.lookup;
-        let mut insert_text = self.insert_text;
+        let insert_text = self.insert_text.unwrap_or_else(|| label.to_string());
 
         if let [import_edit] = &*self.imports_to_add {
             // snippets can have multiple imports, but normal completions only have up to one
             if let Some(original_path) = import_edit.import.original_path.as_ref() {
                 lookup = lookup.or_else(|| Some(label.clone()));
-                insert_text = insert_text.or_else(|| Some(label.clone()));
-                format_to!(label, " (use {})", original_path)
+                label = SmolStr::from(format!("{} (use {})", label, original_path));
             }
         } else if let Some(trait_name) = self.trait_name {
-            insert_text = insert_text.or_else(|| Some(label.clone()));
-            format_to!(label, " (as {})", trait_name)
+            label = SmolStr::from(format!("{} (as {})", label, trait_name));
         }
 
         let text_edit = match self.text_edit {
             Some(it) => it,
-            None => {
-                TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone()))
-            }
+            None => TextEdit::replace(self.source_range, insert_text),
         };
 
         CompletionItem {
@@ -437,16 +433,16 @@
             import_to_add: self.imports_to_add,
         }
     }
-    pub(crate) fn lookup_by(&mut self, lookup: impl Into<String>) -> &mut Builder {
+    pub(crate) fn lookup_by(&mut self, lookup: impl Into<SmolStr>) -> &mut Builder {
         self.lookup = Some(lookup.into());
         self
     }
-    pub(crate) fn label(&mut self, label: impl Into<String>) -> &mut Builder {
+    pub(crate) fn label(&mut self, label: impl Into<SmolStr>) -> &mut Builder {
         self.label = label.into();
         self
     }
-    pub(crate) fn trait_name(&mut self, trait_name: impl Into<String>) -> &mut Builder {
-        self.trait_name = Some(trait_name.into());
+    pub(crate) fn trait_name(&mut self, trait_name: SmolStr) -> &mut Builder {
+        self.trait_name = Some(trait_name);
         self
     }
     pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder {
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 4180d70..5ae52f3 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -83,11 +83,11 @@
     ty: &hir::Type,
 ) -> CompletionItem {
     let is_deprecated = ctx.is_deprecated(field);
-    let name = field.name(ctx.db()).to_string();
+    let name = field.name(ctx.db()).to_smol_str();
     let mut item = CompletionItem::new(
         SymbolKind::Field,
         ctx.source_range(),
-        receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)),
+        receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name).into()),
     );
     item.set_relevance(CompletionRelevance {
         type_match: compute_type_match(ctx.completion, ty),
@@ -97,7 +97,7 @@
     item.detail(ty.display(ctx.db()).to_string())
         .set_documentation(field.docs(ctx.db()))
         .set_deprecated(is_deprecated)
-        .lookup_by(name.as_str());
+        .lookup_by(name.clone());
     let is_keyword = SyntaxKind::from_keyword(name.as_str()).is_some();
     if is_keyword && !matches!(name.as_str(), "self" | "crate" | "super" | "Self") {
         item.insert_text(format!("r#{}", name));
@@ -199,7 +199,7 @@
             let mut item = CompletionItem::new(
                 CompletionItemKind::UnresolvedReference,
                 ctx.source_range(),
-                local_name.to_string(),
+                local_name.to_smol_str(),
             );
             if let Some(import_to_add) = import_to_add {
                 item.add_import(import_to_add);
@@ -208,7 +208,7 @@
         }
     };
 
-    let local_name = local_name.to_string();
+    let local_name = local_name.to_smol_str();
     let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.clone());
     if let hir::ScopeDef::Local(local) = resolution {
         let ty = local.ty(ctx.db());
diff --git a/crates/ide_completion/src/render/const_.rs b/crates/ide_completion/src/render/const_.rs
index 241e004..707b653 100644
--- a/crates/ide_completion/src/render/const_.rs
+++ b/crates/ide_completion/src/render/const_.rs
@@ -2,10 +2,7 @@
 
 use hir::{AsAssocItem, HasSource};
 use ide_db::SymbolKind;
-use syntax::{
-    ast::{Const, HasName},
-    display::const_label,
-};
+use syntax::{ast::Const, display::const_label};
 
 use crate::{item::CompletionItem, render::RenderContext};
 
@@ -27,7 +24,7 @@
     }
 
     fn render(self) -> Option<CompletionItem> {
-        let name = self.name()?;
+        let name = self.const_.name(self.ctx.db())?.to_smol_str();
         let detail = self.detail();
 
         let mut item =
@@ -42,7 +39,7 @@
         let db = self.ctx.db();
         if let Some(actm) = self.const_.as_assoc_item(db) {
             if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
-                item.trait_name(trt.name(db).to_string());
+                item.trait_name(trt.name(db).to_smol_str());
                 item.insert_text(name);
             }
         }
@@ -50,10 +47,6 @@
         Some(item.build())
     }
 
-    fn name(&self) -> Option<String> {
-        self.ast_node.name().map(|name| name.text().to_string())
-    }
-
     fn detail(&self) -> String {
         const_label(&self.ast_node)
     }
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index 7515bb6..f598b41 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -38,7 +38,7 @@
 #[derive(Debug)]
 struct FunctionRender<'a> {
     ctx: RenderContext<'a>,
-    name: String,
+    name: hir::Name,
     receiver: Option<hir::Name>,
     func: hir::Function,
     /// NB: having `ast::Fn` here might or might not be a good idea. The problem
@@ -67,7 +67,7 @@
         fn_: hir::Function,
         is_method: bool,
     ) -> Option<FunctionRender<'a>> {
-        let name = local_name.unwrap_or_else(|| fn_.name(ctx.db())).to_string();
+        let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()));
         let ast_node = fn_.source(ctx.db())?.value;
 
         Some(FunctionRender { ctx, name, receiver, func: fn_, ast_node, is_method })
@@ -77,7 +77,7 @@
         let params = self.params();
         let call = match &self.receiver {
             Some(receiver) => format!("{}.{}", receiver, &self.name),
-            None => self.name.clone(),
+            None => self.name.to_string(),
         };
         let mut item = CompletionItem::new(self.kind(), self.ctx.source_range(), call.clone());
         item.set_documentation(self.ctx.docs(self.func))
@@ -91,7 +91,7 @@
             let db = self.ctx.db();
             if let Some(actm) = self.func.as_assoc_item(db) {
                 if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
-                    item.trait_name(trt.name(db).to_string());
+                    item.trait_name(trt.name(db).to_smol_str());
                 }
             }
         }
@@ -99,7 +99,7 @@
         if let Some(import_to_add) = import_to_add {
             item.add_import(import_to_add);
         }
-        item.lookup_by(self.name);
+        item.lookup_by(self.name.to_smol_str());
 
         let ret_type = self.func.ret_type(self.ctx.db());
         item.set_relevance(CompletionRelevance {
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index b1ddfab..1f76583 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -2,7 +2,7 @@
 
 use hir::HasSource;
 use ide_db::SymbolKind;
-use syntax::display::macro_label;
+use syntax::{display::macro_label, SmolStr};
 
 use crate::{
     context::CallKind,
@@ -23,7 +23,7 @@
 #[derive(Debug)]
 struct MacroRender<'a> {
     ctx: RenderContext<'a>,
-    name: String,
+    name: SmolStr,
     macro_: hir::MacroDef,
     docs: Option<hir::Documentation>,
     bra: &'static str,
@@ -32,7 +32,7 @@
 
 impl<'a> MacroRender<'a> {
     fn new(ctx: RenderContext<'a>, name: hir::Name, macro_: hir::MacroDef) -> MacroRender<'a> {
-        let name = name.to_string();
+        let name = name.to_smol_str();
         let docs = ctx.docs(macro_);
         let docs_str = docs.as_ref().map_or("", |s| s.as_str());
         let (bra, ket) = guess_macro_braces(&name, docs_str);
@@ -47,7 +47,7 @@
         } else {
             Some(self.ctx.source_range())
         }?;
-        let mut item = CompletionItem::new(SymbolKind::Macro, source_range, &self.label());
+        let mut item = CompletionItem::new(SymbolKind::Macro, source_range, self.label());
         item.set_documentation(self.docs.clone())
             .set_deprecated(self.ctx.is_deprecated(self.macro_))
             .set_detail(self.detail());
@@ -72,7 +72,7 @@
             }
             _ => {
                 cov_mark::hit!(dont_insert_macro_call_parens_unncessary);
-                item.insert_text(&self.name);
+                item.insert_text(&*self.name);
             }
         };
 
@@ -84,18 +84,18 @@
             && !matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac))
     }
 
-    fn label(&self) -> String {
+    fn label(&self) -> SmolStr {
         if self.needs_bang() && self.ctx.snippet_cap().is_some() {
-            format!("{}!{}…{}", self.name, self.bra, self.ket)
+            SmolStr::from_iter([&*self.name, "!", self.bra, "…", self.ket])
         } else if self.macro_.kind() == hir::MacroKind::Derive {
-            self.name.to_string()
+            self.name.clone()
         } else {
             self.banged_name()
         }
     }
 
-    fn banged_name(&self) -> String {
-        format!("{}!", self.name)
+    fn banged_name(&self) -> SmolStr {
+        SmolStr::from_iter([&*self.name, "!"])
     }
 
     fn detail(&self) -> Option<String> {
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs
index b703280..e553ec8 100644
--- a/crates/ide_completion/src/render/pattern.rs
+++ b/crates/ide_completion/src/render/pattern.rs
@@ -3,6 +3,7 @@
 use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
 use ide_db::helpers::SnippetCap;
 use itertools::Itertools;
+use syntax::SmolStr;
 
 use crate::{
     context::{ParamKind, PatternContext},
@@ -25,7 +26,7 @@
         return None;
     }
 
-    let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string();
+    let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_smol_str();
     let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &visible_fields, fields_omitted)?;
 
     Some(build_completion(ctx, name, pat, strukt))
@@ -43,8 +44,8 @@
     let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?;
 
     let name = match &path {
-        Some(path) => path.to_string(),
-        None => local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(),
+        Some(path) => path.to_string().into(),
+        None => local_name.unwrap_or_else(|| variant.name(ctx.db())).to_smol_str(),
     };
     let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?;
 
@@ -53,7 +54,7 @@
 
 fn build_completion(
     ctx: RenderContext<'_>,
-    name: String,
+    name: SmolStr,
     pat: String,
     def: impl HasAttrs + Copy,
 ) -> CompletionItem {
diff --git a/crates/ide_completion/src/render/struct_literal.rs b/crates/ide_completion/src/render/struct_literal.rs
index 4eb4f6b..a70015d 100644
--- a/crates/ide_completion/src/render/struct_literal.rs
+++ b/crates/ide_completion/src/render/struct_literal.rs
@@ -3,6 +3,7 @@
 use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
 use ide_db::helpers::SnippetCap;
 use itertools::Itertools;
+use syntax::SmolStr;
 
 use crate::{render::RenderContext, CompletionItem, CompletionItemKind};
 
@@ -21,7 +22,7 @@
         return None;
     }
 
-    let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string();
+    let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_smol_str();
     let literal = render_literal(&ctx, &name, strukt.kind(ctx.db()), &visible_fields)?;
 
     Some(build_completion(ctx, name, literal, strukt))
@@ -29,12 +30,15 @@
 
 fn build_completion(
     ctx: RenderContext<'_>,
-    name: String,
+    name: SmolStr,
     literal: String,
     def: impl HasAttrs + Copy,
 ) -> CompletionItem {
-    let mut item =
-        CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), name + " {…}");
+    let mut item = CompletionItem::new(
+        CompletionItemKind::Snippet,
+        ctx.source_range(),
+        SmolStr::from_iter([&name, " {…}"]),
+    );
     item.set_documentation(ctx.docs(def)).set_deprecated(ctx.is_deprecated(def)).detail(&literal);
     match ctx.snippet_cap() {
         Some(snippet_cap) => item.insert_snippet(snippet_cap, literal),
diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs
index 7b6d2fa..6e6c120 100644
--- a/crates/ide_completion/src/render/type_alias.rs
+++ b/crates/ide_completion/src/render/type_alias.rs
@@ -58,7 +58,7 @@
         let db = self.ctx.db();
         if let Some(actm) = self.type_alias.as_assoc_item(db) {
             if let Some(trt) = actm.containing_trait_or_trait_impl(db) {
-                item.trait_name(trt.name(db).to_string());
+                item.trait_name(trt.name(db).to_smol_str());
                 item.insert_text(name);
             }
         }
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 080697b..c3d08ac 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -18,7 +18,7 @@
 rustc-hash = "1.1.0"
 once_cell = "1.3.1"
 indexmap = "1.4.0"
-smol_str = "0.1.15"
+smol_str = "0.1.21"
 
 stdx = { path = "../stdx", version = "0.0.0" }
 text_edit = { path = "../text_edit", version = "0.0.0" }