aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/semantics.rs6
-rw-r--r--crates/hir/src/source_analyzer.rs30
-rw-r--r--crates/hir_ty/src/lower.rs2
-rw-r--r--crates/ide/src/hover.rs63
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs2
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt138
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs101
-rw-r--r--crates/rust-analyzer/src/handlers.rs3
-rw-r--r--docs/dev/style.md4
9 files changed, 292 insertions, 57 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index d3caeef4e..3bf722d2a 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -76,9 +76,11 @@ impl PathResolution {
76 pub fn assoc_type_shorthand_candidates<R>( 76 pub fn assoc_type_shorthand_candidates<R>(
77 &self, 77 &self,
78 db: &dyn HirDatabase, 78 db: &dyn HirDatabase,
79 mut cb: impl FnMut(TypeAlias) -> Option<R>, 79 mut cb: impl FnMut(&Name, TypeAlias) -> Option<R>,
80 ) -> Option<R> { 80 ) -> Option<R> {
81 associated_type_shorthand_candidates(db, self.in_type_ns()?, |_, _, id| cb(id.into())) 81 associated_type_shorthand_candidates(db, self.in_type_ns()?, |name, _, id| {
82 cb(name, id.into())
83 })
82 } 84 }
83} 85}
84 86
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 37d162b32..8e9ea0a03 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -20,7 +20,7 @@ use hir_def::{
20use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; 20use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
21use hir_ty::{ 21use hir_ty::{
22 diagnostics::{record_literal_missing_fields, record_pattern_missing_fields}, 22 diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
23 InferenceResult, Substitution, 23 InferenceResult, Substitution, TyLoweringContext,
24}; 24};
25use syntax::{ 25use syntax::{
26 ast::{self, AstNode}, 26 ast::{self, AstNode},
@@ -466,7 +466,21 @@ fn resolve_hir_path_(
466 prefer_value_ns: bool, 466 prefer_value_ns: bool,
467) -> Option<PathResolution> { 467) -> Option<PathResolution> {
468 let types = || { 468 let types = || {
469 resolver.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()).map(|ty| match ty { 469 let (ty, unresolved) = match path.type_anchor() {
470 Some(type_ref) => {
471 let (_, res) = TyLoweringContext::new(db, resolver).lower_ty_ext(type_ref);
472 res.map(|ty_ns| (ty_ns, path.segments().first()))
473 }
474 None => {
475 let (ty, remaining) =
476 resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?;
477 match remaining {
478 Some(remaining) if remaining > 1 => None,
479 _ => Some((ty, path.segments().get(1))),
480 }
481 }
482 }?;
483 let res = match ty {
470 TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), 484 TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
471 TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), 485 TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }),
472 TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { 486 TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => {
@@ -476,7 +490,17 @@ fn resolve_hir_path_(
476 TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), 490 TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()),
477 TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()), 491 TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()),
478 TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), 492 TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
479 }) 493 };
494 match unresolved {
495 Some(unresolved) => res
496 .assoc_type_shorthand_candidates(db, |name, alias| {
497 (name == unresolved.name).then(|| alias)
498 })
499 .map(TypeAlias::from)
500 .map(Into::into)
501 .map(PathResolution::Def),
502 None => Some(res),
503 }
480 }; 504 };
481 505
482 let body_owner = resolver.body_owner(); 506 let body_owner = resolver.body_owner();
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index afbfa12d5..14f34d73c 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -146,7 +146,7 @@ impl<'a> TyLoweringContext<'a> {
146 self.lower_ty_ext(type_ref).0 146 self.lower_ty_ext(type_ref).0
147 } 147 }
148 148
149 fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) { 149 pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
150 let mut res = None; 150 let mut res = None;
151 let ty = match type_ref { 151 let ty = match type_ref {
152 TypeRef::Never => TyKind::Never.intern(&Interner), 152 TypeRef::Never => TyKind::Never.intern(&Interner),
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 5f9edb476..28e2e17dc 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -3834,4 +3834,67 @@ fn foo() {}
3834 "#]], 3834 "#]],
3835 ); 3835 );
3836 } 3836 }
3837
3838 #[test]
3839 fn hover_generic_assoc() {
3840 check(
3841 r#"
3842fn foo<T: A>() where T::Assoc$0: {}
3843
3844trait A {
3845 type Assoc;
3846}"#,
3847 expect![[r#"
3848 *Assoc*
3849
3850 ```rust
3851 test
3852 ```
3853
3854 ```rust
3855 type Assoc
3856 ```
3857 "#]],
3858 );
3859 check(
3860 r#"
3861fn foo<T: A>() {
3862 let _: <T>::Assoc$0;
3863}
3864
3865trait A {
3866 type Assoc;
3867}"#,
3868 expect![[r#"
3869 *Assoc*
3870
3871 ```rust
3872 test
3873 ```
3874
3875 ```rust
3876 type Assoc
3877 ```
3878 "#]],
3879 );
3880 check(
3881 r#"
3882trait A where
3883 Self::Assoc$0: ,
3884{
3885 type Assoc;
3886}"#,
3887 expect![[r#"
3888 *Assoc*
3889
3890 ```rust
3891 test
3892 ```
3893
3894 ```rust
3895 type Assoc
3896 ```
3897 "#]],
3898 );
3899 }
3837} 3900}
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 1891eb5b3..969249df6 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -24,7 +24,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
24 }; 24 };
25 25
26 // Add associated types on type parameters and `Self`. 26 // Add associated types on type parameters and `Self`.
27 resolution.assoc_type_shorthand_candidates(ctx.db, |alias| { 27 resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
28 acc.add_type_alias(ctx, alias); 28 acc.add_type_alias(ctx, alias);
29 None::<()> 29 None::<()>
30 }); 30 });
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
index f999848a7..c847bbb35 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
@@ -13,16 +13,16 @@
13 diagnostic: Diagnostic { 13 diagnostic: Diagnostic {
14 range: Range { 14 range: Range {
15 start: Position { 15 start: Position {
16 line: 264, 16 line: 271,
17 character: 8, 17 character: 8,
18 }, 18 },
19 end: Position { 19 end: Position {
20 line: 264, 20 line: 271,
21 character: 76, 21 character: 50,
22 }, 22 },
23 }, 23 },
24 severity: Some( 24 severity: Some(
25 Error, 25 Hint,
26 ), 26 ),
27 code: None, 27 code: None,
28 code_description: None, 28 code_description: None,
@@ -40,18 +40,18 @@
40 password: None, 40 password: None,
41 host: None, 41 host: None,
42 port: None, 42 port: None,
43 path: "/test/crates/hir_def/src/data.rs", 43 path: "/test/crates/hir_def/src/path.rs",
44 query: None, 44 query: None,
45 fragment: None, 45 fragment: None,
46 }, 46 },
47 range: Range { 47 range: Range {
48 start: Position { 48 start: Position {
49 line: 79, 49 line: 264,
50 character: 15, 50 character: 8,
51 }, 51 },
52 end: Position { 52 end: Position {
53 line: 79, 53 line: 264,
54 character: 41, 54 character: 76,
55 }, 55 },
56 }, 56 },
57 }, 57 },
@@ -87,6 +87,71 @@
87 }, 87 },
88 }, 88 },
89 severity: Some( 89 severity: Some(
90 Hint,
91 ),
92 code: None,
93 code_description: None,
94 source: Some(
95 "rustc",
96 ),
97 message: "Please register your known path in the path module",
98 related_information: Some(
99 [
100 DiagnosticRelatedInformation {
101 location: Location {
102 uri: Url {
103 scheme: "file",
104 username: "",
105 password: None,
106 host: None,
107 port: None,
108 path: "/test/crates/hir_def/src/path.rs",
109 query: None,
110 fragment: None,
111 },
112 range: Range {
113 start: Position {
114 line: 264,
115 character: 8,
116 },
117 end: Position {
118 line: 264,
119 character: 76,
120 },
121 },
122 },
123 message: "Exact error occurred here",
124 },
125 ],
126 ),
127 tags: None,
128 data: None,
129 },
130 fixes: [],
131 },
132 MappedRustDiagnostic {
133 url: Url {
134 scheme: "file",
135 username: "",
136 password: None,
137 host: None,
138 port: None,
139 path: "/test/crates/hir_def/src/path.rs",
140 query: None,
141 fragment: None,
142 },
143 diagnostic: Diagnostic {
144 range: Range {
145 start: Position {
146 line: 264,
147 character: 8,
148 },
149 end: Position {
150 line: 264,
151 character: 76,
152 },
153 },
154 severity: Some(
90 Error, 155 Error,
91 ), 156 ),
92 code: None, 157 code: None,
@@ -95,7 +160,60 @@
95 "rustc", 160 "rustc",
96 ), 161 ),
97 message: "Please register your known path in the path module", 162 message: "Please register your known path in the path module",
98 related_information: None, 163 related_information: Some(
164 [
165 DiagnosticRelatedInformation {
166 location: Location {
167 uri: Url {
168 scheme: "file",
169 username: "",
170 password: None,
171 host: None,
172 port: None,
173 path: "/test/crates/hir_def/src/path.rs",
174 query: None,
175 fragment: None,
176 },
177 range: Range {
178 start: Position {
179 line: 271,
180 character: 8,
181 },
182 end: Position {
183 line: 271,
184 character: 50,
185 },
186 },
187 },
188 message: "Error originated from macro call here",
189 },
190 DiagnosticRelatedInformation {
191 location: Location {
192 uri: Url {
193 scheme: "file",
194 username: "",
195 password: None,
196 host: None,
197 port: None,
198 path: "/test/crates/hir_def/src/data.rs",
199 query: None,
200 fragment: None,
201 },
202 range: Range {
203 start: Position {
204 line: 79,
205 character: 15,
206 },
207 end: Position {
208 line: 79,
209 character: 41,
210 },
211 },
212 },
213 message: "Error originated from macro call here",
214 },
215 ],
216 ),
99 tags: None, 217 tags: None,
100 data: None, 218 data: None,
101 }, 219 },
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 76994de71..e2f319f6b 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -34,22 +34,14 @@ fn diagnostic_severity(
34 Some(res) 34 Some(res)
35} 35}
36 36
37/// Check whether a file name is from macro invocation 37/// Checks whether a file name is from macro invocation and does not refer to an actual file.
38fn is_from_macro(file_name: &str) -> bool { 38fn is_dummy_macro_file(file_name: &str) -> bool {
39 // FIXME: current rustc does not seem to emit `<macro file>` files anymore?
39 file_name.starts_with('<') && file_name.ends_with('>') 40 file_name.starts_with('<') && file_name.ends_with('>')
40} 41}
41 42
42/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
43fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
44 let mut span = span.clone();
45 while let Some(expansion) = span.expansion {
46 span = expansion.span;
47 }
48 return location_naive(workspace_root, &span);
49}
50
51/// Converts a Rust span to a LSP location 43/// Converts a Rust span to a LSP location
52fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { 44fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
53 let file_name = workspace_root.join(&span.file_name); 45 let file_name = workspace_root.join(&span.file_name);
54 let uri = url_from_abs_path(&file_name); 46 let uri = url_from_abs_path(&file_name);
55 47
@@ -62,7 +54,25 @@ fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Lo
62 lsp_types::Location { uri, range } 54 lsp_types::Location { uri, range }
63} 55}
64 56
65/// Converts a secondary Rust span to a LSP related inflocation(ormation 57/// Extracts a suitable "primary" location from a rustc diagnostic.
58///
59/// This takes locations pointing into the standard library, or generally outside the current
60/// workspace into account and tries to avoid those, in case macros are involved.
61fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
62 let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span));
63 for span in span_stack.clone() {
64 let abs_path = workspace_root.join(&span.file_name);
65 if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) {
66 return location(workspace_root, span);
67 }
68 }
69
70 // Fall back to the outermost macro invocation if no suitable span comes up.
71 let last_span = span_stack.last().unwrap();
72 location(workspace_root, last_span)
73}
74
75/// Converts a secondary Rust span to a LSP related information
66/// 76///
67/// If the span is unlabelled this will return `None`. 77/// If the span is unlabelled this will return `None`.
68fn diagnostic_related_information( 78fn diagnostic_related_information(
@@ -231,7 +241,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
231 primary_spans 241 primary_spans
232 .iter() 242 .iter()
233 .flat_map(|primary_span| { 243 .flat_map(|primary_span| {
234 let location = location(workspace_root, &primary_span); 244 let primary_location = primary_location(workspace_root, &primary_span);
235 245
236 let mut message = message.clone(); 246 let mut message = message.clone();
237 if needs_primary_span_label { 247 if needs_primary_span_label {
@@ -243,31 +253,47 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
243 // Each primary diagnostic span may result in multiple LSP diagnostics. 253 // Each primary diagnostic span may result in multiple LSP diagnostics.
244 let mut diagnostics = Vec::new(); 254 let mut diagnostics = Vec::new();
245 255
246 let mut related_macro_info = None; 256 let mut related_info_macro_calls = vec![];
247 257
248 // If error occurs from macro expansion, add related info pointing to 258 // If error occurs from macro expansion, add related info pointing to
249 // where the error originated 259 // where the error originated
250 // Also, we would generate an additional diagnostic, so that exact place of macro 260 // Also, we would generate an additional diagnostic, so that exact place of macro
251 // will be highlighted in the error origin place. 261 // will be highlighted in the error origin place.
252 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { 262 let span_stack = std::iter::successors(Some(*primary_span), |span| {
253 let in_macro_location = location_naive(workspace_root, &primary_span); 263 Some(&span.expansion.as_ref()?.span)
264 });
265 for (i, span) in span_stack.enumerate() {
266 if is_dummy_macro_file(&span.file_name) {
267 continue;
268 }
254 269
255 // Add related information for the main disagnostic. 270 // First span is the original diagnostic, others are macro call locations that
256 related_macro_info = Some(lsp_types::DiagnosticRelatedInformation { 271 // generated that code.
257 location: in_macro_location.clone(), 272 let is_in_macro_call = i != 0;
258 message: "Error originated from macro here".to_string(),
259 });
260 273
274 let secondary_location = location(workspace_root, &span);
275 if secondary_location == primary_location {
276 continue;
277 }
278 related_info_macro_calls.push(lsp_types::DiagnosticRelatedInformation {
279 location: secondary_location.clone(),
280 message: if is_in_macro_call {
281 "Error originated from macro call here".to_string()
282 } else {
283 "Actual error occurred here".to_string()
284 },
285 });
261 // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. 286 // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code.
262 let information_for_additional_diagnostic = 287 let information_for_additional_diagnostic =
263 vec![lsp_types::DiagnosticRelatedInformation { 288 vec![lsp_types::DiagnosticRelatedInformation {
264 location: location.clone(), 289 location: primary_location.clone(),
265 message: "Exact error occurred here".to_string(), 290 message: "Exact error occurred here".to_string(),
266 }]; 291 }];
267 292
268 let diagnostic = lsp_types::Diagnostic { 293 let diagnostic = lsp_types::Diagnostic {
269 range: in_macro_location.range, 294 range: secondary_location.range,
270 severity, 295 // downgrade to hint if we're pointing at the macro
296 severity: Some(lsp_types::DiagnosticSeverity::Hint),
271 code: code.clone().map(lsp_types::NumberOrString::String), 297 code: code.clone().map(lsp_types::NumberOrString::String),
272 code_description: code_description.clone(), 298 code_description: code_description.clone(),
273 source: Some(source.clone()), 299 source: Some(source.clone()),
@@ -276,9 +302,8 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
276 tags: if tags.is_empty() { None } else { Some(tags.clone()) }, 302 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
277 data: None, 303 data: None,
278 }; 304 };
279
280 diagnostics.push(MappedRustDiagnostic { 305 diagnostics.push(MappedRustDiagnostic {
281 url: in_macro_location.uri, 306 url: secondary_location.uri,
282 diagnostic, 307 diagnostic,
283 fixes: Vec::new(), 308 fixes: Vec::new(),
284 }); 309 });
@@ -286,23 +311,25 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
286 311
287 // Emit the primary diagnostic. 312 // Emit the primary diagnostic.
288 diagnostics.push(MappedRustDiagnostic { 313 diagnostics.push(MappedRustDiagnostic {
289 url: location.uri.clone(), 314 url: primary_location.uri.clone(),
290 diagnostic: lsp_types::Diagnostic { 315 diagnostic: lsp_types::Diagnostic {
291 range: location.range, 316 range: primary_location.range,
292 severity, 317 severity,
293 code: code.clone().map(lsp_types::NumberOrString::String), 318 code: code.clone().map(lsp_types::NumberOrString::String),
294 code_description: code_description.clone(), 319 code_description: code_description.clone(),
295 source: Some(source.clone()), 320 source: Some(source.clone()),
296 message, 321 message,
297 related_information: if subdiagnostics.is_empty() { 322 related_information: {
298 None 323 let info = related_info_macro_calls
299 } else {
300 let mut related = subdiagnostics
301 .iter() 324 .iter()
302 .map(|sub| sub.related.clone()) 325 .cloned()
326 .chain(subdiagnostics.iter().map(|sub| sub.related.clone()))
303 .collect::<Vec<_>>(); 327 .collect::<Vec<_>>();
304 related.extend(related_macro_info); 328 if info.is_empty() {
305 Some(related) 329 None
330 } else {
331 Some(info)
332 }
306 }, 333 },
307 tags: if tags.is_empty() { None } else { Some(tags.clone()) }, 334 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
308 data: None, 335 data: None,
@@ -314,7 +341,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
314 // This is useful because they will show up in the user's editor, unlike 341 // This is useful because they will show up in the user's editor, unlike
315 // `related_information`, which just produces hard-to-read links, at least in VS Code. 342 // `related_information`, which just produces hard-to-read links, at least in VS Code.
316 let back_ref = lsp_types::DiagnosticRelatedInformation { 343 let back_ref = lsp_types::DiagnosticRelatedInformation {
317 location, 344 location: primary_location,
318 message: "original diagnostic".to_string(), 345 message: "original diagnostic".to_string(),
319 }; 346 };
320 for sub in &subdiagnostics { 347 for sub in &subdiagnostics {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 85e67554c..53d29ddfc 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -84,7 +84,8 @@ pub(crate) fn handle_analyzer_status(
84 84
85pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> { 85pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> {
86 let _p = profile::span("handle_memory_usage"); 86 let _p = profile::span("handle_memory_usage");
87 let mem = state.analysis_host.per_query_memory_usage(); 87 let mut mem = state.analysis_host.per_query_memory_usage();
88 mem.push(("Remaining".into(), profile::memory_usage().allocated));
88 89
89 let mut out = String::new(); 90 let mut out = String::new();
90 for (name, bytes) in mem { 91 for (name, bytes) in mem {
diff --git a/docs/dev/style.md b/docs/dev/style.md
index e4a1672ca..1b1c24b1e 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -55,9 +55,9 @@ https://www.tedinski.com/2018/02/06/system-boundaries.html
55We try to be very conservative with usage of crates.io dependencies. 55We try to be very conservative with usage of crates.io dependencies.
56Don't use small "helper" crates (exception: `itertools` is allowed). 56Don't use small "helper" crates (exception: `itertools` is allowed).
57If there's some general reusable bit of code you need, consider adding it to the `stdx` crate. 57If there's some general reusable bit of code you need, consider adding it to the `stdx` crate.
58A useful exercise is to read Cargo.lock and see if some of the *transitive* dependencies do not make sense for rust-analyzer.
58 59
59**Rationale:** keep compile times low, create ecosystem pressure for faster 60**Rationale:** keep compile times low, create ecosystem pressure for faster compiles, reduce the number of things which might break.
60compiles, reduce the number of things which might break.
61 61
62## Commit Style 62## Commit Style
63 63