aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_arena/src/lib.rs3
-rw-r--r--crates/ra_hir/src/code_model.rs7
-rw-r--r--crates/ra_hir_def/src/body.rs2
-rw-r--r--crates/ra_hir_def/src/body/lower.rs4
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs113
-rw-r--r--crates/ra_hir_ty/src/infer.rs5
-rw-r--r--crates/ra_hir_ty/src/lib.rs12
-rw-r--r--crates/ra_hir_ty/src/lower.rs42
-rw-r--r--crates/ra_hir_ty/src/tests.rs32
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs33
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs30
-rw-r--r--crates/ra_hir_ty/src/utils.rs48
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs44
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs33
-rw-r--r--crates/ra_ide/src/completion/presentation.rs58
-rw-r--r--crates/ra_ide/src/snapshots/highlight_injection.html1
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html71
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html1
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html1
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs2
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs1
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs2
-rw-r--r--crates/ra_prof/Cargo.toml1
-rw-r--r--crates/ra_prof/src/hprof.rs246
-rw-r--r--crates/ra_prof/src/lib.rs398
-rw-r--r--crates/ra_prof/src/tree.rs84
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs1
-rw-r--r--crates/rust-analyzer/src/config.rs6
-rw-r--r--crates/rust-analyzer/src/conv.rs4
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs78
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs2
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs83
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs6
33 files changed, 867 insertions, 587 deletions
diff --git a/crates/ra_arena/src/lib.rs b/crates/ra_arena/src/lib.rs
index ea98d5444..441fbb3cb 100644
--- a/crates/ra_arena/src/lib.rs
+++ b/crates/ra_arena/src/lib.rs
@@ -96,6 +96,9 @@ impl<T> Arena<T> {
96 pub const fn new() -> Arena<T> { 96 pub const fn new() -> Arena<T> {
97 Arena { data: Vec::new() } 97 Arena { data: Vec::new() }
98 } 98 }
99 pub fn clear(&mut self) {
100 self.data.clear();
101 }
99 102
100 pub fn len(&self) -> usize { 103 pub fn len(&self) -> usize {
101 self.data.len() 104 self.data.len()
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 3f645a1dd..fb788736d 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -1136,6 +1136,13 @@ impl Type {
1136 matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { .. }, .. })) 1136 matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { .. }, .. }))
1137 } 1137 }
1138 1138
1139 pub fn is_fn(&self) -> bool {
1140 matches!(&self.ty.value,
1141 Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(..), .. }) |
1142 Ty::Apply(ApplicationTy { ctor: TypeCtor::FnPtr { .. }, .. })
1143 )
1144 }
1145
1139 pub fn contains_unknown(&self) -> bool { 1146 pub fn contains_unknown(&self) -> bool {
1140 return go(&self.ty.value); 1147 return go(&self.ty.value);
1141 1148
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 3b169440a..890cefcaf 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -27,7 +27,7 @@ use crate::{
27 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, 27 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
28}; 28};
29 29
30/// A subser of Exander that only deals with cfg attributes. We only need it to 30/// A subset of Exander that only deals with cfg attributes. We only need it to
31/// avoid cyclic queries in crate def map during enum processing. 31/// avoid cyclic queries in crate def map during enum processing.
32pub(crate) struct CfgExpander { 32pub(crate) struct CfgExpander {
33 cfg_options: CfgOptions, 33 cfg_options: CfgOptions,
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 0caedd8d8..571603854 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -141,6 +141,10 @@ impl ExprCollector<'_> {
141 141
142 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { 142 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
143 let syntax_ptr = AstPtr::new(&expr); 143 let syntax_ptr = AstPtr::new(&expr);
144 let attrs = self.expander.parse_attrs(&expr);
145 if !self.expander.is_cfg_enabled(&attrs) {
146 return self.missing_expr();
147 }
144 match expr { 148 match expr {
145 ast::Expr::IfExpr(e) => { 149 ast::Expr::IfExpr(e) => {
146 let then_branch = self.collect_block_opt(e.then_branch()); 150 let then_branch = self.collect_block_opt(e.then_branch());
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
index bb45b0f1d..e60f879a3 100644
--- a/crates/ra_hir_expand/src/builtin_derive.rs
+++ b/crates/ra_hir_expand/src/builtin_derive.rs
@@ -9,7 +9,7 @@ use ra_syntax::{
9}; 9};
10 10
11use crate::db::AstDatabase; 11use crate::db::AstDatabase;
12use crate::{name, quote, LazyMacroId, MacroDefId, MacroDefKind}; 12use crate::{name, quote, LazyMacroId, MacroCallId, MacroDefId, MacroDefKind};
13 13
14macro_rules! register_builtin { 14macro_rules! register_builtin {
15 ( $($trait:ident => $expand:ident),* ) => { 15 ( $($trait:ident => $expand:ident),* ) => {
@@ -153,76 +153,113 @@ fn expand_simple_derive(
153 Ok(expanded) 153 Ok(expanded)
154} 154}
155 155
156fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree {
157 // FIXME: make hygiene works for builtin derive macro
158 // such that $crate can be used here.
159
160 let m: MacroCallId = id.into();
161 let file_id = m.as_file().original_file(db);
162 let cg = db.crate_graph();
163 let krates = db.relevant_crates(file_id);
164 let krate = match krates.get(0) {
165 Some(krate) => krate,
166 None => {
167 let tt = quote! { core };
168 return tt.token_trees[0].clone();
169 }
170 };
171
172 // XXX
173 // All crates except core itself should have a dependency on core,
174 // We detect `core` by seeing whether it doesn't have such a dependency.
175 let tt = if cg[*krate].dependencies.iter().any(|dep| dep.name == "core") {
176 quote! { core }
177 } else {
178 quote! { crate }
179 };
180
181 tt.token_trees[0].clone()
182}
183
156fn copy_expand( 184fn copy_expand(
157 _db: &dyn AstDatabase, 185 db: &dyn AstDatabase,
158 _id: LazyMacroId, 186 id: LazyMacroId,
159 tt: &tt::Subtree, 187 tt: &tt::Subtree,
160) -> Result<tt::Subtree, mbe::ExpandError> { 188) -> Result<tt::Subtree, mbe::ExpandError> {
161 expand_simple_derive(tt, quote! { std::marker::Copy }) 189 let krate = find_builtin_crate(db, id);
190 expand_simple_derive(tt, quote! { #krate::marker::Copy })
162} 191}
163 192
164fn clone_expand( 193fn clone_expand(
165 _db: &dyn AstDatabase, 194 db: &dyn AstDatabase,
166 _id: LazyMacroId, 195 id: LazyMacroId,
167 tt: &tt::Subtree, 196 tt: &tt::Subtree,
168) -> Result<tt::Subtree, mbe::ExpandError> { 197) -> Result<tt::Subtree, mbe::ExpandError> {
169 expand_simple_derive(tt, quote! { std::clone::Clone }) 198 let krate = find_builtin_crate(db, id);
199 expand_simple_derive(tt, quote! { #krate::clone::Clone })
170} 200}
171 201
172fn default_expand( 202fn default_expand(
173 _db: &dyn AstDatabase, 203 db: &dyn AstDatabase,
174 _id: LazyMacroId, 204 id: LazyMacroId,
175 tt: &tt::Subtree, 205 tt: &tt::Subtree,
176) -> Result<tt::Subtree, mbe::ExpandError> { 206) -> Result<tt::Subtree, mbe::ExpandError> {
177 expand_simple_derive(tt, quote! { std::default::Default }) 207 let krate = find_builtin_crate(db, id);
208 expand_simple_derive(tt, quote! { #krate::default::Default })
178} 209}
179 210
180fn debug_expand( 211fn debug_expand(
181 _db: &dyn AstDatabase, 212 db: &dyn AstDatabase,
182 _id: LazyMacroId, 213 id: LazyMacroId,
183 tt: &tt::Subtree, 214 tt: &tt::Subtree,
184) -> Result<tt::Subtree, mbe::ExpandError> { 215) -> Result<tt::Subtree, mbe::ExpandError> {
185 expand_simple_derive(tt, quote! { std::fmt::Debug }) 216 let krate = find_builtin_crate(db, id);
217 expand_simple_derive(tt, quote! { #krate::fmt::Debug })
186} 218}
187 219
188fn hash_expand( 220fn hash_expand(
189 _db: &dyn AstDatabase, 221 db: &dyn AstDatabase,
190 _id: LazyMacroId, 222 id: LazyMacroId,
191 tt: &tt::Subtree, 223 tt: &tt::Subtree,
192) -> Result<tt::Subtree, mbe::ExpandError> { 224) -> Result<tt::Subtree, mbe::ExpandError> {
193 expand_simple_derive(tt, quote! { std::hash::Hash }) 225 let krate = find_builtin_crate(db, id);
226 expand_simple_derive(tt, quote! { #krate::hash::Hash })
194} 227}
195 228
196fn eq_expand( 229fn eq_expand(
197 _db: &dyn AstDatabase, 230 db: &dyn AstDatabase,
198 _id: LazyMacroId, 231 id: LazyMacroId,
199 tt: &tt::Subtree, 232 tt: &tt::Subtree,
200) -> Result<tt::Subtree, mbe::ExpandError> { 233) -> Result<tt::Subtree, mbe::ExpandError> {
201 expand_simple_derive(tt, quote! { std::cmp::Eq }) 234 let krate = find_builtin_crate(db, id);
235 expand_simple_derive(tt, quote! { #krate::cmp::Eq })
202} 236}
203 237
204fn partial_eq_expand( 238fn partial_eq_expand(
205 _db: &dyn AstDatabase, 239 db: &dyn AstDatabase,
206 _id: LazyMacroId, 240 id: LazyMacroId,
207 tt: &tt::Subtree, 241 tt: &tt::Subtree,
208) -> Result<tt::Subtree, mbe::ExpandError> { 242) -> Result<tt::Subtree, mbe::ExpandError> {
209 expand_simple_derive(tt, quote! { std::cmp::PartialEq }) 243 let krate = find_builtin_crate(db, id);
244 expand_simple_derive(tt, quote! { #krate::cmp::PartialEq })
210} 245}
211 246
212fn ord_expand( 247fn ord_expand(
213 _db: &dyn AstDatabase, 248 db: &dyn AstDatabase,
214 _id: LazyMacroId, 249 id: LazyMacroId,
215 tt: &tt::Subtree, 250 tt: &tt::Subtree,
216) -> Result<tt::Subtree, mbe::ExpandError> { 251) -> Result<tt::Subtree, mbe::ExpandError> {
217 expand_simple_derive(tt, quote! { std::cmp::Ord }) 252 let krate = find_builtin_crate(db, id);
253 expand_simple_derive(tt, quote! { #krate::cmp::Ord })
218} 254}
219 255
220fn partial_ord_expand( 256fn partial_ord_expand(
221 _db: &dyn AstDatabase, 257 db: &dyn AstDatabase,
222 _id: LazyMacroId, 258 id: LazyMacroId,
223 tt: &tt::Subtree, 259 tt: &tt::Subtree,
224) -> Result<tt::Subtree, mbe::ExpandError> { 260) -> Result<tt::Subtree, mbe::ExpandError> {
225 expand_simple_derive(tt, quote! { std::cmp::PartialOrd }) 261 let krate = find_builtin_crate(db, id);
262 expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })
226} 263}
227 264
228#[cfg(test)] 265#[cfg(test)]
@@ -234,8 +271,18 @@ mod tests {
234 271
235 fn expand_builtin_derive(s: &str, name: Name) -> String { 272 fn expand_builtin_derive(s: &str, name: Name) -> String {
236 let def = find_builtin_derive(&name).unwrap(); 273 let def = find_builtin_derive(&name).unwrap();
274 let fixture = format!(
275 r#"//- /main.rs crate:main deps:core
276<|>
277{}
278//- /lib.rs crate:core
279// empty
280"#,
281 s
282 );
237 283
238 let (db, file_id) = TestDB::with_single_file(&s); 284 let (db, file_pos) = TestDB::with_position(&fixture);
285 let file_id = file_pos.file_id;
239 let parsed = db.parse(file_id); 286 let parsed = db.parse(file_id);
240 let items: Vec<_> = 287 let items: Vec<_> =
241 parsed.syntax_node().descendants().filter_map(ast::ModuleItem::cast).collect(); 288 parsed.syntax_node().descendants().filter_map(ast::ModuleItem::cast).collect();
@@ -264,7 +311,7 @@ mod tests {
264 known::Copy, 311 known::Copy,
265 ); 312 );
266 313
267 assert_eq!(expanded, "impl< >std::marker::CopyforFoo< >{}"); 314 assert_eq!(expanded, "impl< >core::marker::CopyforFoo< >{}");
268 } 315 }
269 316
270 #[test] 317 #[test]
@@ -279,7 +326,7 @@ mod tests {
279 326
280 assert_eq!( 327 assert_eq!(
281 expanded, 328 expanded,
282 "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}" 329 "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"
283 ); 330 );
284 } 331 }
285 332
@@ -297,7 +344,7 @@ mod tests {
297 344
298 assert_eq!( 345 assert_eq!(
299 expanded, 346 expanded,
300 "impl<T0:std::marker::Copy,T1:std::marker::Copy>std::marker::CopyforFoo<T0,T1>{}" 347 "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"
301 ); 348 );
302 } 349 }
303 350
@@ -313,7 +360,7 @@ mod tests {
313 360
314 assert_eq!( 361 assert_eq!(
315 expanded, 362 expanded,
316 "impl<T0:std::clone::Clone,T1:std::clone::Clone>std::clone::CloneforFoo<T0,T1>{}" 363 "impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}"
317 ); 364 );
318 } 365 }
319} 366}
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index 6a53be621..bd4ef69a0 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -667,7 +667,7 @@ impl Expectation {
667} 667}
668 668
669mod diagnostics { 669mod diagnostics {
670 use hir_def::{expr::ExprId, src::HasSource, FunctionId, Lookup}; 670 use hir_def::{expr::ExprId, FunctionId};
671 use hir_expand::diagnostics::DiagnosticSink; 671 use hir_expand::diagnostics::DiagnosticSink;
672 672
673 use crate::{db::HirDatabase, diagnostics::NoSuchField}; 673 use crate::{db::HirDatabase, diagnostics::NoSuchField};
@@ -686,10 +686,9 @@ mod diagnostics {
686 ) { 686 ) {
687 match self { 687 match self {
688 InferenceDiagnostic::NoSuchField { expr, field } => { 688 InferenceDiagnostic::NoSuchField { expr, field } => {
689 let source = owner.lookup(db.upcast()).source(db.upcast());
690 let (_, source_map) = db.body_with_source_map(owner.into()); 689 let (_, source_map) = db.body_with_source_map(owner.into());
691 let field = source_map.field_syntax(*expr, *field); 690 let field = source_map.field_syntax(*expr, *field);
692 sink.push(NoSuchField { file: source.file_id, field: field.value }) 691 sink.push(NoSuchField { file: field.file_id, field: field.value })
693 } 692 }
694 } 693 }
695 } 694 }
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index 279c06d65..a8ef32ec5 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -487,6 +487,18 @@ impl<T> Binders<T> {
487 pub fn new(num_binders: usize, value: T) -> Self { 487 pub fn new(num_binders: usize, value: T) -> Self {
488 Self { num_binders, value } 488 Self { num_binders, value }
489 } 489 }
490
491 pub fn as_ref(&self) -> Binders<&T> {
492 Binders { num_binders: self.num_binders, value: &self.value }
493 }
494
495 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Binders<U> {
496 Binders { num_binders: self.num_binders, value: f(self.value) }
497 }
498
499 pub fn filter_map<U>(self, f: impl FnOnce(T) -> Option<U>) -> Option<Binders<U>> {
500 Some(Binders { num_binders: self.num_binders, value: f(self.value)? })
501 }
490} 502}
491 503
492impl<T: Clone> Binders<&T> { 504impl<T: Clone> Binders<&T> {
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index b57214296..a6f893037 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -28,11 +28,11 @@ use crate::{
28 db::HirDatabase, 28 db::HirDatabase,
29 primitive::{FloatTy, IntTy}, 29 primitive::{FloatTy, IntTy},
30 utils::{ 30 utils::{
31 all_super_traits, associated_type_by_name_including_super_traits, generics, make_mut_slice, 31 all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
32 variant_data, 32 make_mut_slice, variant_data,
33 }, 33 },
34 Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, 34 Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate,
35 ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, 35 ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
36}; 36};
37 37
38#[derive(Debug)] 38#[derive(Debug)]
@@ -256,7 +256,7 @@ impl Ty {
256 if remaining_segments.len() == 1 { 256 if remaining_segments.len() == 1 {
257 // resolve unselected assoc types 257 // resolve unselected assoc types
258 let segment = remaining_segments.first().unwrap(); 258 let segment = remaining_segments.first().unwrap();
259 (Ty::select_associated_type(ctx, ty, res, segment), None) 259 (Ty::select_associated_type(ctx, res, segment), None)
260 } else if remaining_segments.len() > 1 { 260 } else if remaining_segments.len() > 1 {
261 // FIXME report error (ambiguous associated type) 261 // FIXME report error (ambiguous associated type)
262 (Ty::Unknown, None) 262 (Ty::Unknown, None)
@@ -380,21 +380,20 @@ impl Ty {
380 380
381 fn select_associated_type( 381 fn select_associated_type(
382 ctx: &TyLoweringContext<'_>, 382 ctx: &TyLoweringContext<'_>,
383 self_ty: Ty,
384 res: Option<TypeNs>, 383 res: Option<TypeNs>,
385 segment: PathSegment<'_>, 384 segment: PathSegment<'_>,
386 ) -> Ty { 385 ) -> Ty {
387 let traits_from_env: Vec<_> = match res { 386 let traits_from_env: Vec<_> = match res {
388 Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) { 387 Some(TypeNs::SelfType(impl_id)) => match ctx.db.impl_trait(impl_id) {
389 None => return Ty::Unknown, 388 None => return Ty::Unknown,
390 Some(trait_ref) => vec![trait_ref.value.trait_], 389 Some(trait_ref) => vec![trait_ref.value],
391 }, 390 },
392 Some(TypeNs::GenericParam(param_id)) => { 391 Some(TypeNs::GenericParam(param_id)) => {
393 let predicates = ctx.db.generic_predicates_for_param(param_id); 392 let predicates = ctx.db.generic_predicates_for_param(param_id);
394 let mut traits_: Vec<_> = predicates 393 let mut traits_: Vec<_> = predicates
395 .iter() 394 .iter()
396 .filter_map(|pred| match &pred.value { 395 .filter_map(|pred| match &pred.value {
397 GenericPredicate::Implemented(tr) => Some(tr.trait_), 396 GenericPredicate::Implemented(tr) => Some(tr.clone()),
398 _ => None, 397 _ => None,
399 }) 398 })
400 .collect(); 399 .collect();
@@ -404,20 +403,37 @@ impl Ty {
404 if generics.params.types[param_id.local_id].provenance 403 if generics.params.types[param_id.local_id].provenance
405 == TypeParamProvenance::TraitSelf 404 == TypeParamProvenance::TraitSelf
406 { 405 {
407 traits_.push(trait_id); 406 let trait_ref = TraitRef {
407 trait_: trait_id,
408 substs: Substs::bound_vars(&generics, DebruijnIndex::INNERMOST),
409 };
410 traits_.push(trait_ref);
408 } 411 }
409 } 412 }
410 traits_ 413 traits_
411 } 414 }
412 _ => return Ty::Unknown, 415 _ => return Ty::Unknown,
413 }; 416 };
414 let traits = traits_from_env.into_iter().flat_map(|t| all_super_traits(ctx.db.upcast(), t)); 417 let traits = traits_from_env.into_iter().flat_map(|t| all_super_trait_refs(ctx.db, t));
415 for t in traits { 418 for t in traits {
416 if let Some(associated_ty) = ctx.db.trait_data(t).associated_type_by_name(&segment.name) 419 if let Some(associated_ty) =
420 ctx.db.trait_data(t.trait_).associated_type_by_name(&segment.name)
417 { 421 {
418 let substs = 422 let substs = match ctx.type_param_mode {
419 Substs::build_for_def(ctx.db, t).push(self_ty).fill_with_unknown().build(); 423 TypeParamLoweringMode::Placeholder => {
420 // FIXME handle type parameters on the segment 424 // if we're lowering to placeholders, we have to put
425 // them in now
426 let s = Substs::type_params(
427 ctx.db,
428 ctx.resolver
429 .generic_def()
430 .expect("there should be generics if there's a generic param"),
431 );
432 t.substs.subst_bound_vars(&s)
433 }
434 TypeParamLoweringMode::Variable => t.substs,
435 };
436 // FIXME handle (forbid) type parameters on the segment
421 return Ty::Projection(ProjectionTy { associated_ty, parameters: substs }); 437 return Ty::Projection(ProjectionTy { associated_ty, parameters: substs });
422 } 438 }
423 } 439 }
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index b6a96bb5c..588d81282 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -391,6 +391,38 @@ fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
391} 391}
392 392
393#[test] 393#[test]
394fn no_such_field_with_feature_flag_diagnostics_on_block_expr() {
395 let diagnostics = TestDB::with_files(
396 r#"
397 //- /lib.rs crate:foo cfg:feature=foo
398 struct S {
399 #[cfg(feature = "foo")]
400 foo: u32,
401 #[cfg(not(feature = "foo"))]
402 bar: u32,
403 }
404
405 impl S {
406 fn new(bar: u32) -> Self {
407 #[cfg(feature = "foo")]
408 {
409 Self { foo: bar }
410 }
411 #[cfg(not(feature = "foo"))]
412 {
413 Self { bar }
414 }
415 }
416 }
417 "#,
418 )
419 .diagnostics()
420 .0;
421
422 assert_snapshot!(diagnostics, @r###""###);
423}
424
425#[test]
394fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() { 426fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() {
395 let diagnostics = TestDB::with_files( 427 let diagnostics = TestDB::with_files(
396 r#" 428 r#"
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index 6b5267232..5ddecbdc6 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -622,14 +622,14 @@ fn main() {
622fn infer_derive_clone_simple() { 622fn infer_derive_clone_simple() {
623 let (db, pos) = TestDB::with_position( 623 let (db, pos) = TestDB::with_position(
624 r#" 624 r#"
625//- /main.rs crate:main deps:std 625//- /main.rs crate:main deps:core
626#[derive(Clone)] 626#[derive(Clone)]
627struct S; 627struct S;
628fn test() { 628fn test() {
629 S.clone()<|>; 629 S.clone()<|>;
630} 630}
631 631
632//- /lib.rs crate:std 632//- /lib.rs crate:core
633#[prelude_import] 633#[prelude_import]
634use clone::*; 634use clone::*;
635mod clone { 635mod clone {
@@ -643,10 +643,35 @@ mod clone {
643} 643}
644 644
645#[test] 645#[test]
646fn infer_derive_clone_in_core() {
647 let (db, pos) = TestDB::with_position(
648 r#"
649//- /lib.rs crate:core
650#[prelude_import]
651use clone::*;
652mod clone {
653 trait Clone {
654 fn clone(&self) -> Self;
655 }
656}
657#[derive(Clone)]
658pub struct S;
659
660//- /main.rs crate:main deps:core
661use core::S;
662fn test() {
663 S.clone()<|>;
664}
665"#,
666 );
667 assert_eq!("S", type_at_pos(&db, pos));
668}
669
670#[test]
646fn infer_derive_clone_with_params() { 671fn infer_derive_clone_with_params() {
647 let (db, pos) = TestDB::with_position( 672 let (db, pos) = TestDB::with_position(
648 r#" 673 r#"
649//- /main.rs crate:main deps:std 674//- /main.rs crate:main deps:core
650#[derive(Clone)] 675#[derive(Clone)]
651struct S; 676struct S;
652#[derive(Clone)] 677#[derive(Clone)]
@@ -656,7 +681,7 @@ fn test() {
656 (Wrapper(S).clone(), Wrapper(NonClone).clone())<|>; 681 (Wrapper(S).clone(), Wrapper(NonClone).clone())<|>;
657} 682}
658 683
659//- /lib.rs crate:std 684//- /lib.rs crate:core
660#[prelude_import] 685#[prelude_import]
661use clone::*; 686use clone::*;
662mod clone { 687mod clone {
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index f51cdd496..e555c879a 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -1898,6 +1898,36 @@ fn test() {
1898} 1898}
1899 1899
1900#[test] 1900#[test]
1901fn unselected_projection_chalk_fold() {
1902 let t = type_at(
1903 r#"
1904//- /main.rs
1905trait Interner {}
1906trait Fold<I: Interner, TI = I> {
1907 type Result;
1908}
1909
1910struct Ty<I: Interner> {}
1911impl<I: Interner, TI: Interner> Fold<I, TI> for Ty<I> {
1912 type Result = Ty<TI>;
1913}
1914
1915fn fold<I: Interner, T>(interner: &I, t: T) -> T::Result
1916where
1917 T: Fold<I, I>,
1918{
1919 loop {}
1920}
1921
1922fn foo<I: Interner>(interner: &I, t: Ty<I>) {
1923 fold(interner, t)<|>;
1924}
1925"#,
1926 );
1927 assert_eq!(t, "Ty<I>");
1928}
1929
1930#[test]
1901fn trait_impl_self_ty() { 1931fn trait_impl_self_ty() {
1902 let t = type_at( 1932 let t = type_at(
1903 r#" 1933 r#"
diff --git a/crates/ra_hir_ty/src/utils.rs b/crates/ra_hir_ty/src/utils.rs
index 1e5022fa4..f98350bf9 100644
--- a/crates/ra_hir_ty/src/utils.rs
+++ b/crates/ra_hir_ty/src/utils.rs
@@ -14,6 +14,8 @@ use hir_def::{
14}; 14};
15use hir_expand::name::{name, Name}; 15use hir_expand::name::{name, Name};
16 16
17use crate::{db::HirDatabase, GenericPredicate, TraitRef};
18
17fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { 19fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
18 let resolver = trait_.resolver(db); 20 let resolver = trait_.resolver(db);
19 // returning the iterator directly doesn't easily work because of 21 // returning the iterator directly doesn't easily work because of
@@ -41,6 +43,28 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
41 .collect() 43 .collect()
42} 44}
43 45
46fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<TraitRef> {
47 // returning the iterator directly doesn't easily work because of
48 // lifetime problems, but since there usually shouldn't be more than a
49 // few direct traits this should be fine (we could even use some kind of
50 // SmallVec if performance is a concern)
51 let generic_params = db.generic_params(trait_ref.trait_.into());
52 let trait_self = match generic_params.find_trait_self_param() {
53 Some(p) => TypeParamId { parent: trait_ref.trait_.into(), local_id: p },
54 None => return Vec::new(),
55 };
56 db.generic_predicates_for_param(trait_self)
57 .iter()
58 .filter_map(|pred| {
59 pred.as_ref().filter_map(|pred| match pred {
60 GenericPredicate::Implemented(tr) => Some(tr.clone()),
61 _ => None,
62 })
63 })
64 .map(|pred| pred.subst(&trait_ref.substs))
65 .collect()
66}
67
44/// Returns an iterator over the whole super trait hierarchy (including the 68/// Returns an iterator over the whole super trait hierarchy (including the
45/// trait itself). 69/// trait itself).
46pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { 70pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
@@ -62,6 +86,30 @@ pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<Tra
62 result 86 result
63} 87}
64 88
89/// Given a trait ref (`Self: Trait`), builds all the implied trait refs for
90/// super traits. The original trait ref will be included. So the difference to
91/// `all_super_traits` is that we keep track of type parameters; for example if
92/// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get
93/// `Self: OtherTrait<i32>`.
94pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> Vec<TraitRef> {
95 // we need to take care a bit here to avoid infinite loops in case of cycles
96 // (i.e. if we have `trait A: B; trait B: A;`)
97 let mut result = vec![trait_ref];
98 let mut i = 0;
99 while i < result.len() {
100 let t = &result[i];
101 // yeah this is quadratic, but trait hierarchies should be flat
102 // enough that this doesn't matter
103 for tt in direct_super_trait_refs(db, t) {
104 if !result.iter().any(|tr| tr.trait_ == tt.trait_) {
105 result.push(tt);
106 }
107 }
108 i += 1;
109 }
110 result
111}
112
65/// Finds a path from a trait to one of its super traits. Returns an empty 113/// Finds a path from a trait to one of its super traits. Returns an empty
66/// vector if there is no path. 114/// vector if there is no path.
67pub(super) fn find_super_trait_path( 115pub(super) fn find_super_trait_path(
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index 56cd086c6..f559f2b97 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -4,7 +4,7 @@ use hir::ScopeDef;
4use test_utils::tested_by; 4use test_utils::tested_by;
5 5
6use crate::completion::{CompletionContext, Completions}; 6use crate::completion::{CompletionContext, Completions};
7use hir::{Adt, ModuleDef}; 7use hir::{Adt, ModuleDef, Type};
8use ra_syntax::AstNode; 8use ra_syntax::AstNode;
9 9
10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
@@ -15,7 +15,9 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
15 return; 15 return;
16 } 16 }
17 17
18 complete_enum_variants(acc, ctx); 18 if let Some(ty) = &ctx.expected_type {
19 complete_enum_variants(acc, ctx, ty);
20 }
19 21
20 if ctx.is_pat_binding_or_const { 22 if ctx.is_pat_binding_or_const {
21 return; 23 return;
@@ -34,26 +36,24 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
34 }); 36 });
35} 37}
36 38
37fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext) { 39fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
38 if let Some(ty) = ctx.expected_type_of(&ctx.token.parent()) { 40 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
39 if let Some(Adt::Enum(enum_data)) = ty.as_adt() { 41 let variants = enum_data.variants(ctx.db);
40 let variants = enum_data.variants(ctx.db); 42
41 43 let module = if let Some(module) = ctx.scope().module() {
42 let module = if let Some(module) = ctx.scope().module() { 44 // Compute path from the completion site if available.
43 // Compute path from the completion site if available. 45 module
44 module 46 } else {
45 } else { 47 // Otherwise fall back to the enum's definition site.
46 // Otherwise fall back to the enum's definition site. 48 enum_data.module(ctx.db)
47 enum_data.module(ctx.db) 49 };
48 }; 50
49 51 for variant in variants {
50 for variant in variants { 52 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
51 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { 53 // Variants with trivial paths are already added by the existing completion logic,
52 // Variants with trivial paths are already added by the existing completion logic, 54 // so we should avoid adding these twice
53 // so we should avoid adding these twice 55 if path.segments.len() > 1 {
54 if path.segments.len() > 1 { 56 acc.add_enum_variant(ctx, variant, Some(path.to_string()));
55 acc.add_enum_variant(ctx, variant, Some(path.to_string()));
56 }
57 } 57 }
58 } 58 }
59 } 59 }
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 5f2797e41..118fceb2e 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -5,7 +5,7 @@ use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
8 ast, AstNode, 8 ast, match_ast, AstNode,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, 10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11}; 11};
@@ -26,6 +26,7 @@ pub(crate) struct CompletionContext<'a> {
26 /// The token before the cursor, in the macro-expanded file. 26 /// The token before the cursor, in the macro-expanded file.
27 pub(super) token: SyntaxToken, 27 pub(super) token: SyntaxToken,
28 pub(super) krate: Option<hir::Crate>, 28 pub(super) krate: Option<hir::Crate>,
29 pub(super) expected_type: Option<Type>,
29 pub(super) name_ref_syntax: Option<ast::NameRef>, 30 pub(super) name_ref_syntax: Option<ast::NameRef>,
30 pub(super) function_syntax: Option<ast::FnDef>, 31 pub(super) function_syntax: Option<ast::FnDef>,
31 pub(super) use_item_syntax: Option<ast::UseItem>, 32 pub(super) use_item_syntax: Option<ast::UseItem>,
@@ -93,6 +94,7 @@ impl<'a> CompletionContext<'a> {
93 token, 94 token,
94 offset: position.offset, 95 offset: position.offset,
95 krate, 96 krate,
97 expected_type: None,
96 name_ref_syntax: None, 98 name_ref_syntax: None,
97 function_syntax: None, 99 function_syntax: None,
98 use_item_syntax: None, 100 use_item_syntax: None,
@@ -175,23 +177,30 @@ impl<'a> CompletionContext<'a> {
175 self.sema.scope_at_offset(&self.token.parent(), self.offset) 177 self.sema.scope_at_offset(&self.token.parent(), self.offset)
176 } 178 }
177 179
178 pub(crate) fn expected_type_of(&self, node: &SyntaxNode) -> Option<Type> {
179 for ancestor in node.ancestors() {
180 if let Some(pat) = ast::Pat::cast(ancestor.clone()) {
181 return self.sema.type_of_pat(&pat);
182 } else if let Some(expr) = ast::Expr::cast(ancestor) {
183 return self.sema.type_of_expr(&expr);
184 }
185 }
186 None
187 }
188
189 fn fill( 180 fn fill(
190 &mut self, 181 &mut self,
191 original_file: &SyntaxNode, 182 original_file: &SyntaxNode,
192 file_with_fake_ident: SyntaxNode, 183 file_with_fake_ident: SyntaxNode,
193 offset: TextSize, 184 offset: TextSize,
194 ) { 185 ) {
186 // FIXME: this is wrong in at least two cases:
187 // * when there's no token `foo(<|>)`
188 // * when there is a token, but it happens to have type of it's own
189 self.expected_type = self
190 .token
191 .ancestors()
192 .find_map(|node| {
193 let ty = match_ast! {
194 match node {
195 ast::Pat(it) => self.sema.type_of_pat(&it),
196 ast::Expr(it) => self.sema.type_of_expr(&it),
197 _ => return None,
198 }
199 };
200 Some(ty)
201 })
202 .flatten();
203
195 // First, let's try to complete a reference to some declaration. 204 // First, let's try to complete a reference to some declaration.
196 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { 205 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
197 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. 206 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 7633cd7fd..77d354376 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -349,6 +349,14 @@ impl Builder {
349 if ctx.use_item_syntax.is_some() || ctx.is_call { 349 if ctx.use_item_syntax.is_some() || ctx.is_call {
350 return self; 350 return self;
351 } 351 }
352
353 // Don't add parentheses if the expected type is some function reference.
354 if let Some(ty) = &ctx.expected_type {
355 if ty.is_fn() {
356 return self;
357 }
358 }
359
352 let cap = match ctx.config.snippet_cap { 360 let cap = match ctx.config.snippet_cap {
353 Some(it) => it, 361 Some(it) => it,
354 None => return self, 362 None => return self,
@@ -749,6 +757,54 @@ mod tests {
749 } 757 }
750 758
751 #[test] 759 #[test]
760 fn no_call_parens_if_fn_ptr_needed() {
761 assert_debug_snapshot!(
762 do_reference_completion(
763 r"
764 fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8) {}
765
766 struct ManualVtable {
767 method: fn(u8, u8, u8, u8, u8),
768 }
769
770 fn main() -> ManualVtable {
771 ManualVtable {
772 method: some<|>
773 }
774 }
775 "
776 ),
777 @r###"
778 [
779 CompletionItem {
780 label: "ManualVtable",
781 source_range: 295..299,
782 delete: 295..299,
783 insert: "ManualVtable",
784 kind: Struct,
785 },
786 CompletionItem {
787 label: "main",
788 source_range: 295..299,
789 delete: 295..299,
790 insert: "main",
791 kind: Function,
792 detail: "fn main() -> ManualVtable",
793 },
794 CompletionItem {
795 label: "somefn",
796 source_range: 295..299,
797 delete: 295..299,
798 insert: "somefn",
799 kind: Function,
800 detail: "fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8)",
801 },
802 ]
803 "###
804 );
805 }
806
807 #[test]
752 fn arg_snippets_for_method_call() { 808 fn arg_snippets_for_method_call() {
753 assert_debug_snapshot!( 809 assert_debug_snapshot!(
754 do_reference_completion( 810 do_reference_completion(
@@ -1179,7 +1235,7 @@ mod tests {
1179 1235
1180 #[test] 1236 #[test]
1181 fn test_struct_field_completion_in_record_lit() { 1237 fn test_struct_field_completion_in_record_lit() {
1182 covers!(test_struct_field_completion_in_func_call); 1238 covers!(test_struct_field_completion_in_record_lit);
1183 assert_debug_snapshot!( 1239 assert_debug_snapshot!(
1184 do_reference_completion( 1240 do_reference_completion(
1185 r" 1241 r"
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
index 6ec13bd80..ea026d7a0 100644
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ b/crates/ra_ide/src/snapshots/highlight_injection.html
@@ -20,6 +20,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
20.macro { color: #94BFF3; } 20.macro { color: #94BFF3; }
21.module { color: #AFD8AF; } 21.module { color: #AFD8AF; }
22.variable { color: #DCDCCC; } 22.variable { color: #DCDCCC; }
23.format_specifier { color: #CC696B; }
23.mutable { text-decoration: underline; } 24.mutable { text-decoration: underline; }
24 25
25.keyword { color: #F0DFAF; font-weight: bold; } 26.keyword { color: #F0DFAF; font-weight: bold; }
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
index 433f2e0c5..de06daf72 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/src/snapshots/highlight_strings.html
@@ -20,6 +20,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
20.macro { color: #94BFF3; } 20.macro { color: #94BFF3; }
21.module { color: #AFD8AF; } 21.module { color: #AFD8AF; }
22.variable { color: #DCDCCC; } 22.variable { color: #DCDCCC; }
23.format_specifier { color: #CC696B; }
23.mutable { text-decoration: underline; } 24.mutable { text-decoration: underline; }
24 25
25.keyword { color: #F0DFAF; font-weight: bold; } 26.keyword { color: #F0DFAF; font-weight: bold; }
@@ -40,43 +41,43 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
40<span class="keyword">fn</span> <span class="function declaration">main</span>() { 41<span class="keyword">fn</span> <span class="function declaration">main</span>() {
41 <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span> 42 <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
42 <span class="macro">println!</span>(<span class="string_literal">"Hello"</span>); <span class="comment">// =&gt; "Hello"</span> 43 <span class="macro">println!</span>(<span class="string_literal">"Hello"</span>); <span class="comment">// =&gt; "Hello"</span>
43 <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); <span class="comment">// =&gt; "Hello, world!"</span> 44 <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); <span class="comment">// =&gt; "Hello, world!"</span>
44 <span class="macro">println!</span>(<span class="string_literal">"The number is </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>); <span class="comment">// =&gt; "The number is 1"</span> 45 <span class="macro">println!</span>(<span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>); <span class="comment">// =&gt; "The number is 1"</span>
45 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">?</span><span class="attribute">}</span><span class="string_literal">"</span>, (<span class="numeric_literal">3</span>, <span class="numeric_literal">4</span>)); <span class="comment">// =&gt; "(3, 4)"</span> 46 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal">"</span>, (<span class="numeric_literal">3</span>, <span class="numeric_literal">4</span>)); <span class="comment">// =&gt; "(3, 4)"</span>
46 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">value</span><span class="attribute">}</span><span class="string_literal">"</span>, value=<span class="numeric_literal">4</span>); <span class="comment">// =&gt; "4"</span> 47 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span>, value=<span class="numeric_literal">4</span>); <span class="comment">// =&gt; "4"</span>
47 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "1 2"</span> 48 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "1 2"</span>
48 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">42</span>); <span class="comment">// =&gt; "0042" with leading zerosV</span> 49 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">42</span>); <span class="comment">// =&gt; "0042" with leading zerosV</span>
49 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1 1 2"</span> 50 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1 1 2"</span>
50 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">argument</span><span class="attribute">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// =&gt; "test"</span> 51 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// =&gt; "test"</span>
51 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1"</span> 52 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1"</span>
52 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">a</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="variable">c</span><span class="attribute">}</span><span class="string_literal"> </span><span class="attribute">{</span><span class="variable">b</span><span class="attribute">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// =&gt; "a 3 b"</span> 53 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// =&gt; "a 3 b"</span>
53 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); 54 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
54 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">1</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>); 55 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>);
55 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>); 56 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>);
56 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="variable">width</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>); 57 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>);
57 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">&lt;</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); 58 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
58 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">-</span><span class="attribute">&lt;</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); 59 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
59 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">^</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); 60 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
60 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">&gt;</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); 61 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>);
61 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">+</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); 62 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
62 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>); 63 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="string_literal">}!"</span>, <span class="numeric_literal">27</span>);
63 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>); 64 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>);
64 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>); 65 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, -<span class="numeric_literal">5</span>);
65 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>); 66 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">27</span>);
66 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">5</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>); 67 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
67 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">1</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">0</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>); 68 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
68 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="numeric_literal">0</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="numeric_literal">1</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); 69 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
69 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); 70 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
70 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="numeric_literal">2</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>); 71 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
71 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal"> is </span><span class="attribute">{</span><span class="variable">number</span><span class="attribute">:</span><span class="attribute">.</span><span class="variable">prec</span><span class="attribute">$</span><span class="attribute">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>); 72 <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>);
72 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 fractional digits"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>); 73 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>);
73 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>); 74 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
74 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">, `</span><span class="attribute">{</span><span class="variable">name</span><span class="attribute">:</span><span class="attribute">&gt;</span><span class="numeric_literal">8</span><span class="attribute">.</span><span class="attribute">*</span><span class="attribute">}</span><span class="string_literal">` has 3 right-aligned characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>); 75 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span>, <span class="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
75 <span class="macro">println!</span>(<span class="string_literal">"Hello {{}}"</span>); 76 <span class="macro">println!</span>(<span class="string_literal">"Hello {{}}"</span>);
76 <span class="macro">println!</span>(<span class="string_literal">"{{ Hello"</span>); 77 <span class="macro">println!</span>(<span class="string_literal">"{{ Hello"</span>);
77 78
78 <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="attribute">{</span><span class="attribute">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); 79 <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>);
79 80
80 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">\x41</span><span class="attribute">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>); 81 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>);
81 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="attribute">{</span><span class="variable">ничоси</span><span class="attribute">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>); 82 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>);
82}</code></pre> \ No newline at end of file 83}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index ccb1fc751..4b12fe823 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -20,6 +20,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
20.macro { color: #94BFF3; } 20.macro { color: #94BFF3; }
21.module { color: #AFD8AF; } 21.module { color: #AFD8AF; }
22.variable { color: #DCDCCC; } 22.variable { color: #DCDCCC; }
23.format_specifier { color: #CC696B; }
23.mutable { text-decoration: underline; } 24.mutable { text-decoration: underline; }
24 25
25.keyword { color: #F0DFAF; font-weight: bold; } 26.keyword { color: #F0DFAF; font-weight: bold; }
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 3df82c45f..11e1f3e44 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -20,6 +20,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
20.macro { color: #94BFF3; } 20.macro { color: #94BFF3; }
21.module { color: #AFD8AF; } 21.module { color: #AFD8AF; }
22.variable { color: #DCDCCC; } 22.variable { color: #DCDCCC; }
23.format_specifier { color: #CC696B; }
23.mutable { text-decoration: underline; } 24.mutable { text-decoration: underline; }
24 25
25.keyword { color: #F0DFAF; font-weight: bold; } 26.keyword { color: #F0DFAF; font-weight: bold; }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index be0f8c827..6658c7bb2 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -290,7 +290,7 @@ fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> {
290 | FormatSpecifier::DollarSign 290 | FormatSpecifier::DollarSign
291 | FormatSpecifier::Dot 291 | FormatSpecifier::Dot
292 | FormatSpecifier::Asterisk 292 | FormatSpecifier::Asterisk
293 | FormatSpecifier::QuestionMark => HighlightTag::Attribute, 293 | FormatSpecifier::QuestionMark => HighlightTag::FormatSpecifier,
294 FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral, 294 FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral,
295 FormatSpecifier::Identifier => HighlightTag::Local, 295 FormatSpecifier::Identifier => HighlightTag::Local,
296 }) 296 })
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index 010db4017..ff0eeeb52 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -79,6 +79,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
79.macro { color: #94BFF3; } 79.macro { color: #94BFF3; }
80.module { color: #AFD8AF; } 80.module { color: #AFD8AF; }
81.variable { color: #DCDCCC; } 81.variable { color: #DCDCCC; }
82.format_specifier { color: #CC696B; }
82.mutable { text-decoration: underline; } 83.mutable { text-decoration: underline; }
83 84
84.keyword { color: #F0DFAF; font-weight: bold; } 85.keyword { color: #F0DFAF; font-weight: bold; }
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index f2c421654..be1a0f12b 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -39,6 +39,7 @@ pub enum HighlightTag {
39 Union, 39 Union,
40 Local, 40 Local,
41 UnresolvedReference, 41 UnresolvedReference,
42 FormatSpecifier,
42} 43}
43 44
44#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 45#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -81,6 +82,7 @@ impl HighlightTag {
81 HighlightTag::Union => "union", 82 HighlightTag::Union => "union",
82 HighlightTag::Local => "variable", 83 HighlightTag::Local => "variable",
83 HighlightTag::UnresolvedReference => "unresolved_reference", 84 HighlightTag::UnresolvedReference => "unresolved_reference",
85 HighlightTag::FormatSpecifier => "format_specifier",
84 } 86 }
85 } 87 }
86} 88}
diff --git a/crates/ra_prof/Cargo.toml b/crates/ra_prof/Cargo.toml
index d15b08992..c33b5121a 100644
--- a/crates/ra_prof/Cargo.toml
+++ b/crates/ra_prof/Cargo.toml
@@ -9,6 +9,7 @@ publish = false
9doctest = false 9doctest = false
10 10
11[dependencies] 11[dependencies]
12ra_arena = { path = "../ra_arena" }
12once_cell = "1.3.1" 13once_cell = "1.3.1"
13backtrace = { version = "0.3.44", optional = true } 14backtrace = { version = "0.3.44", optional = true }
14 15
diff --git a/crates/ra_prof/src/hprof.rs b/crates/ra_prof/src/hprof.rs
new file mode 100644
index 000000000..2b8a90363
--- /dev/null
+++ b/crates/ra_prof/src/hprof.rs
@@ -0,0 +1,246 @@
1//! Simple hierarchical profiler
2use once_cell::sync::Lazy;
3use std::{
4 cell::RefCell,
5 collections::{BTreeMap, HashSet},
6 io::{stderr, Write},
7 sync::{
8 atomic::{AtomicBool, Ordering},
9 RwLock,
10 },
11 time::{Duration, Instant},
12};
13
14use crate::tree::{Idx, Tree};
15
16/// Filtering syntax
17/// env RA_PROFILE=* // dump everything
18/// env RA_PROFILE=foo|bar|baz // enabled only selected entries
19/// env RA_PROFILE=*@3>10 // dump everything, up to depth 3, if it takes more than 10 ms
20pub fn init() {
21 let spec = std::env::var("RA_PROFILE").unwrap_or_default();
22 init_from(&spec);
23}
24
25pub fn init_from(spec: &str) {
26 let filter = if spec.is_empty() { Filter::disabled() } else { Filter::from_spec(spec) };
27 filter.install();
28}
29
30pub type Label = &'static str;
31
32/// This function starts a profiling scope in the current execution stack with a given description.
33/// It returns a Profile structure and measure elapsed time between this method invocation and Profile structure drop.
34/// It supports nested profiling scopes in case when this function invoked multiple times at the execution stack. In this case the profiling information will be nested at the output.
35/// Profiling information is being printed in the stderr.
36///
37/// # Example
38/// ```
39/// use ra_prof::{profile, set_filter, Filter};
40///
41/// let f = Filter::from_spec("profile1|profile2@2");
42/// set_filter(f);
43/// profiling_function1();
44///
45/// fn profiling_function1() {
46/// let _p = profile("profile1");
47/// profiling_function2();
48/// }
49///
50/// fn profiling_function2() {
51/// let _p = profile("profile2");
52/// }
53/// ```
54/// This will print in the stderr the following:
55/// ```text
56/// 0ms - profile
57/// 0ms - profile2
58/// ```
59pub fn profile(label: Label) -> Profiler {
60 assert!(!label.is_empty());
61 let enabled = PROFILING_ENABLED.load(Ordering::Relaxed)
62 && PROFILE_STACK.with(|stack| stack.borrow_mut().push(label));
63 let label = if enabled { Some(label) } else { None };
64 Profiler { label, detail: None }
65}
66
67pub struct Profiler {
68 label: Option<Label>,
69 detail: Option<String>,
70}
71
72impl Profiler {
73 pub fn detail(mut self, detail: impl FnOnce() -> String) -> Profiler {
74 if self.label.is_some() {
75 self.detail = Some(detail())
76 }
77 self
78 }
79}
80
81impl Drop for Profiler {
82 fn drop(&mut self) {
83 match self {
84 Profiler { label: Some(label), detail } => {
85 PROFILE_STACK.with(|stack| {
86 stack.borrow_mut().pop(label, detail.take());
87 });
88 }
89 Profiler { label: None, .. } => (),
90 }
91 }
92}
93
94static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
95static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default);
96thread_local!(static PROFILE_STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new()));
97
98#[derive(Default, Clone, Debug)]
99struct Filter {
100 depth: usize,
101 allowed: HashSet<String>,
102 longer_than: Duration,
103 version: usize,
104}
105
106impl Filter {
107 fn disabled() -> Filter {
108 Filter::default()
109 }
110
111 fn from_spec(mut spec: &str) -> Filter {
112 let longer_than = if let Some(idx) = spec.rfind('>') {
113 let longer_than = spec[idx + 1..].parse().expect("invalid profile longer_than");
114 spec = &spec[..idx];
115 Duration::from_millis(longer_than)
116 } else {
117 Duration::new(0, 0)
118 };
119
120 let depth = if let Some(idx) = spec.rfind('@') {
121 let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
122 spec = &spec[..idx];
123 depth
124 } else {
125 999
126 };
127 let allowed =
128 if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() };
129 Filter { depth, allowed, longer_than, version: 0 }
130 }
131
132 fn install(mut self) {
133 PROFILING_ENABLED.store(self.depth > 0, Ordering::SeqCst);
134 let mut old = FILTER.write().unwrap();
135 self.version = old.version + 1;
136 *old = self;
137 }
138}
139
140struct ProfileStack {
141 starts: Vec<Instant>,
142 filter: Filter,
143 messages: Tree<Message>,
144}
145
146#[derive(Default)]
147struct Message {
148 duration: Duration,
149 label: Label,
150 detail: Option<String>,
151}
152
153impl ProfileStack {
154 fn new() -> ProfileStack {
155 ProfileStack { starts: Vec::new(), messages: Tree::default(), filter: Default::default() }
156 }
157
158 fn push(&mut self, label: Label) -> bool {
159 if self.starts.is_empty() {
160 if let Ok(f) = FILTER.try_read() {
161 if f.version > self.filter.version {
162 self.filter = f.clone();
163 }
164 };
165 }
166 if self.starts.len() > self.filter.depth {
167 return false;
168 }
169 let allowed = &self.filter.allowed;
170 if self.starts.is_empty() && !allowed.is_empty() && !allowed.contains(label) {
171 return false;
172 }
173
174 self.starts.push(Instant::now());
175 self.messages.start();
176 true
177 }
178
179 pub fn pop(&mut self, label: Label, detail: Option<String>) {
180 let start = self.starts.pop().unwrap();
181 let duration = start.elapsed();
182 let level = self.starts.len();
183 self.messages.finish(Message { duration, label, detail });
184 if level == 0 {
185 let longer_than = self.filter.longer_than;
186 // Convert to millis for comparison to avoid problems with rounding
187 // (otherwise we could print `0ms` despite user's `>0` filter when
188 // `duration` is just a few nanos).
189 if duration.as_millis() > longer_than.as_millis() {
190 let stderr = stderr();
191 if let Some(root) = self.messages.root() {
192 print(&self.messages, root, 0, longer_than, &mut stderr.lock());
193 }
194 }
195 self.messages.clear();
196 assert!(self.starts.is_empty())
197 }
198 }
199}
200
201fn print(
202 tree: &Tree<Message>,
203 curr: Idx<Message>,
204 level: u32,
205 longer_than: Duration,
206 out: &mut impl Write,
207) {
208 let current_indent = " ".repeat(level as usize);
209 let detail = tree[curr].detail.as_ref().map(|it| format!(" @ {}", it)).unwrap_or_default();
210 writeln!(
211 out,
212 "{}{:5}ms - {}{}",
213 current_indent,
214 tree[curr].duration.as_millis(),
215 tree[curr].label,
216 detail,
217 )
218 .expect("printing profiling info");
219
220 let mut accounted_for = Duration::default();
221 let mut short_children = BTreeMap::new(); // Use `BTreeMap` to get deterministic output.
222 for child in tree.children(curr) {
223 accounted_for += tree[child].duration;
224
225 if tree[child].duration.as_millis() > longer_than.as_millis() {
226 print(tree, child, level + 1, longer_than, out)
227 } else {
228 let (total_duration, cnt) =
229 short_children.entry(tree[child].label).or_insert((Duration::default(), 0));
230 *total_duration += tree[child].duration;
231 *cnt += 1;
232 }
233 }
234
235 for (child_msg, (duration, count)) in short_children.iter() {
236 let millis = duration.as_millis();
237 writeln!(out, " {}{:5}ms - {} ({} calls)", current_indent, millis, child_msg, count)
238 .expect("printing profiling info");
239 }
240
241 let unaccounted = tree[curr].duration - accounted_for;
242 if tree.children(curr).next().is_some() && unaccounted > longer_than {
243 writeln!(out, " {}{:5}ms - ???", current_indent, unaccounted.as_millis())
244 .expect("printing profiling info");
245 }
246}
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs
index 2d4f68f5e..89df7f04b 100644
--- a/crates/ra_prof/src/lib.rs
+++ b/crates/ra_prof/src/lib.rs
@@ -1,24 +1,17 @@
1//! FIXME: write short doc here 1//! A collection of tools for profiling rust-analyzer.
2 2
3mod memory_usage; 3mod memory_usage;
4#[cfg(feature = "cpu_profiler")] 4#[cfg(feature = "cpu_profiler")]
5mod google_cpu_profiler; 5mod google_cpu_profiler;
6mod hprof;
7mod tree;
6 8
7use std::{ 9use std::cell::RefCell;
8 cell::RefCell,
9 collections::BTreeMap,
10 collections::HashSet,
11 io::{stderr, Write},
12 sync::{
13 atomic::{AtomicBool, Ordering},
14 RwLock,
15 },
16 time::{Duration, Instant},
17};
18
19use once_cell::sync::Lazy;
20 10
21pub use crate::memory_usage::{Bytes, MemoryUsage}; 11pub use crate::{
12 hprof::{init, init_from, profile},
13 memory_usage::{Bytes, MemoryUsage},
14};
22 15
23// We use jemalloc mainly to get heap usage statistics, actual performance 16// We use jemalloc mainly to get heap usage statistics, actual performance
24// difference is not measures. 17// difference is not measures.
@@ -26,298 +19,6 @@ pub use crate::memory_usage::{Bytes, MemoryUsage};
26#[global_allocator] 19#[global_allocator]
27static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; 20static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
28 21
29pub fn init() {
30 set_filter(match std::env::var("RA_PROFILE") {
31 Ok(spec) => Filter::from_spec(&spec),
32 Err(_) => Filter::disabled(),
33 });
34}
35
36/// Set profiling filter. It specifies descriptions allowed to profile.
37/// This is helpful when call stack has too many nested profiling scopes.
38/// Additionally filter can specify maximum depth of profiling scopes nesting.
39///
40/// #Example
41/// ```
42/// use ra_prof::{set_filter, Filter};
43/// let f = Filter::from_spec("profile1|profile2@2");
44/// set_filter(f);
45/// ```
46pub fn set_filter(f: Filter) {
47 PROFILING_ENABLED.store(f.depth > 0, Ordering::SeqCst);
48 let set: HashSet<_> = f.allowed.iter().cloned().collect();
49 let mut old = FILTER.write().unwrap();
50 let filter_data = FilterData {
51 depth: f.depth,
52 allowed: set,
53 longer_than: f.longer_than,
54 version: old.version + 1,
55 };
56 *old = filter_data;
57}
58
59pub type Label = &'static str;
60
61/// This function starts a profiling scope in the current execution stack with a given description.
62/// It returns a Profile structure and measure elapsed time between this method invocation and Profile structure drop.
63/// It supports nested profiling scopes in case when this function invoked multiple times at the execution stack. In this case the profiling information will be nested at the output.
64/// Profiling information is being printed in the stderr.
65///
66/// # Example
67/// ```
68/// use ra_prof::{profile, set_filter, Filter};
69///
70/// let f = Filter::from_spec("profile1|profile2@2");
71/// set_filter(f);
72/// profiling_function1();
73///
74/// fn profiling_function1() {
75/// let _p = profile("profile1");
76/// profiling_function2();
77/// }
78///
79/// fn profiling_function2() {
80/// let _p = profile("profile2");
81/// }
82/// ```
83/// This will print in the stderr the following:
84/// ```text
85/// 0ms - profile
86/// 0ms - profile2
87/// ```
88pub fn profile(label: Label) -> Profiler {
89 assert!(!label.is_empty());
90 if !PROFILING_ENABLED.load(Ordering::Relaxed) {
91 return Profiler { label: None, detail: None };
92 }
93
94 PROFILE_STACK.with(|stack| {
95 let mut stack = stack.borrow_mut();
96 if stack.starts.is_empty() {
97 if let Ok(f) = FILTER.try_read() {
98 if f.version > stack.filter_data.version {
99 stack.filter_data = f.clone();
100 }
101 };
102 }
103 if stack.starts.len() > stack.filter_data.depth {
104 return Profiler { label: None, detail: None };
105 }
106 let allowed = &stack.filter_data.allowed;
107 if stack.starts.is_empty() && !allowed.is_empty() && !allowed.contains(label) {
108 return Profiler { label: None, detail: None };
109 }
110
111 stack.starts.push(Instant::now());
112 Profiler { label: Some(label), detail: None }
113 })
114}
115
116pub struct Profiler {
117 label: Option<Label>,
118 detail: Option<String>,
119}
120
121impl Profiler {
122 pub fn detail(mut self, detail: impl FnOnce() -> String) -> Profiler {
123 if self.label.is_some() {
124 self.detail = Some(detail())
125 }
126 self
127 }
128}
129
130pub struct Filter {
131 depth: usize,
132 allowed: Vec<String>,
133 longer_than: Duration,
134}
135
136impl Filter {
137 // Filtering syntax
138 // env RA_PROFILE=* // dump everything
139 // env RA_PROFILE=foo|bar|baz // enabled only selected entries
140 // env RA_PROFILE=*@3>10 // dump everything, up to depth 3, if it takes more than 10 ms
141 pub fn from_spec(mut spec: &str) -> Filter {
142 let longer_than = if let Some(idx) = spec.rfind('>') {
143 let longer_than = spec[idx + 1..].parse().expect("invalid profile longer_than");
144 spec = &spec[..idx];
145 Duration::from_millis(longer_than)
146 } else {
147 Duration::new(0, 0)
148 };
149
150 let depth = if let Some(idx) = spec.rfind('@') {
151 let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
152 spec = &spec[..idx];
153 depth
154 } else {
155 999
156 };
157 let allowed =
158 if spec == "*" { Vec::new() } else { spec.split('|').map(String::from).collect() };
159 Filter::new(depth, allowed, longer_than)
160 }
161
162 pub fn disabled() -> Filter {
163 Filter::new(0, Vec::new(), Duration::new(0, 0))
164 }
165
166 pub fn new(depth: usize, allowed: Vec<String>, longer_than: Duration) -> Filter {
167 Filter { depth, allowed, longer_than }
168 }
169}
170
171struct ProfileStack {
172 starts: Vec<Instant>,
173 messages: Vec<Message>,
174 filter_data: FilterData,
175}
176
177struct Message {
178 level: usize,
179 duration: Duration,
180 label: Label,
181 detail: Option<String>,
182}
183
184impl ProfileStack {
185 fn new() -> ProfileStack {
186 ProfileStack { starts: Vec::new(), messages: Vec::new(), filter_data: Default::default() }
187 }
188}
189
190#[derive(Default, Clone)]
191struct FilterData {
192 depth: usize,
193 version: usize,
194 allowed: HashSet<String>,
195 longer_than: Duration,
196}
197
198static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
199
200static FILTER: Lazy<RwLock<FilterData>> = Lazy::new(Default::default);
201
202thread_local!(static PROFILE_STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new()));
203
204impl Drop for Profiler {
205 fn drop(&mut self) {
206 match self {
207 Profiler { label: Some(label), detail } => {
208 PROFILE_STACK.with(|stack| {
209 let mut stack = stack.borrow_mut();
210 let start = stack.starts.pop().unwrap();
211 let duration = start.elapsed();
212 let level = stack.starts.len();
213 stack.messages.push(Message { level, duration, label, detail: detail.take() });
214 if level == 0 {
215 let stdout = stderr();
216 let longer_than = stack.filter_data.longer_than;
217 // Convert to millis for comparison to avoid problems with rounding
218 // (otherwise we could print `0ms` despite user's `>0` filter when
219 // `duration` is just a few nanos).
220 if duration.as_millis() > longer_than.as_millis() {
221 print(&stack.messages, longer_than, &mut stdout.lock());
222 }
223 stack.messages.clear();
224 }
225 });
226 }
227 Profiler { label: None, .. } => (),
228 }
229 }
230}
231
232fn print(msgs: &[Message], longer_than: Duration, out: &mut impl Write) {
233 if msgs.is_empty() {
234 return;
235 }
236 let children_map = idx_to_children(msgs);
237 let root_idx = msgs.len() - 1;
238 print_for_idx(root_idx, &children_map, msgs, longer_than, out);
239}
240
241fn print_for_idx(
242 current_idx: usize,
243 children_map: &[Vec<usize>],
244 msgs: &[Message],
245 longer_than: Duration,
246 out: &mut impl Write,
247) {
248 let current = &msgs[current_idx];
249 let current_indent = " ".repeat(current.level);
250 let detail = current.detail.as_ref().map(|it| format!(" @ {}", it)).unwrap_or_default();
251 writeln!(
252 out,
253 "{}{:5}ms - {}{}",
254 current_indent,
255 current.duration.as_millis(),
256 current.label,
257 detail,
258 )
259 .expect("printing profiling info");
260
261 let longer_than_millis = longer_than.as_millis();
262 let children_indices = &children_map[current_idx];
263 let mut accounted_for = Duration::default();
264 let mut short_children = BTreeMap::new(); // Use `BTreeMap` to get deterministic output.
265
266 for child_idx in children_indices.iter() {
267 let child = &msgs[*child_idx];
268 if child.duration.as_millis() > longer_than_millis {
269 print_for_idx(*child_idx, children_map, msgs, longer_than, out);
270 } else {
271 let pair = short_children.entry(child.label).or_insert((Duration::default(), 0));
272 pair.0 += child.duration;
273 pair.1 += 1;
274 }
275 accounted_for += child.duration;
276 }
277
278 for (child_msg, (duration, count)) in short_children.iter() {
279 let millis = duration.as_millis();
280 writeln!(out, " {}{:5}ms - {} ({} calls)", current_indent, millis, child_msg, count)
281 .expect("printing profiling info");
282 }
283
284 let unaccounted_millis = (current.duration - accounted_for).as_millis();
285 if !children_indices.is_empty()
286 && unaccounted_millis > 0
287 && unaccounted_millis > longer_than_millis
288 {
289 writeln!(out, " {}{:5}ms - ???", current_indent, unaccounted_millis)
290 .expect("printing profiling info");
291 }
292}
293
294/// Returns a mapping from an index in the `msgs` to the vector with the indices of its children.
295///
296/// This assumes that the entries in `msgs` are in the order of when the calls to `profile` finish.
297/// In other words, a postorder of the call graph. In particular, the root is the last element of
298/// `msgs`.
299fn idx_to_children(msgs: &[Message]) -> Vec<Vec<usize>> {
300 // Initialize with the index of the root; `msgs` and `ancestors` should be never empty.
301 assert!(!msgs.is_empty());
302 let mut ancestors = vec![msgs.len() - 1];
303 let mut result: Vec<Vec<usize>> = vec![vec![]; msgs.len()];
304 for (idx, msg) in msgs[..msgs.len() - 1].iter().enumerate().rev() {
305 // We need to find the parent of the current message, i.e., the last ancestor that has a
306 // level lower than the current message.
307 while msgs[*ancestors.last().unwrap()].level >= msg.level {
308 ancestors.pop();
309 }
310 result[*ancestors.last().unwrap()].push(idx);
311 ancestors.push(idx);
312 }
313 // Note that above we visited all children from the last to the first one. Let's reverse vectors
314 // to get the more natural order where the first element is the first child.
315 for vec in result.iter_mut() {
316 vec.reverse();
317 }
318 result
319}
320
321/// Prints backtrace to stderr, useful for debugging. 22/// Prints backtrace to stderr, useful for debugging.
322#[cfg(feature = "backtrace")] 23#[cfg(feature = "backtrace")]
323pub fn print_backtrace() { 24pub fn print_backtrace() {
@@ -403,86 +104,3 @@ impl Drop for CpuProfiler {
403pub fn memory_usage() -> MemoryUsage { 104pub fn memory_usage() -> MemoryUsage {
404 MemoryUsage::current() 105 MemoryUsage::current()
405} 106}
406
407#[cfg(test)]
408mod tests {
409 use super::*;
410
411 #[test]
412 fn test_basic_profile() {
413 let s = vec!["profile1".to_string(), "profile2".to_string()];
414 let f = Filter::new(2, s, Duration::new(0, 0));
415 set_filter(f);
416 profiling_function1();
417 }
418
419 fn profiling_function1() {
420 let _p = profile("profile1");
421 profiling_function2();
422 }
423
424 fn profiling_function2() {
425 let _p = profile("profile2");
426 }
427
428 #[test]
429 fn test_longer_than() {
430 let mut result = vec![];
431 let msgs = vec![
432 Message { level: 1, duration: Duration::from_nanos(3), label: "bar", detail: None },
433 Message { level: 1, duration: Duration::from_nanos(2), label: "bar", detail: None },
434 Message { level: 0, duration: Duration::from_millis(1), label: "foo", detail: None },
435 ];
436 print(&msgs, Duration::from_millis(0), &mut result);
437 // The calls to `bar` are so short that they'll be rounded to 0ms and should get collapsed
438 // when printing.
439 assert_eq!(
440 std::str::from_utf8(&result).unwrap(),
441 " 1ms - foo\n 0ms - bar (2 calls)\n"
442 );
443 }
444
445 #[test]
446 fn test_unaccounted_for_topmost() {
447 let mut result = vec![];
448 let msgs = vec![
449 Message { level: 1, duration: Duration::from_millis(2), label: "bar", detail: None },
450 Message { level: 0, duration: Duration::from_millis(5), label: "foo", detail: None },
451 ];
452 print(&msgs, Duration::from_millis(0), &mut result);
453 assert_eq!(
454 std::str::from_utf8(&result).unwrap().lines().collect::<Vec<_>>(),
455 vec![
456 " 5ms - foo",
457 " 2ms - bar",
458 " 3ms - ???",
459 // Dummy comment to improve formatting
460 ]
461 );
462 }
463
464 #[test]
465 fn test_unaccounted_for_multiple_levels() {
466 let mut result = vec![];
467 let msgs = vec![
468 Message { level: 2, duration: Duration::from_millis(3), label: "baz", detail: None },
469 Message { level: 1, duration: Duration::from_millis(5), label: "bar", detail: None },
470 Message { level: 2, duration: Duration::from_millis(2), label: "baz", detail: None },
471 Message { level: 1, duration: Duration::from_millis(4), label: "bar", detail: None },
472 Message { level: 0, duration: Duration::from_millis(9), label: "foo", detail: None },
473 ];
474 print(&msgs, Duration::from_millis(0), &mut result);
475 assert_eq!(
476 std::str::from_utf8(&result).unwrap().lines().collect::<Vec<_>>(),
477 vec![
478 " 9ms - foo",
479 " 5ms - bar",
480 " 3ms - baz",
481 " 2ms - ???",
482 " 4ms - bar",
483 " 2ms - baz",
484 " 2ms - ???",
485 ]
486 );
487 }
488}
diff --git a/crates/ra_prof/src/tree.rs b/crates/ra_prof/src/tree.rs
new file mode 100644
index 000000000..9ea5b5db8
--- /dev/null
+++ b/crates/ra_prof/src/tree.rs
@@ -0,0 +1,84 @@
1//! A simple tree implementation which tries to not allocate all over the place.
2use std::ops;
3
4use ra_arena::Arena;
5
6#[derive(Default)]
7pub struct Tree<T> {
8 nodes: Arena<Node<T>>,
9 current_path: Vec<(Idx<T>, Option<Idx<T>>)>,
10}
11
12pub type Idx<T> = ra_arena::Idx<Node<T>>;
13
14impl<T> Tree<T> {
15 pub fn start(&mut self)
16 where
17 T: Default,
18 {
19 let me = self.nodes.alloc(Node::new(T::default()));
20 if let Some((parent, last_child)) = self.current_path.last_mut() {
21 let slot = match *last_child {
22 Some(last_child) => &mut self.nodes[last_child].next_sibling,
23 None => &mut self.nodes[*parent].first_child,
24 };
25 let prev = slot.replace(me);
26 assert!(prev.is_none());
27 *last_child = Some(me);
28 }
29
30 self.current_path.push((me, None));
31 }
32
33 pub fn finish(&mut self, data: T) {
34 let (me, _last_child) = self.current_path.pop().unwrap();
35 self.nodes[me].data = data;
36 }
37
38 pub fn root(&self) -> Option<Idx<T>> {
39 self.nodes.iter().next().map(|(idx, _)| idx)
40 }
41
42 pub fn children(&self, idx: Idx<T>) -> impl Iterator<Item = Idx<T>> + '_ {
43 NodeIter { nodes: &self.nodes, next: self.nodes[idx].first_child }
44 }
45 pub fn clear(&mut self) {
46 self.nodes.clear();
47 self.current_path.clear();
48 }
49}
50
51impl<T> ops::Index<Idx<T>> for Tree<T> {
52 type Output = T;
53 fn index(&self, index: Idx<T>) -> &T {
54 &self.nodes[index].data
55 }
56}
57
58pub struct Node<T> {
59 data: T,
60 first_child: Option<Idx<T>>,
61 next_sibling: Option<Idx<T>>,
62}
63
64impl<T> Node<T> {
65 fn new(data: T) -> Node<T> {
66 Node { data, first_child: None, next_sibling: None }
67 }
68}
69
70struct NodeIter<'a, T> {
71 nodes: &'a Arena<Node<T>>,
72 next: Option<Idx<T>>,
73}
74
75impl<'a, T> Iterator for NodeIter<'a, T> {
76 type Item = Idx<T>;
77
78 fn next(&mut self) -> Option<Idx<T>> {
79 self.next.map(|next| {
80 self.next = self.nodes[next].next_sibling;
81 next
82 })
83 }
84}
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index c2ece49f4..10c25666a 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -9,6 +9,7 @@ use crate::{world::WorldSnapshot, Result};
9/// 9///
10/// We use it to cook up the set of cli args we need to pass to Cargo to 10/// We use it to cook up the set of cli args we need to pass to Cargo to
11/// build/test/run the target. 11/// build/test/run the target.
12#[derive(Clone)]
12pub(crate) struct CargoTargetSpec { 13pub(crate) struct CargoTargetSpec {
13 pub(crate) package: String, 14 pub(crate) package: String,
14 pub(crate) target: String, 15 pub(crate) target: String,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 715eddadb..74a63e32a 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -69,6 +69,7 @@ pub enum RustfmtConfig {
69pub struct ClientCapsConfig { 69pub struct ClientCapsConfig {
70 pub location_link: bool, 70 pub location_link: bool,
71 pub line_folding_only: bool, 71 pub line_folding_only: bool,
72 pub hierarchical_symbols: bool,
72} 73}
73 74
74impl Default for Config { 75impl Default for Config {
@@ -215,6 +216,11 @@ impl Config {
215 if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) { 216 if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) {
216 self.client_caps.line_folding_only = value 217 self.client_caps.line_folding_only = value
217 } 218 }
219 if let Some(value) =
220 caps.document_symbol.as_ref().and_then(|it| it.hierarchical_document_symbol_support)
221 {
222 self.client_caps.hierarchical_symbols = value
223 }
218 self.completion.allow_snippets(false); 224 self.completion.allow_snippets(false);
219 if let Some(completion) = &caps.completion { 225 if let Some(completion) = &caps.completion {
220 if let Some(completion_item) = &completion.completion_item { 226 if let Some(completion_item) = &completion.completion_item {
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index b0f911f71..ffe3ea84d 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -25,7 +25,8 @@ use crate::{
25 Result, 25 Result,
26}; 26};
27use semantic_tokens::{ 27use semantic_tokens::{
28 ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, LIFETIME, TYPE_ALIAS, UNION, UNRESOLVED_REFERENCE, 28 ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, FORMAT_SPECIFIER, LIFETIME, TYPE_ALIAS, UNION,
29 UNRESOLVED_REFERENCE,
29}; 30};
30 31
31pub trait Conv { 32pub trait Conv {
@@ -381,6 +382,7 @@ impl Conv for Highlight {
381 HighlightTag::Attribute => ATTRIBUTE, 382 HighlightTag::Attribute => ATTRIBUTE,
382 HighlightTag::Keyword => SemanticTokenType::KEYWORD, 383 HighlightTag::Keyword => SemanticTokenType::KEYWORD,
383 HighlightTag::UnresolvedReference => UNRESOLVED_REFERENCE, 384 HighlightTag::UnresolvedReference => UNRESOLVED_REFERENCE,
385 HighlightTag::FormatSpecifier => FORMAT_SPECIFIER,
384 }; 386 };
385 387
386 for modifier in self.modifiers.iter() { 388 for modifier in self.modifiers.iter() {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index c2c1a23cd..6caaf5f88 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -16,7 +16,7 @@ use lsp_types::{
16 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, 16 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse,
17 Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams, 17 Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams,
18 SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, 18 SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier,
19 TextEdit, WorkspaceEdit, 19 TextEdit, Url, WorkspaceEdit,
20}; 20};
21use ra_ide::{ 21use ra_ide::{
22 Assist, AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, 22 Assist, AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
@@ -219,6 +219,7 @@ pub fn handle_document_symbol(
219 let _p = profile("handle_document_symbol"); 219 let _p = profile("handle_document_symbol");
220 let file_id = params.text_document.try_conv_with(&world)?; 220 let file_id = params.text_document.try_conv_with(&world)?;
221 let line_index = world.analysis().file_line_index(file_id)?; 221 let line_index = world.analysis().file_line_index(file_id)?;
222 let url = file_id.try_conv_with(&world)?;
222 223
223 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); 224 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
224 225
@@ -234,10 +235,10 @@ pub fn handle_document_symbol(
234 }; 235 };
235 parents.push((doc_symbol, symbol.parent)); 236 parents.push((doc_symbol, symbol.parent));
236 } 237 }
237 let mut res = Vec::new(); 238 let mut document_symbols = Vec::new();
238 while let Some((node, parent)) = parents.pop() { 239 while let Some((node, parent)) = parents.pop() {
239 match parent { 240 match parent {
240 None => res.push(node), 241 None => document_symbols.push(node),
241 Some(i) => { 242 Some(i) => {
242 let children = &mut parents[i].0.children; 243 let children = &mut parents[i].0.children;
243 if children.is_none() { 244 if children.is_none() {
@@ -248,7 +249,35 @@ pub fn handle_document_symbol(
248 } 249 }
249 } 250 }
250 251
251 Ok(Some(res.into())) 252 if world.config.client_caps.hierarchical_symbols {
253 Ok(Some(document_symbols.into()))
254 } else {
255 let mut symbol_information = Vec::<SymbolInformation>::new();
256 for symbol in document_symbols {
257 flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
258 }
259
260 Ok(Some(symbol_information.into()))
261 }
262}
263
264fn flatten_document_symbol(
265 symbol: &DocumentSymbol,
266 container_name: Option<String>,
267 url: &Url,
268 res: &mut Vec<SymbolInformation>,
269) {
270 res.push(SymbolInformation {
271 name: symbol.name.clone(),
272 kind: symbol.kind,
273 deprecated: symbol.deprecated,
274 location: Location::new(url.clone(), symbol.range),
275 container_name: container_name,
276 });
277
278 for child in symbol.children.iter().flatten() {
279 flatten_document_symbol(child, Some(symbol.name.clone()), url, res);
280 }
252} 281}
253 282
254pub fn handle_workspace_symbol( 283pub fn handle_workspace_symbol(
@@ -364,28 +393,37 @@ pub fn handle_runnables(
364 } 393 }
365 res.push(to_lsp_runnable(&world, file_id, runnable)?); 394 res.push(to_lsp_runnable(&world, file_id, runnable)?);
366 } 395 }
367 let mut check_args = vec!["check".to_string()]; 396 // Add `cargo check` and `cargo test` for the whole package
368 let label;
369 match CargoTargetSpec::for_file(&world, file_id)? { 397 match CargoTargetSpec::for_file(&world, file_id)? {
370 Some(spec) => { 398 Some(spec) => {
371 label = format!("cargo check -p {}", spec.package); 399 for &cmd in ["check", "test"].iter() {
372 spec.push_to(&mut check_args); 400 res.push(req::Runnable {
401 range: Default::default(),
402 label: format!("cargo {} -p {}", cmd, spec.package),
403 bin: "cargo".to_string(),
404 args: {
405 let mut args = vec![cmd.to_string()];
406 spec.clone().push_to(&mut args);
407 args
408 },
409 extra_args: Vec::new(),
410 env: FxHashMap::default(),
411 cwd: workspace_root.map(|root| root.to_owned()),
412 })
413 }
373 } 414 }
374 None => { 415 None => {
375 label = "cargo check --all".to_string(); 416 res.push(req::Runnable {
376 check_args.push("--all".to_string()) 417 range: Default::default(),
418 label: "cargo check --workspace".to_string(),
419 bin: "cargo".to_string(),
420 args: vec!["check".to_string(), "--workspace".to_string()],
421 extra_args: Vec::new(),
422 env: FxHashMap::default(),
423 cwd: workspace_root.map(|root| root.to_owned()),
424 });
377 } 425 }
378 } 426 }
379 // Always add `cargo check`.
380 res.push(req::Runnable {
381 range: Default::default(),
382 label,
383 bin: "cargo".to_string(),
384 args: check_args,
385 extra_args: Vec::new(),
386 env: FxHashMap::default(),
387 cwd: workspace_root.map(|root| root.to_owned()),
388 });
389 Ok(res) 427 Ok(res)
390} 428}
391 429
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 10fe696f6..71f4f58a3 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -12,6 +12,7 @@ pub(crate) const TYPE_ALIAS: SemanticTokenType = SemanticTokenType::new("typeAli
12pub(crate) const UNION: SemanticTokenType = SemanticTokenType::new("union"); 12pub(crate) const UNION: SemanticTokenType = SemanticTokenType::new("union");
13pub(crate) const UNRESOLVED_REFERENCE: SemanticTokenType = 13pub(crate) const UNRESOLVED_REFERENCE: SemanticTokenType =
14 SemanticTokenType::new("unresolvedReference"); 14 SemanticTokenType::new("unresolvedReference");
15pub(crate) const FORMAT_SPECIFIER: SemanticTokenType = SemanticTokenType::new("formatSpecifier");
15 16
16pub(crate) const CONSTANT: SemanticTokenModifier = SemanticTokenModifier::new("constant"); 17pub(crate) const CONSTANT: SemanticTokenModifier = SemanticTokenModifier::new("constant");
17pub(crate) const CONTROL_FLOW: SemanticTokenModifier = SemanticTokenModifier::new("controlFlow"); 18pub(crate) const CONTROL_FLOW: SemanticTokenModifier = SemanticTokenModifier::new("controlFlow");
@@ -46,6 +47,7 @@ pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
46 TYPE_ALIAS, 47 TYPE_ALIAS,
47 UNION, 48 UNION,
48 UNRESOLVED_REFERENCE, 49 UNRESOLVED_REFERENCE,
50 FORMAT_SPECIFIER,
49]; 51];
50 52
51pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[ 53pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index b31533e5e..f6245ddd4 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -87,24 +87,15 @@ fn foo() {
87 } 87 }
88 }, 88 },
89 { 89 {
90 "args": [ 90 "args": ["check", "--workspace"],
91 "check",
92 "--all"
93 ],
94 "extraArgs": [], 91 "extraArgs": [],
95 "bin": "cargo", 92 "bin": "cargo",
96 "env": {}, 93 "env": {},
97 "cwd": null, 94 "cwd": null,
98 "label": "cargo check --all", 95 "label": "cargo check --workspace",
99 "range": { 96 "range": {
100 "end": { 97 "end": { "character": 0, "line": 0 },
101 "character": 0, 98 "start": { "character": 0, "line": 0 }
102 "line": 0
103 },
104 "start": {
105 "character": 0,
106 "line": 0
107 }
108 } 99 }
109 } 100 }
110 ]), 101 ]),
@@ -145,42 +136,42 @@ fn main() {}
145 server.request::<Runnables>( 136 server.request::<Runnables>(
146 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, 137 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
147 json!([ 138 json!([
148 { 139 {
149 "args": [ "test", "--package", "foo", "--test", "spam" ], 140 "args": [ "test", "--package", "foo", "--test", "spam" ],
150 "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], 141 "extraArgs": [ "test_eggs", "--exact", "--nocapture" ],
151 "bin": "cargo", 142 "bin": "cargo",
152 "env": { "RUST_BACKTRACE": "short" }, 143 "env": { "RUST_BACKTRACE": "short" },
153 "label": "test test_eggs", 144 "label": "test test_eggs",
154 "range": { 145 "range": {
155 "end": { "character": 17, "line": 1 }, 146 "end": { "character": 17, "line": 1 },
156 "start": { "character": 0, "line": 0 } 147 "start": { "character": 0, "line": 0 }
148 },
149 "cwd": server.path().join("foo")
157 }, 150 },
158 "cwd": server.path().join("foo") 151 {
159 }, 152 "args": [ "check", "--package", "foo", "--test", "spam" ],
160 { 153 "extraArgs": [],
161 "args": [ 154 "bin": "cargo",
162 "check", 155 "env": {},
163 "--package", 156 "label": "cargo check -p foo",
164 "foo", 157 "range": {
165 "--test", 158 "end": { "character": 0, "line": 0 },
166 "spam" 159 "start": { "character": 0, "line": 0 }
167 ],
168 "extraArgs": [],
169 "bin": "cargo",
170 "env": {},
171 "cwd": server.path().join("foo"),
172 "label": "cargo check -p foo",
173 "range": {
174 "end": {
175 "character": 0,
176 "line": 0
177 }, 160 },
178 "start": { 161 "cwd": server.path().join("foo")
179 "character": 0, 162 },
180 "line": 0 163 {
181 } 164 "args": [ "test", "--package", "foo", "--test", "spam" ],
165 "extraArgs": [],
166 "bin": "cargo",
167 "env": {},
168 "label": "cargo test -p foo",
169 "range": {
170 "end": { "character": 0, "line": 0 },
171 "start": { "character": 0, "line": 0 }
172 },
173 "cwd": server.path().join("foo")
182 } 174 }
183 }
184 ]), 175 ]),
185 ); 176 );
186} 177}
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index 7eebedff7..e4fe3411a 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -62,11 +62,7 @@ impl<'a> Project<'a> {
62 static INIT: Once = Once::new(); 62 static INIT: Once = Once::new();
63 INIT.call_once(|| { 63 INIT.call_once(|| {
64 env_logger::builder().is_test(true).try_init().unwrap(); 64 env_logger::builder().is_test(true).try_init().unwrap();
65 ra_prof::set_filter(if crate::PROFILE.is_empty() { 65 ra_prof::init_from(crate::PROFILE);
66 ra_prof::Filter::disabled()
67 } else {
68 ra_prof::Filter::from_spec(&crate::PROFILE)
69 });
70 }); 66 });
71 67
72 let mut paths = vec![]; 68 let mut paths = vec![];