aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-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
-rw-r--r--docs/dev/architecture.md2
-rw-r--r--docs/user/readme.adoc27
-rw-r--r--editors/code/src/client.ts31
-rw-r--r--editors/code/src/ctx.ts2
38 files changed, 892 insertions, 625 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 20e6cd540..367ff3f82 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1139,6 +1139,7 @@ dependencies = [
1139 "jemalloc-ctl", 1139 "jemalloc-ctl",
1140 "jemallocator", 1140 "jemallocator",
1141 "once_cell", 1141 "once_cell",
1142 "ra_arena",
1142] 1143]
1143 1144
1144[[package]] 1145[[package]]
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![];
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md
index 0343b6c81..3a337c574 100644
--- a/docs/dev/architecture.md
+++ b/docs/dev/architecture.md
@@ -56,7 +56,7 @@ In particular, `cargo xtask codegen` generates:
562. [`ast/generated`](https://github.com/rust-analyzer/rust-analyzer/blob/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_syntax/src/ast/generated.rs) 562. [`ast/generated`](https://github.com/rust-analyzer/rust-analyzer/blob/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_syntax/src/ast/generated.rs)
57 -- AST data structure. 57 -- AST data structure.
58 58
59.3 [`doc_tests/generated`](https://github.com/rust-analyzer/rust-analyzer/blob/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_assists/src/doc_tests/generated.rs), 593. [`doc_tests/generated`](https://github.com/rust-analyzer/rust-analyzer/blob/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_assists/src/doc_tests/generated.rs),
60 [`test_data/parser/inline`](https://github.com/rust-analyzer/rust-analyzer/tree/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_syntax/test_data/parser/inline) 60 [`test_data/parser/inline`](https://github.com/rust-analyzer/rust-analyzer/tree/a0be39296d2925972cacd9fbf8b5fb258fad6947/crates/ra_syntax/test_data/parser/inline)
61 -- tests for assists and the parser. 61 -- tests for assists and the parser.
62 62
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index ce5704836..76d065d35 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -23,7 +23,7 @@ https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/readme.adoc
23 23
24== Installation 24== Installation
25 25
26In theory, one should be able to just install the server binary and have it automatically work with any editor. 26In theory, one should be able to just install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> and have it automatically work with any editor.
27We are not there yet, so some editor specific setup is required. 27We are not there yet, so some editor specific setup is required.
28 28
29Additionally, rust-analyzer needs the sources of the standard library. 29Additionally, rust-analyzer needs the sources of the standard library.
@@ -108,15 +108,26 @@ Here are some useful self-diagnostic commands:
108* To log all LSP requests, add `"rust-analyzer.trace.server": "verbose"` to the settings and look for `Server Trace` in the panel. 108* To log all LSP requests, add `"rust-analyzer.trace.server": "verbose"` to the settings and look for `Server Trace` in the panel.
109* To enable client-side logging, add `"rust-analyzer.trace.extension": true` to the settings and open the `Console` tab of VS Code developer tools. 109* To enable client-side logging, add `"rust-analyzer.trace.extension": true` to the settings and open the `Console` tab of VS Code developer tools.
110 110
111=== Language Server Binary 111=== rust-analyzer Language Server Binary
112 112
113Other editors generally require the `rust-analyzer` binary to be in `$PATH`. 113Other editors generally require the `rust-analyzer` binary to be in `$PATH`.
114You can download the pre-built binary from the https://github.com/rust-analyzer/rust-analyzer/releases[releases] page. Typically, you then need to rename the binary for your platform, e.g. `rust-analyzer-mac` if you're on Mac OS, to `rust-analzyer` and make it executable in addition to moving it into a directory in your `$PATH`. 114You can download the pre-built binary from the https://github.com/rust-analyzer/rust-analyzer/releases[releases] page. Typically, you then need to rename the binary for your platform, e.g. `rust-analyzer-mac` if you're on Mac OS, to `rust-analzyer` and make it executable in addition to moving it into a directory in your `$PATH`.
115 115
116On Linux to install the `rust-analyzer` binary into `~/.local/bin`, this commands could be used
117
118[source,bash]
119----
120$ curl -L https://github.com/rust-analyzer/rust-analyzer/releases/latest/download/rust-analyzer-linux -o ~/.local/bin/rust-analyzer
121$ chmod +x ~/.local/bin/rust-analyzer
122----
123
124Ensure `~/.local/bin` is listed in the `$PATH` variable.
125
116Alternatively, you can install it from source using the following command: 126Alternatively, you can install it from source using the following command:
117 127
118[source,bash] 128[source,bash]
119---- 129----
130$ git clone https://github.com/rust-analyzer/rust-analyzer.git && cd rust-analyzer
120$ cargo xtask install --server 131$ cargo xtask install --server
121---- 132----
122 133
@@ -139,15 +150,19 @@ $ yay -S rust-analyzer-bin
139 150
140=== Emacs 151=== Emacs
141 152
142Emacs support is maintained https://github.com/emacs-lsp/lsp-mode/blob/master/lsp-rust.el[upstream]. 153Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
143 154
1441. Install the most recent version of `emacs-lsp` package by following the instructions https://github.com/emacs-lsp/lsp-mode[here]. 155Emacs support is maintained as part of the https://github.com/emacs-lsp/lsp-mode[Emacs-LSP] package in https://github.com/emacs-lsp/lsp-mode/blob/master/lsp-rust.el[lsp-rust.el].
156
1571. Install the most recent version of `emacs-lsp` package by following the https://github.com/emacs-lsp/lsp-mode[Emacs-LSP instructions].
1452. Set `lsp-rust-server` to `'rust-analyzer`. 1582. Set `lsp-rust-server` to `'rust-analyzer`.
1463. Run `lsp` in a Rust buffer. 1593. Run `lsp` in a Rust buffer.
1474. (Optionally) bind commands like `lsp-rust-analyzer-join-lines`, `lsp-extend-selection` and `lsp-rust-analyzer-expand-macro` to keys. 1604. (Optionally) bind commands like `lsp-rust-analyzer-join-lines`, `lsp-extend-selection` and `lsp-rust-analyzer-expand-macro` to keys.
148 161
149=== Vim 162=== Vim
150 163
164Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
165
151The are several LSP client implementations for vim: 166The are several LSP client implementations for vim:
152 167
153==== coc-rust-analyzer 168==== coc-rust-analyzer
@@ -205,7 +220,7 @@ Once `neovim/nvim-lsp` is installed, use `+lua require'nvim_lsp'.rust_analyzer.s
205 220
206=== Sublime Text 3 221=== Sublime Text 3
207 222
208Prerequisites: You have installed the <<language-server-binary,`rust-analyzer` binary>>. 223Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
209 224
210You also need the `LSP` package. To install it: 225You also need the `LSP` package. To install it:
211 226
@@ -218,7 +233,7 @@ Finally, with your Rust project open, in the command palette, run `LSP: Enable L
218 233
219If it worked, you should see "rust-analzyer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available. 234If it worked, you should see "rust-analzyer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available.
220 235
221If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<language-server-binary,section on installing the language server binary>>. 236If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary.
222 237
223== Usage 238== Usage
224 239
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 0ad4b63ae..cffdcf11a 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -4,7 +4,7 @@ import * as vscode from 'vscode';
4import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; 4import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed';
5import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; 5import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed';
6 6
7export async function createClient(serverPath: string, cwd: string): Promise<lc.LanguageClient> { 7export function createClient(serverPath: string, cwd: string): lc.LanguageClient {
8 // '.' Is the fallback if no folder is open 8 // '.' Is the fallback if no folder is open
9 // TODO?: Workspace folders support Uri's (eg: file://test.txt). 9 // TODO?: Workspace folders support Uri's (eg: file://test.txt).
10 // It might be a good idea to test if the uri points to a file. 10 // It might be a good idea to test if the uri points to a file.
@@ -42,35 +42,6 @@ export async function createClient(serverPath: string, cwd: string): Promise<lc.
42 clientOptions, 42 clientOptions,
43 ); 43 );
44 44
45 // HACK: This is an awful way of filtering out the decorations notifications
46 // However, pending proper support, this is the most effecitve approach
47 // Proper support for this would entail a change to vscode-languageclient to allow not notifying on certain messages
48 // Or the ability to disable the serverside component of highlighting (but this means that to do tracing we need to disable hihlighting)
49 // This also requires considering our settings strategy, which is work which needs doing
50 // @ts-ignore The tracer is private to vscode-languageclient, but we need access to it to not log publishDecorations requests
51 res._tracer = {
52 log: (messageOrDataObject: string | unknown, data?: string) => {
53 if (typeof messageOrDataObject === 'string') {
54 if (
55 messageOrDataObject.includes(
56 'rust-analyzer/publishDecorations',
57 ) ||
58 messageOrDataObject.includes(
59 'rust-analyzer/decorationsRequest',
60 )
61 ) {
62 // Don't log publish decorations requests
63 } else {
64 // @ts-ignore This is just a utility function
65 res.logTrace(messageOrDataObject, data);
66 }
67 } else {
68 // @ts-ignore
69 res.logObjectTrace(messageOrDataObject);
70 }
71 },
72 };
73
74 // To turn on all proposed features use: res.registerProposedFeatures(); 45 // To turn on all proposed features use: res.registerProposedFeatures();
75 // Here we want to enable CallHierarchyFeature and SemanticTokensFeature 46 // Here we want to enable CallHierarchyFeature and SemanticTokensFeature
76 // since they are available on stable. 47 // since they are available on stable.
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index f7ed62d03..41df11991 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -21,7 +21,7 @@ export class Ctx {
21 serverPath: string, 21 serverPath: string,
22 cwd: string, 22 cwd: string,
23 ): Promise<Ctx> { 23 ): Promise<Ctx> {
24 const client = await createClient(serverPath, cwd); 24 const client = createClient(serverPath, cwd);
25 const res = new Ctx(config, extCtx, client, serverPath); 25 const res = new Ctx(config, extCtx, client, serverPath);
26 res.pushCleanup(client.start()); 26 res.pushCleanup(client.start());
27 await client.onReady(); 27 await client.onReady();