aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PRIVACY.md17
-rw-r--r--crates/assists/src/assist_config.rs5
-rw-r--r--crates/assists/src/handlers/auto_import.rs16
-rw-r--r--crates/assists/src/handlers/convert_integer_literal.rs701
-rw-r--r--crates/assists/src/handlers/remove_dbg.rs9
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests.rs27
-rw-r--r--crates/assists/src/tests/generated.rs13
-rw-r--r--crates/base_db/src/fixture.rs8
-rw-r--r--crates/base_db/src/input.rs13
-rw-r--r--crates/hir/src/code_model.rs10
-rw-r--r--crates/hir/src/lib.rs1
-rw-r--r--crates/hir_def/src/find_path.rs247
-rw-r--r--crates/hir_def/src/import_map.rs10
-rw-r--r--crates/hir_def/src/nameres.rs2
-rw-r--r--crates/hir_def/src/nameres/collector.rs3
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs7
-rw-r--r--crates/ide/src/completion/complete_postfix/format_like.rs2
-rw-r--r--crates/ide/src/completion/complete_unqualified_path.rs20
-rw-r--r--crates/ide/src/completion/completion_context.rs5
-rw-r--r--crates/ide/src/hover.rs32
-rw-r--r--crates/ide/src/link_rewrite.rs6
-rw-r--r--crates/ide/src/status.rs2
-rw-r--r--crates/project_model/src/lib.rs5
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs11
-rw-r--r--crates/rust-analyzer/src/config.rs17
-rw-r--r--editors/code/package.json17
-rw-r--r--xtask/src/codegen/gen_feature_docs.rs12
-rw-r--r--xtask/tests/tidy.rs7
29 files changed, 1061 insertions, 166 deletions
diff --git a/PRIVACY.md b/PRIVACY.md
new file mode 100644
index 000000000..dd165c0e2
--- /dev/null
+++ b/PRIVACY.md
@@ -0,0 +1,17 @@
1# Privacy Notes
2
3## LSP server binary
4
5The LSP server performs no network access in itself, but runs `cargo metadata` which will update or download the crate registry and the source code of the project dependencies.
6
7## Visual Studio Code extension
8
9The Code extension connects to GitHub to download updated LSP binaries and, if the nightly channel is selected, to perform update checks.
10
11## Other editor plugins
12
13Any other editor plugins that integrate with `rust-analyzer` are not under the control of the `rust-analyzer` developers. For any privacy concerns, you should check with their respective developers.
14
15## Others
16
17If `cargo check` is enabled (the default), any build scripts or procedural macros used by the project or its dependencies will be executed. This is also the case when `cargo check` is disabled, but build script or procedural macro support is enabled in `rust-analyzer` (off by default).
diff --git a/crates/assists/src/assist_config.rs b/crates/assists/src/assist_config.rs
index adf02edab..b24527ec4 100644
--- a/crates/assists/src/assist_config.rs
+++ b/crates/assists/src/assist_config.rs
@@ -4,6 +4,8 @@
4//! module, and we use to statically check that we only produce snippet 4//! module, and we use to statically check that we only produce snippet
5//! assists if we are allowed to. 5//! assists if we are allowed to.
6 6
7use hir::PrefixKind;
8
7use crate::{utils::MergeBehaviour, AssistKind}; 9use crate::{utils::MergeBehaviour, AssistKind};
8 10
9#[derive(Clone, Debug, PartialEq, Eq)] 11#[derive(Clone, Debug, PartialEq, Eq)]
@@ -37,10 +39,11 @@ impl Default for AssistConfig {
37#[derive(Clone, Copy, Debug, PartialEq, Eq)] 39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38pub struct InsertUseConfig { 40pub struct InsertUseConfig {
39 pub merge: Option<MergeBehaviour>, 41 pub merge: Option<MergeBehaviour>,
42 pub prefix_kind: PrefixKind,
40} 43}
41 44
42impl Default for InsertUseConfig { 45impl Default for InsertUseConfig {
43 fn default() -> Self { 46 fn default() -> Self {
44 InsertUseConfig { merge: Some(MergeBehaviour::Full) } 47 InsertUseConfig { merge: Some(MergeBehaviour::Full), prefix_kind: PrefixKind::Plain }
45 } 48 }
46} 49}
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index fa524ffd9..357ff6392 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -191,12 +191,16 @@ impl AutoImportAssets {
191 _ => Some(candidate), 191 _ => Some(candidate),
192 }) 192 })
193 .filter_map(|candidate| match candidate { 193 .filter_map(|candidate| match candidate {
194 Either::Left(module_def) => { 194 Either::Left(module_def) => self.module_with_name_to_import.find_use_path_prefixed(
195 self.module_with_name_to_import.find_use_path_prefixed(db, module_def) 195 db,
196 } 196 module_def,
197 Either::Right(macro_def) => { 197 ctx.config.insert_use.prefix_kind,
198 self.module_with_name_to_import.find_use_path_prefixed(db, macro_def) 198 ),
199 } 199 Either::Right(macro_def) => self.module_with_name_to_import.find_use_path_prefixed(
200 db,
201 macro_def,
202 ctx.config.insert_use.prefix_kind,
203 ),
200 }) 204 })
201 .filter(|use_path| !use_path.segments.is_empty()) 205 .filter(|use_path| !use_path.segments.is_empty())
202 .take(20) 206 .take(20)
diff --git a/crates/assists/src/handlers/convert_integer_literal.rs b/crates/assists/src/handlers/convert_integer_literal.rs
new file mode 100644
index 000000000..ea35e833a
--- /dev/null
+++ b/crates/assists/src/handlers/convert_integer_literal.rs
@@ -0,0 +1,701 @@
1use syntax::{ast, AstNode, SmolStr};
2
3use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
4
5// Assist: convert_integer_literal
6//
7// Converts the base of integer literals to other bases.
8//
9// ```
10// const _: i32 = 10<|>;
11// ```
12// ->
13// ```
14// const _: i32 = 0b1010;
15// ```
16pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
17 let literal = ctx.find_node_at_offset::<ast::Literal>()?;
18 let range = literal.syntax().text_range();
19 let group_id = GroupLabel("Convert integer base".into());
20
21 let suffix = match literal.kind() {
22 ast::LiteralKind::IntNumber { suffix } => suffix,
23 _ => return None,
24 };
25 let suffix_len = suffix.as_ref().map(|s| s.len()).unwrap_or(0);
26 let raw_literal_text = literal.syntax().to_string();
27
28 // Gets the literal's text without the type suffix and without underscores.
29 let literal_text = raw_literal_text
30 .chars()
31 .take(raw_literal_text.len() - suffix_len)
32 .filter(|c| *c != '_')
33 .collect::<SmolStr>();
34 let literal_base = IntegerLiteralBase::identify(&literal_text)?;
35
36 for base in IntegerLiteralBase::bases() {
37 if *base == literal_base {
38 continue;
39 }
40
41 let mut converted = literal_base.convert(&literal_text, base);
42
43 let label = if let Some(suffix) = &suffix {
44 format!("Convert {} ({}) to {}", &literal_text, suffix, &converted)
45 } else {
46 format!("Convert {} to {}", &literal_text, &converted)
47 };
48
49 // Appends the type suffix back into the new literal if it exists.
50 if let Some(suffix) = &suffix {
51 converted.push_str(&suffix);
52 }
53
54 acc.add_group(
55 &group_id,
56 AssistId("convert_integer_literal", AssistKind::RefactorInline),
57 label,
58 range,
59 |builder| builder.replace(range, converted),
60 );
61 }
62
63 Some(())
64}
65
66#[derive(Debug, PartialEq, Eq)]
67enum IntegerLiteralBase {
68 Binary,
69 Octal,
70 Decimal,
71 Hexadecimal,
72}
73
74impl IntegerLiteralBase {
75 fn identify(literal_text: &str) -> Option<Self> {
76 // We cannot express a literal in anything other than decimal in under 3 characters, so we return here if possible.
77 if literal_text.len() < 3 && literal_text.chars().all(|c| c.is_digit(10)) {
78 return Some(Self::Decimal);
79 }
80
81 let base = match &literal_text[..2] {
82 "0b" => Self::Binary,
83 "0o" => Self::Octal,
84 "0x" => Self::Hexadecimal,
85 _ => Self::Decimal,
86 };
87
88 // Checks that all characters after the base prefix are all valid digits for that base.
89 if literal_text[base.prefix_len()..].chars().all(|c| c.is_digit(base.base())) {
90 Some(base)
91 } else {
92 None
93 }
94 }
95
96 fn convert(&self, literal_text: &str, to: &IntegerLiteralBase) -> String {
97 let digits = &literal_text[self.prefix_len()..];
98 let value = u128::from_str_radix(digits, self.base()).unwrap();
99
100 match to {
101 Self::Binary => format!("0b{:b}", value),
102 Self::Octal => format!("0o{:o}", value),
103 Self::Decimal => value.to_string(),
104 Self::Hexadecimal => format!("0x{:X}", value),
105 }
106 }
107
108 const fn base(&self) -> u32 {
109 match self {
110 Self::Binary => 2,
111 Self::Octal => 8,
112 Self::Decimal => 10,
113 Self::Hexadecimal => 16,
114 }
115 }
116
117 const fn prefix_len(&self) -> usize {
118 match self {
119 Self::Decimal => 0,
120 _ => 2,
121 }
122 }
123
124 const fn bases() -> &'static [IntegerLiteralBase] {
125 &[
126 IntegerLiteralBase::Binary,
127 IntegerLiteralBase::Octal,
128 IntegerLiteralBase::Decimal,
129 IntegerLiteralBase::Hexadecimal,
130 ]
131 }
132}
133
134#[cfg(test)]
135mod tests {
136
137 use super::*;
138 use crate::tests::{check_assist_by_label, check_assist_target};
139
140 #[test]
141 fn binary_target() {
142 check_assist_target(convert_integer_literal, "const _: i32 = 0b1010<|>;", "0b1010");
143 }
144
145 #[test]
146 fn octal_target() {
147 check_assist_target(convert_integer_literal, "const _: i32 = 0o12<|>;", "0o12");
148 }
149
150 #[test]
151 fn decimal_target() {
152 check_assist_target(convert_integer_literal, "const _: i32 = 10<|>;", "10");
153 }
154
155 #[test]
156 fn hexadecimal_target() {
157 check_assist_target(convert_integer_literal, "const _: i32 = 0xA<|>;", "0xA");
158 }
159
160 #[test]
161 fn binary_target_with_underscores() {
162 check_assist_target(convert_integer_literal, "const _: i32 = 0b10_10<|>;", "0b10_10");
163 }
164
165 #[test]
166 fn octal_target_with_underscores() {
167 check_assist_target(convert_integer_literal, "const _: i32 = 0o1_2<|>;", "0o1_2");
168 }
169
170 #[test]
171 fn decimal_target_with_underscores() {
172 check_assist_target(convert_integer_literal, "const _: i32 = 1_0<|>;", "1_0");
173 }
174
175 #[test]
176 fn hexadecimal_target_with_underscores() {
177 check_assist_target(convert_integer_literal, "const _: i32 = 0x_A<|>;", "0x_A");
178 }
179
180 #[test]
181 fn convert_decimal_integer() {
182 let before = "const _: i32 = 1000<|>;";
183
184 check_assist_by_label(
185 convert_integer_literal,
186 before,
187 "const _: i32 = 0b1111101000;",
188 "Convert 1000 to 0b1111101000",
189 );
190
191 check_assist_by_label(
192 convert_integer_literal,
193 before,
194 "const _: i32 = 0o1750;",
195 "Convert 1000 to 0o1750",
196 );
197
198 check_assist_by_label(
199 convert_integer_literal,
200 before,
201 "const _: i32 = 0x3E8;",
202 "Convert 1000 to 0x3E8",
203 );
204 }
205
206 // Decimal numbers under 3 digits have a special case where they return early because we can't fit a
207 // other base's prefix, so we have a separate test for that.
208 #[test]
209 fn convert_small_decimal_integer() {
210 let before = "const _: i32 = 10<|>;";
211
212 check_assist_by_label(
213 convert_integer_literal,
214 before,
215 "const _: i32 = 0b1010;",
216 "Convert 10 to 0b1010",
217 );
218
219 check_assist_by_label(
220 convert_integer_literal,
221 before,
222 "const _: i32 = 0o12;",
223 "Convert 10 to 0o12",
224 );
225
226 check_assist_by_label(
227 convert_integer_literal,
228 before,
229 "const _: i32 = 0xA;",
230 "Convert 10 to 0xA",
231 );
232 }
233
234 #[test]
235 fn convert_hexadecimal_integer() {
236 let before = "const _: i32 = 0xFF<|>;";
237
238 check_assist_by_label(
239 convert_integer_literal,
240 before,
241 "const _: i32 = 0b11111111;",
242 "Convert 0xFF to 0b11111111",
243 );
244
245 check_assist_by_label(
246 convert_integer_literal,
247 before,
248 "const _: i32 = 0o377;",
249 "Convert 0xFF to 0o377",
250 );
251
252 check_assist_by_label(
253 convert_integer_literal,
254 before,
255 "const _: i32 = 255;",
256 "Convert 0xFF to 255",
257 );
258 }
259
260 #[test]
261 fn convert_binary_integer() {
262 let before = "const _: i32 = 0b11111111<|>;";
263
264 check_assist_by_label(
265 convert_integer_literal,
266 before,
267 "const _: i32 = 0o377;",
268 "Convert 0b11111111 to 0o377",
269 );
270
271 check_assist_by_label(
272 convert_integer_literal,
273 before,
274 "const _: i32 = 255;",
275 "Convert 0b11111111 to 255",
276 );
277
278 check_assist_by_label(
279 convert_integer_literal,
280 before,
281 "const _: i32 = 0xFF;",
282 "Convert 0b11111111 to 0xFF",
283 );
284 }
285
286 #[test]
287 fn convert_octal_integer() {
288 let before = "const _: i32 = 0o377<|>;";
289
290 check_assist_by_label(
291 convert_integer_literal,
292 before,
293 "const _: i32 = 0b11111111;",
294 "Convert 0o377 to 0b11111111",
295 );
296
297 check_assist_by_label(
298 convert_integer_literal,
299 before,
300 "const _: i32 = 255;",
301 "Convert 0o377 to 255",
302 );
303
304 check_assist_by_label(
305 convert_integer_literal,
306 before,
307 "const _: i32 = 0xFF;",
308 "Convert 0o377 to 0xFF",
309 );
310 }
311
312 #[test]
313 fn convert_decimal_integer_with_underscores() {
314 let before = "const _: i32 = 1_00_0<|>;";
315
316 check_assist_by_label(
317 convert_integer_literal,
318 before,
319 "const _: i32 = 0b1111101000;",
320 "Convert 1000 to 0b1111101000",
321 );
322
323 check_assist_by_label(
324 convert_integer_literal,
325 before,
326 "const _: i32 = 0o1750;",
327 "Convert 1000 to 0o1750",
328 );
329
330 check_assist_by_label(
331 convert_integer_literal,
332 before,
333 "const _: i32 = 0x3E8;",
334 "Convert 1000 to 0x3E8",
335 );
336 }
337
338 #[test]
339 fn convert_small_decimal_integer_with_underscores() {
340 let before = "const _: i32 = 1_0<|>;";
341
342 check_assist_by_label(
343 convert_integer_literal,
344 before,
345 "const _: i32 = 0b1010;",
346 "Convert 10 to 0b1010",
347 );
348
349 check_assist_by_label(
350 convert_integer_literal,
351 before,
352 "const _: i32 = 0o12;",
353 "Convert 10 to 0o12",
354 );
355
356 check_assist_by_label(
357 convert_integer_literal,
358 before,
359 "const _: i32 = 0xA;",
360 "Convert 10 to 0xA",
361 );
362 }
363
364 #[test]
365 fn convert_hexadecimal_integer_with_underscores() {
366 let before = "const _: i32 = 0x_F_F<|>;";
367
368 check_assist_by_label(
369 convert_integer_literal,
370 before,
371 "const _: i32 = 0b11111111;",
372 "Convert 0xFF to 0b11111111",
373 );
374
375 check_assist_by_label(
376 convert_integer_literal,
377 before,
378 "const _: i32 = 0o377;",
379 "Convert 0xFF to 0o377",
380 );
381
382 check_assist_by_label(
383 convert_integer_literal,
384 before,
385 "const _: i32 = 255;",
386 "Convert 0xFF to 255",
387 );
388 }
389
390 #[test]
391 fn convert_binary_integer_with_underscores() {
392 let before = "const _: i32 = 0b1111_1111<|>;";
393
394 check_assist_by_label(
395 convert_integer_literal,
396 before,
397 "const _: i32 = 0o377;",
398 "Convert 0b11111111 to 0o377",
399 );
400
401 check_assist_by_label(
402 convert_integer_literal,
403 before,
404 "const _: i32 = 255;",
405 "Convert 0b11111111 to 255",
406 );
407
408 check_assist_by_label(
409 convert_integer_literal,
410 before,
411 "const _: i32 = 0xFF;",
412 "Convert 0b11111111 to 0xFF",
413 );
414 }
415
416 #[test]
417 fn convert_octal_integer_with_underscores() {
418 let before = "const _: i32 = 0o3_77<|>;";
419
420 check_assist_by_label(
421 convert_integer_literal,
422 before,
423 "const _: i32 = 0b11111111;",
424 "Convert 0o377 to 0b11111111",
425 );
426
427 check_assist_by_label(
428 convert_integer_literal,
429 before,
430 "const _: i32 = 255;",
431 "Convert 0o377 to 255",
432 );
433
434 check_assist_by_label(
435 convert_integer_literal,
436 before,
437 "const _: i32 = 0xFF;",
438 "Convert 0o377 to 0xFF",
439 );
440 }
441
442 #[test]
443 fn convert_decimal_integer_with_suffix() {
444 let before = "const _: i32 = 1000i32<|>;";
445
446 check_assist_by_label(
447 convert_integer_literal,
448 before,
449 "const _: i32 = 0b1111101000i32;",
450 "Convert 1000 (i32) to 0b1111101000",
451 );
452
453 check_assist_by_label(
454 convert_integer_literal,
455 before,
456 "const _: i32 = 0o1750i32;",
457 "Convert 1000 (i32) to 0o1750",
458 );
459
460 check_assist_by_label(
461 convert_integer_literal,
462 before,
463 "const _: i32 = 0x3E8i32;",
464 "Convert 1000 (i32) to 0x3E8",
465 );
466 }
467
468 #[test]
469 fn convert_small_decimal_integer_with_suffix() {
470 let before = "const _: i32 = 10i32<|>;";
471
472 check_assist_by_label(
473 convert_integer_literal,
474 before,
475 "const _: i32 = 0b1010i32;",
476 "Convert 10 (i32) to 0b1010",
477 );
478
479 check_assist_by_label(
480 convert_integer_literal,
481 before,
482 "const _: i32 = 0o12i32;",
483 "Convert 10 (i32) to 0o12",
484 );
485
486 check_assist_by_label(
487 convert_integer_literal,
488 before,
489 "const _: i32 = 0xAi32;",
490 "Convert 10 (i32) to 0xA",
491 );
492 }
493
494 #[test]
495 fn convert_hexadecimal_integer_with_suffix() {
496 let before = "const _: i32 = 0xFFi32<|>;";
497
498 check_assist_by_label(
499 convert_integer_literal,
500 before,
501 "const _: i32 = 0b11111111i32;",
502 "Convert 0xFF (i32) to 0b11111111",
503 );
504
505 check_assist_by_label(
506 convert_integer_literal,
507 before,
508 "const _: i32 = 0o377i32;",
509 "Convert 0xFF (i32) to 0o377",
510 );
511
512 check_assist_by_label(
513 convert_integer_literal,
514 before,
515 "const _: i32 = 255i32;",
516 "Convert 0xFF (i32) to 255",
517 );
518 }
519
520 #[test]
521 fn convert_binary_integer_with_suffix() {
522 let before = "const _: i32 = 0b11111111i32<|>;";
523
524 check_assist_by_label(
525 convert_integer_literal,
526 before,
527 "const _: i32 = 0o377i32;",
528 "Convert 0b11111111 (i32) to 0o377",
529 );
530
531 check_assist_by_label(
532 convert_integer_literal,
533 before,
534 "const _: i32 = 255i32;",
535 "Convert 0b11111111 (i32) to 255",
536 );
537
538 check_assist_by_label(
539 convert_integer_literal,
540 before,
541 "const _: i32 = 0xFFi32;",
542 "Convert 0b11111111 (i32) to 0xFF",
543 );
544 }
545
546 #[test]
547 fn convert_octal_integer_with_suffix() {
548 let before = "const _: i32 = 0o377i32<|>;";
549
550 check_assist_by_label(
551 convert_integer_literal,
552 before,
553 "const _: i32 = 0b11111111i32;",
554 "Convert 0o377 (i32) to 0b11111111",
555 );
556
557 check_assist_by_label(
558 convert_integer_literal,
559 before,
560 "const _: i32 = 255i32;",
561 "Convert 0o377 (i32) to 255",
562 );
563
564 check_assist_by_label(
565 convert_integer_literal,
566 before,
567 "const _: i32 = 0xFFi32;",
568 "Convert 0o377 (i32) to 0xFF",
569 );
570 }
571
572 #[test]
573 fn convert_decimal_integer_with_underscores_and_suffix() {
574 let before = "const _: i32 = 1_00_0i32<|>;";
575
576 check_assist_by_label(
577 convert_integer_literal,
578 before,
579 "const _: i32 = 0b1111101000i32;",
580 "Convert 1000 (i32) to 0b1111101000",
581 );
582
583 check_assist_by_label(
584 convert_integer_literal,
585 before,
586 "const _: i32 = 0o1750i32;",
587 "Convert 1000 (i32) to 0o1750",
588 );
589
590 check_assist_by_label(
591 convert_integer_literal,
592 before,
593 "const _: i32 = 0x3E8i32;",
594 "Convert 1000 (i32) to 0x3E8",
595 );
596 }
597
598 #[test]
599 fn convert_small_decimal_integer_with_underscores_and_suffix() {
600 let before = "const _: i32 = 1_0i32<|>;";
601
602 check_assist_by_label(
603 convert_integer_literal,
604 before,
605 "const _: i32 = 0b1010i32;",
606 "Convert 10 (i32) to 0b1010",
607 );
608
609 check_assist_by_label(
610 convert_integer_literal,
611 before,
612 "const _: i32 = 0o12i32;",
613 "Convert 10 (i32) to 0o12",
614 );
615
616 check_assist_by_label(
617 convert_integer_literal,
618 before,
619 "const _: i32 = 0xAi32;",
620 "Convert 10 (i32) to 0xA",
621 );
622 }
623
624 #[test]
625 fn convert_hexadecimal_integer_with_underscores_and_suffix() {
626 let before = "const _: i32 = 0x_F_Fi32<|>;";
627
628 check_assist_by_label(
629 convert_integer_literal,
630 before,
631 "const _: i32 = 0b11111111i32;",
632 "Convert 0xFF (i32) to 0b11111111",
633 );
634
635 check_assist_by_label(
636 convert_integer_literal,
637 before,
638 "const _: i32 = 0o377i32;",
639 "Convert 0xFF (i32) to 0o377",
640 );
641
642 check_assist_by_label(
643 convert_integer_literal,
644 before,
645 "const _: i32 = 255i32;",
646 "Convert 0xFF (i32) to 255",
647 );
648 }
649
650 #[test]
651 fn convert_binary_integer_with_underscores_and_suffix() {
652 let before = "const _: i32 = 0b1111_1111i32<|>;";
653
654 check_assist_by_label(
655 convert_integer_literal,
656 before,
657 "const _: i32 = 0o377i32;",
658 "Convert 0b11111111 (i32) to 0o377",
659 );
660
661 check_assist_by_label(
662 convert_integer_literal,
663 before,
664 "const _: i32 = 255i32;",
665 "Convert 0b11111111 (i32) to 255",
666 );
667
668 check_assist_by_label(
669 convert_integer_literal,
670 before,
671 "const _: i32 = 0xFFi32;",
672 "Convert 0b11111111 (i32) to 0xFF",
673 );
674 }
675
676 #[test]
677 fn convert_octal_integer_with_underscores_and_suffix() {
678 let before = "const _: i32 = 0o3_77i32<|>;";
679
680 check_assist_by_label(
681 convert_integer_literal,
682 before,
683 "const _: i32 = 0b11111111i32;",
684 "Convert 0o377 (i32) to 0b11111111",
685 );
686
687 check_assist_by_label(
688 convert_integer_literal,
689 before,
690 "const _: i32 = 255i32;",
691 "Convert 0o377 (i32) to 255",
692 );
693
694 check_assist_by_label(
695 convert_integer_literal,
696 before,
697 "const _: i32 = 0xFFi32;",
698 "Convert 0o377 (i32) to 0xFF",
699 );
700 }
701}
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs
index a8ab2aecc..e10616779 100644
--- a/crates/assists/src/handlers/remove_dbg.rs
+++ b/crates/assists/src/handlers/remove_dbg.rs
@@ -1,6 +1,6 @@
1use syntax::{ 1use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 SyntaxElement, TextRange, TextSize, T, 3 SyntaxElement, SyntaxKind, TextRange, TextSize, T,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -117,7 +117,10 @@ fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) -
117 } 117 }
118 symbol_kind => { 118 symbol_kind => {
119 let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty(); 119 let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty();
120 if symbol_not_in_bracket && symbol_kind.is_punct() { 120 if symbol_not_in_bracket
121 && symbol_kind != SyntaxKind::COLON
122 && symbol_kind.is_punct()
123 {
121 return true; 124 return true;
122 } 125 }
123 } 126 }
@@ -159,6 +162,8 @@ fn foo(n: usize) {
159} 162}
160", 163",
161 ); 164 );
165
166 check_assist(remove_dbg, "<|>dbg!(Foo::foo_test()).bar()", "Foo::foo_test().bar()");
162 } 167 }
163 168
164 #[test] 169 #[test]
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index cbac53e71..a2bec818c 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -128,6 +128,7 @@ mod handlers {
128 mod auto_import; 128 mod auto_import;
129 mod change_return_type_to_result; 129 mod change_return_type_to_result;
130 mod change_visibility; 130 mod change_visibility;
131 mod convert_integer_literal;
131 mod early_return; 132 mod early_return;
132 mod expand_glob_import; 133 mod expand_glob_import;
133 mod extract_struct_from_enum_variant; 134 mod extract_struct_from_enum_variant;
@@ -172,6 +173,7 @@ mod handlers {
172 auto_import::auto_import, 173 auto_import::auto_import,
173 change_return_type_to_result::change_return_type_to_result, 174 change_return_type_to_result::change_return_type_to_result,
174 change_visibility::change_visibility, 175 change_visibility::change_visibility,
176 convert_integer_literal::convert_integer_literal,
175 early_return::convert_to_guarded_return, 177 early_return::convert_to_guarded_return,
176 expand_glob_import::expand_glob_import, 178 expand_glob_import::expand_glob_import,
177 extract_struct_from_enum_variant::extract_struct_from_enum_variant, 179 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs
index ba1fb543b..2b687decf 100644
--- a/crates/assists/src/tests.rs
+++ b/crates/assists/src/tests.rs
@@ -15,18 +15,30 @@ pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
15 15
16pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) { 16pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) {
17 let ra_fixture_after = trim_indent(ra_fixture_after); 17 let ra_fixture_after = trim_indent(ra_fixture_after);
18 check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after)); 18 check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after), None);
19}
20
21// There is no way to choose what assist within a group you want to test against,
22// so this is here to allow you choose.
23pub(crate) fn check_assist_by_label(
24 assist: Handler,
25 ra_fixture_before: &str,
26 ra_fixture_after: &str,
27 label: &str,
28) {
29 let ra_fixture_after = trim_indent(ra_fixture_after);
30 check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after), Some(label));
19} 31}
20 32
21// FIXME: instead of having a separate function here, maybe use 33// FIXME: instead of having a separate function here, maybe use
22// `extract_ranges` and mark the target as `<target> </target>` in the 34// `extract_ranges` and mark the target as `<target> </target>` in the
23// fixture? 35// fixture?
24pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) { 36pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) {
25 check(assist, ra_fixture, ExpectedResult::Target(target)); 37 check(assist, ra_fixture, ExpectedResult::Target(target), None);
26} 38}
27 39
28pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) { 40pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) {
29 check(assist, ra_fixture, ExpectedResult::NotApplicable); 41 check(assist, ra_fixture, ExpectedResult::NotApplicable, None);
30} 42}
31 43
32fn check_doc_test(assist_id: &str, before: &str, after: &str) { 44fn check_doc_test(assist_id: &str, before: &str, after: &str) {
@@ -65,7 +77,7 @@ enum ExpectedResult<'a> {
65 Target(&'a str), 77 Target(&'a str),
66} 78}
67 79
68fn check(handler: Handler, before: &str, expected: ExpectedResult) { 80fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: Option<&str>) {
69 let (db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before); 81 let (db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
70 let text_without_caret = db.file_text(file_with_caret_id).to_string(); 82 let text_without_caret = db.file_text(file_with_caret_id).to_string();
71 83
@@ -77,7 +89,12 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
77 let mut acc = Assists::new_resolved(&ctx); 89 let mut acc = Assists::new_resolved(&ctx);
78 handler(&mut acc, &ctx); 90 handler(&mut acc, &ctx);
79 let mut res = acc.finish_resolved(); 91 let mut res = acc.finish_resolved();
80 let assist = res.pop(); 92
93 let assist = match assist_label {
94 Some(label) => res.into_iter().find(|resolved| resolved.assist.label == label),
95 None => res.pop(),
96 };
97
81 match (assist, expected) { 98 match (assist, expected) {
82 (Some(assist), ExpectedResult::After(after)) => { 99 (Some(assist), ExpectedResult::After(after)) => {
83 let mut source_change = assist.source_change; 100 let mut source_change = assist.source_change;
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 27d15adb0..7f6e98a54 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -204,6 +204,19 @@ pub(crate) fn frobnicate() {}
204} 204}
205 205
206#[test] 206#[test]
207fn doctest_convert_integer_literal() {
208 check_doc_test(
209 "convert_integer_literal",
210 r#####"
211const _: i32 = 10<|>;
212"#####,
213 r#####"
214const _: i32 = 0b1010;
215"#####,
216 )
217}
218
219#[test]
207fn doctest_convert_to_guarded_return() { 220fn doctest_convert_to_guarded_return() {
208 check_doc_test( 221 check_doc_test(
209 "convert_to_guarded_return", 222 "convert_to_guarded_return",
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index b7286fc7d..72f1fd667 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -154,19 +154,19 @@ impl ChangeFixture {
154 assert!(meta.path.starts_with(&source_root_prefix)); 154 assert!(meta.path.starts_with(&source_root_prefix));
155 155
156 if let Some(krate) = meta.krate { 156 if let Some(krate) = meta.krate {
157 let crate_name = CrateName::normalize_dashes(&krate);
157 let crate_id = crate_graph.add_crate_root( 158 let crate_id = crate_graph.add_crate_root(
158 file_id, 159 file_id,
159 meta.edition, 160 meta.edition,
160 Some(krate.clone()), 161 Some(crate_name.clone()),
161 meta.cfg, 162 meta.cfg,
162 meta.env, 163 meta.env,
163 Default::default(), 164 Default::default(),
164 ); 165 );
165 let crate_name = CrateName::new(&krate).unwrap();
166 let prev = crates.insert(crate_name.clone(), crate_id); 166 let prev = crates.insert(crate_name.clone(), crate_id);
167 assert!(prev.is_none()); 167 assert!(prev.is_none());
168 for dep in meta.deps { 168 for dep in meta.deps {
169 let dep = CrateName::new(&dep).unwrap(); 169 let dep = CrateName::normalize_dashes(&dep);
170 crate_deps.push((crate_name.clone(), dep)) 170 crate_deps.push((crate_name.clone(), dep))
171 } 171 }
172 } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { 172 } else if meta.path == "/main.rs" || meta.path == "/lib.rs" {
@@ -187,7 +187,7 @@ impl ChangeFixture {
187 crate_graph.add_crate_root( 187 crate_graph.add_crate_root(
188 crate_root, 188 crate_root,
189 Edition::Edition2018, 189 Edition::Edition2018,
190 Some("test".to_string()), 190 Some(CrateName::new("test").unwrap()),
191 default_cfg, 191 default_cfg,
192 Env::default(), 192 Env::default(),
193 Default::default(), 193 Default::default(),
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 9a61f1d56..c330314d4 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -127,10 +127,11 @@ impl PartialEq for ProcMacro {
127pub struct CrateData { 127pub struct CrateData {
128 pub root_file_id: FileId, 128 pub root_file_id: FileId,
129 pub edition: Edition, 129 pub edition: Edition,
130 /// The name to display to the end user. 130 /// A name used in the package's project declaration: for Cargo projects, it's [package].name,
131 /// This actual crate name can be different in a particular dependent crate 131 /// can be different for other project types or even absent (a dummy crate for the code snippet, for example).
132 /// or may even be missing for some cases, such as a dummy crate for the code snippet. 132 /// NOTE: The crate can be referenced as a dependency under a different name,
133 pub display_name: Option<String>, 133 /// this one should be used when working with crate hierarchies.
134 pub declaration_name: Option<CrateName>,
134 pub cfg_options: CfgOptions, 135 pub cfg_options: CfgOptions,
135 pub env: Env, 136 pub env: Env,
136 pub dependencies: Vec<Dependency>, 137 pub dependencies: Vec<Dependency>,
@@ -159,7 +160,7 @@ impl CrateGraph {
159 &mut self, 160 &mut self,
160 file_id: FileId, 161 file_id: FileId,
161 edition: Edition, 162 edition: Edition,
162 display_name: Option<String>, 163 declaration_name: Option<CrateName>,
163 cfg_options: CfgOptions, 164 cfg_options: CfgOptions,
164 env: Env, 165 env: Env,
165 proc_macro: Vec<(SmolStr, Arc<dyn tt::TokenExpander>)>, 166 proc_macro: Vec<(SmolStr, Arc<dyn tt::TokenExpander>)>,
@@ -170,7 +171,7 @@ impl CrateGraph {
170 let data = CrateData { 171 let data = CrateData {
171 root_file_id: file_id, 172 root_file_id: file_id,
172 edition, 173 edition,
173 display_name, 174 declaration_name,
174 cfg_options, 175 cfg_options,
175 env, 176 env,
176 proc_macro, 177 proc_macro,
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 5721a66c4..c75d46bff 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -2,8 +2,9 @@
2use std::{iter, sync::Arc}; 2use std::{iter, sync::Arc};
3 3
4use arrayvec::ArrayVec; 4use arrayvec::ArrayVec;
5use base_db::{CrateId, Edition, FileId}; 5use base_db::{CrateId, CrateName, Edition, FileId};
6use either::Either; 6use either::Either;
7use hir_def::find_path::PrefixKind;
7use hir_def::{ 8use hir_def::{
8 adt::ReprKind, 9 adt::ReprKind,
9 adt::StructKind, 10 adt::StructKind,
@@ -98,8 +99,8 @@ impl Crate {
98 db.crate_graph()[self.id].edition 99 db.crate_graph()[self.id].edition
99 } 100 }
100 101
101 pub fn display_name(self, db: &dyn HirDatabase) -> Option<String> { 102 pub fn declaration_name(self, db: &dyn HirDatabase) -> Option<CrateName> {
102 db.crate_graph()[self.id].display_name.clone() 103 db.crate_graph()[self.id].declaration_name.clone()
103 } 104 }
104 105
105 pub fn query_external_importables( 106 pub fn query_external_importables(
@@ -390,8 +391,9 @@ impl Module {
390 self, 391 self,
391 db: &dyn DefDatabase, 392 db: &dyn DefDatabase,
392 item: impl Into<ItemInNs>, 393 item: impl Into<ItemInNs>,
394 prefix_kind: PrefixKind,
393 ) -> Option<ModPath> { 395 ) -> Option<ModPath> {
394 hir_def::find_path::find_path_prefixed(db, item.into(), self.into()) 396 hir_def::find_path::find_path_prefixed(db, item.into(), self.into(), prefix_kind)
395 } 397 }
396} 398}
397 399
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b9d9c7e25..87084fa13 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -48,6 +48,7 @@ pub use hir_def::{
48 body::scope::ExprScopes, 48 body::scope::ExprScopes,
49 builtin_type::BuiltinType, 49 builtin_type::BuiltinType,
50 docs::Documentation, 50 docs::Documentation,
51 find_path::PrefixKind,
51 item_scope::ItemInNs, 52 item_scope::ItemInNs,
52 nameres::ModuleSource, 53 nameres::ModuleSource,
53 path::ModPath, 54 path::ModPath,
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index baf374144..9106ed45f 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -19,12 +19,17 @@ use crate::{
19/// *from where* you're referring to the item, hence the `from` parameter. 19/// *from where* you're referring to the item, hence the `from` parameter.
20pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 20pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
21 let _p = profile::span("find_path"); 21 let _p = profile::span("find_path");
22 find_path_inner(db, item, from, MAX_PATH_LEN, Prefixed::Not) 22 find_path_inner(db, item, from, MAX_PATH_LEN, None)
23} 23}
24 24
25pub fn find_path_prefixed(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 25pub fn find_path_prefixed(
26 let _p = profile::span("find_path_absolute"); 26 db: &dyn DefDatabase,
27 find_path_inner(db, item, from, MAX_PATH_LEN, Prefixed::Plain) 27 item: ItemInNs,
28 from: ModuleId,
29 prefix_kind: PrefixKind,
30) -> Option<ModPath> {
31 let _p = profile::span("find_path_prefixed");
32 find_path_inner(db, item, from, MAX_PATH_LEN, Some(prefix_kind))
28} 33}
29 34
30const MAX_PATH_LEN: usize = 15; 35const MAX_PATH_LEN: usize = 15;
@@ -42,58 +47,52 @@ impl ModPath {
42 } 47 }
43} 48}
44 49
45fn check_crate_self_super( 50fn check_self_super(def_map: &CrateDefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
46 def_map: &CrateDefMap, 51 if item == ItemInNs::Types(from.into()) {
47 item: ItemInNs,
48 from: ModuleId,
49) -> Option<ModPath> {
50 // - if the item is the crate root, return `crate`
51 if item
52 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
53 krate: from.krate,
54 local_id: def_map.root,
55 }))
56 {
57 Some(ModPath::from_segments(PathKind::Crate, Vec::new()))
58 } else if item == ItemInNs::Types(from.into()) {
59 // - if the item is the module we're in, use `self` 52 // - if the item is the module we're in, use `self`
60 Some(ModPath::from_segments(PathKind::Super(0), Vec::new())) 53 Some(ModPath::from_segments(PathKind::Super(0), Vec::new()))
61 } else { 54 } else if let Some(parent_id) = def_map.modules[from.local_id].parent {
62 if let Some(parent_id) = def_map.modules[from.local_id].parent { 55 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
63 // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly) 56 if item
64 if item 57 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
65 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId { 58 krate: from.krate,
66 krate: from.krate, 59 local_id: parent_id,
67 local_id: parent_id, 60 }))
68 })) 61 {
69 { 62 Some(ModPath::from_segments(PathKind::Super(1), Vec::new()))
70 return Some(ModPath::from_segments(PathKind::Super(1), Vec::new())); 63 } else {
71 } 64 None
72 } 65 }
66 } else {
73 None 67 None
74 } 68 }
75} 69}
76 70
77#[derive(Copy, Clone, PartialEq, Eq)] 71#[derive(Copy, Clone, Debug, PartialEq, Eq)]
78pub enum Prefixed { 72pub enum PrefixKind {
79 Not, 73 /// Causes paths to always start with either `self`, `super`, `crate` or a crate-name.
74 /// This is the same as plain, just that paths will start with `self` iprepended f the path
75 /// starts with an identifier that is not a crate.
80 BySelf, 76 BySelf,
77 /// Causes paths to ignore imports in the local module.
81 Plain, 78 Plain,
79 /// Causes paths to start with `crate` where applicable, effectively forcing paths to be absolute.
80 ByCrate,
82} 81}
83 82
84impl Prefixed { 83impl PrefixKind {
85 #[inline] 84 #[inline]
86 fn prefix(self) -> Option<PathKind> { 85 fn prefix(self) -> PathKind {
87 match self { 86 match self {
88 Prefixed::Not => None, 87 PrefixKind::BySelf => PathKind::Super(0),
89 Prefixed::BySelf => Some(PathKind::Super(0)), 88 PrefixKind::Plain => PathKind::Plain,
90 Prefixed::Plain => Some(PathKind::Plain), 89 PrefixKind::ByCrate => PathKind::Crate,
91 } 90 }
92 } 91 }
93 92
94 #[inline] 93 #[inline]
95 fn prefixed(self) -> bool { 94 fn is_absolute(&self) -> bool {
96 self != Prefixed::Not 95 self == &PrefixKind::ByCrate
97 } 96 }
98} 97}
99 98
@@ -102,7 +101,7 @@ fn find_path_inner(
102 item: ItemInNs, 101 item: ItemInNs,
103 from: ModuleId, 102 from: ModuleId,
104 max_len: usize, 103 max_len: usize,
105 prefixed: Prefixed, 104 prefixed: Option<PrefixKind>,
106) -> Option<ModPath> { 105) -> Option<ModPath> {
107 if max_len == 0 { 106 if max_len == 0 {
108 return None; 107 return None;
@@ -115,13 +114,25 @@ fn find_path_inner(
115 let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope; 114 let from_scope: &crate::item_scope::ItemScope = &def_map.modules[from.local_id].scope;
116 let scope_name = 115 let scope_name =
117 if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None }; 116 if let Some((name, _)) = from_scope.name_of(item) { Some(name.clone()) } else { None };
118 if !prefixed.prefixed() && scope_name.is_some() { 117 if prefixed.is_none() && scope_name.is_some() {
119 return scope_name 118 return scope_name
120 .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name])); 119 .map(|scope_name| ModPath::from_segments(PathKind::Plain, vec![scope_name]));
121 } 120 }
122 121
123 if let modpath @ Some(_) = check_crate_self_super(&def_map, item, from) { 122 // - if the item is the crate root, return `crate`
124 return modpath; 123 if item
124 == ItemInNs::Types(ModuleDefId::ModuleId(ModuleId {
125 krate: from.krate,
126 local_id: def_map.root,
127 }))
128 {
129 return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
130 }
131
132 if prefixed.filter(PrefixKind::is_absolute).is_none() {
133 if let modpath @ Some(_) = check_self_super(&def_map, item, from) {
134 return modpath;
135 }
125 } 136 }
126 137
127 // - if the item is the crate root of a dependency crate, return the name from the extern prelude 138 // - if the item is the crate root of a dependency crate, return the name from the extern prelude
@@ -226,7 +237,7 @@ fn find_path_inner(
226 } 237 }
227 } 238 }
228 239
229 if let Some(prefix) = prefixed.prefix() { 240 if let Some(prefix) = prefixed.map(PrefixKind::prefix) {
230 best_path.or_else(|| { 241 best_path.or_else(|| {
231 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name])) 242 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name]))
232 }) 243 })
@@ -355,7 +366,7 @@ mod tests {
355 /// `code` needs to contain a cursor marker; checks that `find_path` for the 366 /// `code` needs to contain a cursor marker; checks that `find_path` for the
356 /// item the `path` refers to returns that same path when called from the 367 /// item the `path` refers to returns that same path when called from the
357 /// module the cursor is in. 368 /// module the cursor is in.
358 fn check_found_path_(ra_fixture: &str, path: &str, absolute: bool) { 369 fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) {
359 let (db, pos) = TestDB::with_position(ra_fixture); 370 let (db, pos) = TestDB::with_position(ra_fixture);
360 let module = db.module_for_file(pos.file_id); 371 let module = db.module_for_file(pos.file_id);
361 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); 372 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
@@ -375,20 +386,22 @@ mod tests {
375 .take_types() 386 .take_types()
376 .unwrap(); 387 .unwrap();
377 388
378 let found_path = if absolute { find_path_prefixed } else { find_path }( 389 let found_path =
379 &db, 390 find_path_inner(&db, ItemInNs::Types(resolved), module, MAX_PATH_LEN, prefix_kind);
380 ItemInNs::Types(resolved), 391 assert_eq!(found_path, Some(mod_path), "{:?}", prefix_kind);
381 module,
382 );
383 assert_eq!(found_path, Some(mod_path), "absolute {}", absolute);
384 } 392 }
385 393
386 fn check_found_path(ra_fixture: &str, path: &str) { 394 fn check_found_path(
387 check_found_path_(ra_fixture, path, false); 395 ra_fixture: &str,
388 } 396 unprefixed: &str,
389 397 prefixed: &str,
390 fn check_found_path_abs(ra_fixture: &str, path: &str) { 398 absolute: &str,
391 check_found_path_(ra_fixture, path, true); 399 self_prefixed: &str,
400 ) {
401 check_found_path_(ra_fixture, unprefixed, None);
402 check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain));
403 check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate));
404 check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf));
392 } 405 }
393 406
394 #[test] 407 #[test]
@@ -398,8 +411,7 @@ mod tests {
398 struct S; 411 struct S;
399 <|> 412 <|>
400 "#; 413 "#;
401 check_found_path(code, "S"); 414 check_found_path(code, "S", "S", "crate::S", "self::S");
402 check_found_path_abs(code, "S");
403 } 415 }
404 416
405 #[test] 417 #[test]
@@ -409,8 +421,7 @@ mod tests {
409 enum E { A } 421 enum E { A }
410 <|> 422 <|>
411 "#; 423 "#;
412 check_found_path(code, "E::A"); 424 check_found_path(code, "E::A", "E::A", "E::A", "E::A");
413 check_found_path_abs(code, "E::A");
414 } 425 }
415 426
416 #[test] 427 #[test]
@@ -422,8 +433,7 @@ mod tests {
422 } 433 }
423 <|> 434 <|>
424 "#; 435 "#;
425 check_found_path(code, "foo::S"); 436 check_found_path(code, "foo::S", "foo::S", "crate::foo::S", "self::foo::S");
426 check_found_path_abs(code, "foo::S");
427 } 437 }
428 438
429 #[test] 439 #[test]
@@ -437,8 +447,7 @@ mod tests {
437 //- /foo/bar.rs 447 //- /foo/bar.rs
438 <|> 448 <|>
439 "#; 449 "#;
440 check_found_path(code, "super::S"); 450 check_found_path(code, "super::S", "super::S", "crate::foo::S", "super::S");
441 check_found_path_abs(code, "super::S");
442 } 451 }
443 452
444 #[test] 453 #[test]
@@ -449,8 +458,7 @@ mod tests {
449 //- /foo.rs 458 //- /foo.rs
450 <|> 459 <|>
451 "#; 460 "#;
452 check_found_path(code, "self"); 461 check_found_path(code, "self", "self", "crate::foo", "self");
453 check_found_path_abs(code, "self");
454 } 462 }
455 463
456 #[test] 464 #[test]
@@ -461,8 +469,7 @@ mod tests {
461 //- /foo.rs 469 //- /foo.rs
462 <|> 470 <|>
463 "#; 471 "#;
464 check_found_path(code, "crate"); 472 check_found_path(code, "crate", "crate", "crate", "crate");
465 check_found_path_abs(code, "crate");
466 } 473 }
467 474
468 #[test] 475 #[test]
@@ -474,8 +481,7 @@ mod tests {
474 //- /foo.rs 481 //- /foo.rs
475 <|> 482 <|>
476 "#; 483 "#;
477 check_found_path(code, "crate::S"); 484 check_found_path(code, "crate::S", "crate::S", "crate::S", "crate::S");
478 check_found_path_abs(code, "crate::S");
479 } 485 }
480 486
481 #[test] 487 #[test]
@@ -486,8 +492,7 @@ mod tests {
486 //- /std.rs crate:std 492 //- /std.rs crate:std
487 pub struct S; 493 pub struct S;
488 "#; 494 "#;
489 check_found_path(code, "std::S"); 495 check_found_path(code, "std::S", "std::S", "std::S", "std::S");
490 check_found_path_abs(code, "std::S");
491 } 496 }
492 497
493 #[test] 498 #[test]
@@ -499,8 +504,13 @@ mod tests {
499 //- /std.rs crate:std 504 //- /std.rs crate:std
500 pub struct S; 505 pub struct S;
501 "#; 506 "#;
502 check_found_path(code, "std_renamed::S"); 507 check_found_path(
503 check_found_path_abs(code, "std_renamed::S"); 508 code,
509 "std_renamed::S",
510 "std_renamed::S",
511 "std_renamed::S",
512 "std_renamed::S",
513 );
504 } 514 }
505 515
506 #[test] 516 #[test]
@@ -520,8 +530,13 @@ mod tests {
520 } 530 }
521 } 531 }
522 "#; 532 "#;
523 check_found_path(code, "ast::ModuleItem"); 533 check_found_path(
524 check_found_path_abs(code, "syntax::ast::ModuleItem"); 534 code,
535 "ast::ModuleItem",
536 "syntax::ast::ModuleItem",
537 "syntax::ast::ModuleItem",
538 "syntax::ast::ModuleItem",
539 );
525 540
526 let code = r#" 541 let code = r#"
527 //- /main.rs crate:main deps:syntax 542 //- /main.rs crate:main deps:syntax
@@ -535,8 +550,13 @@ mod tests {
535 } 550 }
536 } 551 }
537 "#; 552 "#;
538 check_found_path(code, "syntax::ast::ModuleItem"); 553 check_found_path(
539 check_found_path_abs(code, "syntax::ast::ModuleItem"); 554 code,
555 "syntax::ast::ModuleItem",
556 "syntax::ast::ModuleItem",
557 "syntax::ast::ModuleItem",
558 "syntax::ast::ModuleItem",
559 );
540 } 560 }
541 561
542 #[test] 562 #[test]
@@ -549,8 +569,7 @@ mod tests {
549 } 569 }
550 <|> 570 <|>
551 "#; 571 "#;
552 check_found_path(code, "bar::S"); 572 check_found_path(code, "bar::S", "bar::S", "crate::bar::S", "self::bar::S");
553 check_found_path_abs(code, "bar::S");
554 } 573 }
555 574
556 #[test] 575 #[test]
@@ -563,8 +582,7 @@ mod tests {
563 } 582 }
564 <|> 583 <|>
565 "#; 584 "#;
566 check_found_path(code, "bar::U"); 585 check_found_path(code, "bar::U", "bar::U", "crate::bar::U", "self::bar::U");
567 check_found_path_abs(code, "bar::U");
568 } 586 }
569 587
570 #[test] 588 #[test]
@@ -577,8 +595,7 @@ mod tests {
577 //- /core.rs crate:core 595 //- /core.rs crate:core
578 pub struct S; 596 pub struct S;
579 "#; 597 "#;
580 check_found_path(code, "std::S"); 598 check_found_path(code, "std::S", "std::S", "std::S", "std::S");
581 check_found_path_abs(code, "std::S");
582 } 599 }
583 600
584 #[test] 601 #[test]
@@ -591,8 +608,7 @@ mod tests {
591 #[prelude_import] 608 #[prelude_import]
592 pub use prelude::*; 609 pub use prelude::*;
593 "#; 610 "#;
594 check_found_path(code, "S"); 611 check_found_path(code, "S", "S", "S", "S");
595 check_found_path_abs(code, "S");
596 } 612 }
597 613
598 #[test] 614 #[test]
@@ -608,10 +624,8 @@ mod tests {
608 #[prelude_import] 624 #[prelude_import]
609 pub use prelude::*; 625 pub use prelude::*;
610 "#; 626 "#;
611 check_found_path(code, "None"); 627 check_found_path(code, "None", "None", "None", "None");
612 check_found_path(code, "Some"); 628 check_found_path(code, "Some", "Some", "Some", "Some");
613 check_found_path_abs(code, "None");
614 check_found_path_abs(code, "Some");
615 } 629 }
616 630
617 #[test] 631 #[test]
@@ -627,8 +641,7 @@ mod tests {
627 //- /baz.rs 641 //- /baz.rs
628 pub use crate::foo::bar::S; 642 pub use crate::foo::bar::S;
629 "#; 643 "#;
630 check_found_path(code, "baz::S"); 644 check_found_path(code, "baz::S", "baz::S", "crate::baz::S", "self::baz::S");
631 check_found_path_abs(code, "baz::S");
632 } 645 }
633 646
634 #[test] 647 #[test]
@@ -642,8 +655,7 @@ mod tests {
642 <|> 655 <|>
643 "#; 656 "#;
644 // crate::S would be shorter, but using private imports seems wrong 657 // crate::S would be shorter, but using private imports seems wrong
645 check_found_path(code, "crate::bar::S"); 658 check_found_path(code, "crate::bar::S", "crate::bar::S", "crate::bar::S", "crate::bar::S");
646 check_found_path_abs(code, "crate::bar::S");
647 } 659 }
648 660
649 #[test] 661 #[test]
@@ -661,8 +673,7 @@ mod tests {
661 //- /baz.rs 673 //- /baz.rs
662 pub use super::foo; 674 pub use super::foo;
663 "#; 675 "#;
664 check_found_path(code, "crate::foo::S"); 676 check_found_path(code, "crate::foo::S", "crate::foo::S", "crate::foo::S", "crate::foo::S");
665 check_found_path_abs(code, "crate::foo::S");
666 } 677 }
667 678
668 #[test] 679 #[test]
@@ -682,8 +693,13 @@ mod tests {
682 pub struct Arc; 693 pub struct Arc;
683 } 694 }
684 "#; 695 "#;
685 check_found_path(code, "std::sync::Arc"); 696 check_found_path(
686 check_found_path_abs(code, "std::sync::Arc"); 697 code,
698 "std::sync::Arc",
699 "std::sync::Arc",
700 "std::sync::Arc",
701 "std::sync::Arc",
702 );
687 } 703 }
688 704
689 #[test] 705 #[test]
@@ -707,8 +723,13 @@ mod tests {
707 pub struct Error; 723 pub struct Error;
708 } 724 }
709 "#; 725 "#;
710 check_found_path(code, "core::fmt::Error"); 726 check_found_path(
711 check_found_path_abs(code, "core::fmt::Error"); 727 code,
728 "core::fmt::Error",
729 "core::fmt::Error",
730 "core::fmt::Error",
731 "core::fmt::Error",
732 );
712 } 733 }
713 734
714 #[test] 735 #[test]
@@ -731,8 +752,13 @@ mod tests {
731 pub struct Arc; 752 pub struct Arc;
732 } 753 }
733 "#; 754 "#;
734 check_found_path(code, "alloc::sync::Arc"); 755 check_found_path(
735 check_found_path_abs(code, "alloc::sync::Arc"); 756 code,
757 "alloc::sync::Arc",
758 "alloc::sync::Arc",
759 "alloc::sync::Arc",
760 "alloc::sync::Arc",
761 );
736 } 762 }
737 763
738 #[test] 764 #[test]
@@ -749,8 +775,13 @@ mod tests {
749 //- /zzz.rs crate:megaalloc 775 //- /zzz.rs crate:megaalloc
750 pub struct Arc; 776 pub struct Arc;
751 "#; 777 "#;
752 check_found_path(code, "megaalloc::Arc"); 778 check_found_path(
753 check_found_path_abs(code, "megaalloc::Arc"); 779 code,
780 "megaalloc::Arc",
781 "megaalloc::Arc",
782 "megaalloc::Arc",
783 "megaalloc::Arc",
784 );
754 } 785 }
755 786
756 #[test] 787 #[test]
@@ -763,9 +794,7 @@ mod tests {
763 pub use u8; 794 pub use u8;
764 } 795 }
765 "#; 796 "#;
766 check_found_path(code, "u8"); 797 check_found_path(code, "u8", "u8", "u8", "u8");
767 check_found_path(code, "u16"); 798 check_found_path(code, "u16", "u16", "u16", "u16");
768 check_found_path_abs(code, "u8");
769 check_found_path_abs(code, "u16");
770 } 799 }
771} 800}
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index a442fb63a..44bfe1593 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -334,14 +334,14 @@ mod tests {
334 334
335 use super::*; 335 use super::*;
336 336
337 fn check_search(ra_fixture: &str, krate_name: &str, query: Query, expect: Expect) { 337 fn check_search(ra_fixture: &str, crate_name: &str, query: Query, expect: Expect) {
338 let db = TestDB::with_files(ra_fixture); 338 let db = TestDB::with_files(ra_fixture);
339 let crate_graph = db.crate_graph(); 339 let crate_graph = db.crate_graph();
340 let krate = crate_graph 340 let krate = crate_graph
341 .iter() 341 .iter()
342 .find(|krate| { 342 .find(|krate| {
343 crate_graph[*krate].display_name.as_ref().map(|n| n.to_string()) 343 crate_graph[*krate].declaration_name.as_ref().map(|n| n.to_string())
344 == Some(krate_name.to_string()) 344 == Some(crate_name.to_string())
345 }) 345 })
346 .unwrap(); 346 .unwrap();
347 347
@@ -359,7 +359,7 @@ mod tests {
359 let path = map.path_of(item).unwrap(); 359 let path = map.path_of(item).unwrap();
360 format!( 360 format!(
361 "{}::{} ({})\n", 361 "{}::{} ({})\n",
362 crate_graph[krate].display_name.as_ref().unwrap(), 362 crate_graph[krate].declaration_name.as_ref().unwrap(),
363 path, 363 path,
364 mark 364 mark
365 ) 365 )
@@ -400,7 +400,7 @@ mod tests {
400 .iter() 400 .iter()
401 .filter_map(|krate| { 401 .filter_map(|krate| {
402 let cdata = &crate_graph[krate]; 402 let cdata = &crate_graph[krate];
403 let name = cdata.display_name.as_ref()?; 403 let name = cdata.declaration_name.as_ref()?;
404 404
405 let map = db.import_map(krate); 405 let map = db.import_map(krate);
406 406
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 5e4d73c1f..464ffef21 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -173,7 +173,7 @@ impl CrateDefMap {
173 pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 173 pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
174 let _p = profile::span("crate_def_map_query").detail(|| { 174 let _p = profile::span("crate_def_map_query").detail(|| {
175 db.crate_graph()[krate] 175 db.crate_graph()[krate]
176 .display_name 176 .declaration_name
177 .as_ref() 177 .as_ref()
178 .map(ToString::to_string) 178 .map(ToString::to_string)
179 .unwrap_or_default() 179 .unwrap_or_default()
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 100e25ffc..c8cd04264 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -1229,9 +1229,10 @@ impl ModCollector<'_, '_> {
1229 } else { 1229 } else {
1230 let derive = attrs.by_key("proc_macro_derive"); 1230 let derive = attrs.by_key("proc_macro_derive");
1231 if let Some(arg) = derive.tt_values().next() { 1231 if let Some(arg) = derive.tt_values().next() {
1232 if let [TokenTree::Leaf(Leaf::Ident(trait_name))] = &*arg.token_trees { 1232 if let [TokenTree::Leaf(Leaf::Ident(trait_name)), ..] = &*arg.token_trees {
1233 trait_name.as_name() 1233 trait_name.as_name()
1234 } else { 1234 } else {
1235 log::trace!("malformed `#[proc_macro_derive]`: {}", arg);
1235 return; 1236 return;
1236 } 1237 }
1237 } else { 1238 } else {
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 0851c3b7d..305fca0f9 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -688,13 +688,20 @@ fn resolves_proc_macros() {
688 pub fn derive_macro(_item: TokenStream) -> TokenStream { 688 pub fn derive_macro(_item: TokenStream) -> TokenStream {
689 TokenStream 689 TokenStream
690 } 690 }
691
692 #[proc_macro_derive(AnotherTrait, attributes(helper_attr))]
693 pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
694 TokenStream
695 }
691 ", 696 ",
692 expect![[r#" 697 expect![[r#"
693 crate 698 crate
699 AnotherTrait: m
694 DummyTrait: m 700 DummyTrait: m
695 TokenStream: t v 701 TokenStream: t v
696 attribute_macro: v m 702 attribute_macro: v m
697 derive_macro: v 703 derive_macro: v
704 derive_macro_2: v
698 function_like_macro: v m 705 function_like_macro: v m
699 "#]], 706 "#]],
700 ); 707 );
diff --git a/crates/ide/src/completion/complete_postfix/format_like.rs b/crates/ide/src/completion/complete_postfix/format_like.rs
index 0287fc803..81c33bf3a 100644
--- a/crates/ide/src/completion/complete_postfix/format_like.rs
+++ b/crates/ide/src/completion/complete_postfix/format_like.rs
@@ -1,4 +1,4 @@
1// Feature: Postfix completion for `format`-like strings. 1// Feature: Format String Completion.
2// 2//
3// `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`. 3// `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`.
4// 4//
diff --git a/crates/ide/src/completion/complete_unqualified_path.rs b/crates/ide/src/completion/complete_unqualified_path.rs
index 2010d9a2f..8b6757195 100644
--- a/crates/ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ide/src/completion/complete_unqualified_path.rs
@@ -267,6 +267,26 @@ fn quux() { <|> }
267 ); 267 );
268 } 268 }
269 269
270 /// Regression test for issue #6091.
271 #[test]
272 fn correctly_completes_module_items_prefixed_with_underscore() {
273 check_edit(
274 "_alpha",
275 r#"
276fn main() {
277 _<|>
278}
279fn _alpha() {}
280"#,
281 r#"
282fn main() {
283 _alpha()$0
284}
285fn _alpha() {}
286"#,
287 )
288 }
289
270 #[test] 290 #[test]
271 fn completes_extern_prelude() { 291 fn completes_extern_prelude() {
272 check( 292 check(
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs
index 101be8eb5..8dea8a4bf 100644
--- a/crates/ide/src/completion/completion_context.rs
+++ b/crates/ide/src/completion/completion_context.rs
@@ -221,10 +221,11 @@ impl<'a> CompletionContext<'a> {
221 Some(ctx) 221 Some(ctx)
222 } 222 }
223 223
224 // The range of the identifier that is being completed. 224 /// The range of the identifier that is being completed.
225 pub(crate) fn source_range(&self) -> TextRange { 225 pub(crate) fn source_range(&self) -> TextRange {
226 // check kind of macro-expanded token, but use range of original token 226 // check kind of macro-expanded token, but use range of original token
227 if self.token.kind() == IDENT || self.token.kind().is_keyword() { 227 let kind = self.token.kind();
228 if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() {
228 mark::hit!(completes_if_prefix_is_keyword); 229 mark::hit!(completes_if_prefix_is_keyword);
229 self.original_token.text_range() 230 self.original_token.text_range()
230 } else { 231 } else {
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 9cf02f0a3..4521d72cc 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -289,7 +289,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
289 289
290fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String { 290fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String {
291 let crate_name = 291 let crate_name =
292 db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string); 292 db.crate_graph()[module.krate().into()].declaration_name.as_ref().map(ToString::to_string);
293 let module_path = module 293 let module_path = module
294 .path_to_root(db) 294 .path_to_root(db)
295 .into_iter() 295 .into_iter()
@@ -3163,4 +3163,34 @@ fn main() { let s<|>t = test().get(); }
3163 "#]], 3163 "#]],
3164 ); 3164 );
3165 } 3165 }
3166
3167 #[test]
3168 fn hover_displays_normalized_crate_names() {
3169 check(
3170 r#"
3171//- /lib.rs crate:name-with-dashes
3172pub mod wrapper {
3173 pub struct Thing { x: u32 }
3174
3175 impl Thing {
3176 pub fn new() -> Thing { Thing { x: 0 } }
3177 }
3178}
3179
3180//- /main.rs crate:main deps:name-with-dashes
3181fn main() { let foo_test = name_with_dashes::wrapper::Thing::new<|>(); }
3182"#,
3183 expect![[r#"
3184 *new*
3185
3186 ```rust
3187 name_with_dashes::wrapper::Thing
3188 ```
3189
3190 ```rust
3191 pub fn new() -> Thing
3192 ```
3193 "#]],
3194 )
3195 }
3166} 3196}
diff --git a/crates/ide/src/link_rewrite.rs b/crates/ide/src/link_rewrite.rs
index a16f90e17..c317a2379 100644
--- a/crates/ide/src/link_rewrite.rs
+++ b/crates/ide/src/link_rewrite.rs
@@ -107,7 +107,7 @@ fn rewrite_intra_doc_link(
107 let krate = resolved.module(db)?.krate(); 107 let krate = resolved.module(db)?.krate();
108 let canonical_path = resolved.canonical_path(db)?; 108 let canonical_path = resolved.canonical_path(db)?;
109 let new_target = get_doc_url(db, &krate)? 109 let new_target = get_doc_url(db, &krate)?
110 .join(&format!("{}/", krate.display_name(db)?)) 110 .join(&format!("{}/", krate.declaration_name(db)?))
111 .ok()? 111 .ok()?
112 .join(&canonical_path.replace("::", "/")) 112 .join(&canonical_path.replace("::", "/"))
113 .ok()? 113 .ok()?
@@ -127,7 +127,7 @@ fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<S
127 let module = def.module(db)?; 127 let module = def.module(db)?;
128 let krate = module.krate(); 128 let krate = module.krate();
129 let canonical_path = def.canonical_path(db)?; 129 let canonical_path = def.canonical_path(db)?;
130 let base = format!("{}/{}", krate.display_name(db)?, canonical_path.replace("::", "/")); 130 let base = format!("{}/{}", krate.declaration_name(db)?, canonical_path.replace("::", "/"));
131 131
132 get_doc_url(db, &krate) 132 get_doc_url(db, &krate)
133 .and_then(|url| url.join(&base).ok()) 133 .and_then(|url| url.join(&base).ok())
@@ -248,7 +248,7 @@ fn get_doc_url(db: &RootDatabase, krate: &Crate) -> Option<Url> {
248 // 248 //
249 // FIXME: clicking on the link should just open the file in the editor, 249 // FIXME: clicking on the link should just open the file in the editor,
250 // instead of falling back to external urls. 250 // instead of falling back to external urls.
251 Some(format!("https://docs.rs/{}/*/", krate.display_name(db)?)) 251 Some(format!("https://docs.rs/{}/*/", krate.declaration_name(db)?))
252 }) 252 })
253 .and_then(|s| Url::parse(&s).ok()) 253 .and_then(|s| Url::parse(&s).ok())
254} 254}
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs
index 0af84daa0..f67f10491 100644
--- a/crates/ide/src/status.rs
+++ b/crates/ide/src/status.rs
@@ -45,7 +45,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
45 match krate { 45 match krate {
46 Some(krate) => { 46 Some(krate) => {
47 let crate_graph = db.crate_graph(); 47 let crate_graph = db.crate_graph();
48 let display_crate = |krate: CrateId| match &crate_graph[krate].display_name { 48 let display_crate = |krate: CrateId| match &crate_graph[krate].declaration_name {
49 Some(it) => format!("{}({:?})", it, krate), 49 Some(it) => format!("{}({:?})", it, krate),
50 None => format!("{:?}", krate), 50 None => format!("{:?}", krate),
51 }; 51 };
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index 258f60e28..d1e7602fc 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -411,7 +411,7 @@ impl ProjectWorkspace {
411 let crate_id = crate_graph.add_crate_root( 411 let crate_id = crate_graph.add_crate_root(
412 file_id, 412 file_id,
413 edition, 413 edition,
414 Some(cargo[pkg].name.clone()), 414 Some(CrateName::normalize_dashes(&cargo[pkg].name)),
415 cfg_options, 415 cfg_options,
416 env, 416 env,
417 proc_macro.clone(), 417 proc_macro.clone(),
@@ -546,7 +546,8 @@ fn sysroot_to_crate_graph(
546 546
547 let env = Env::default(); 547 let env = Env::default();
548 let proc_macro = vec![]; 548 let proc_macro = vec![];
549 let name = sysroot[krate].name.clone(); 549 let name = CrateName::new(&sysroot[krate].name)
550 .expect("Sysroot crates' names do not contain dashes");
550 let crate_id = crate_graph.add_crate_root( 551 let crate_id = crate_graph.add_crate_root(
551 file_id, 552 file_id,
552 Edition::Edition2018, 553 Edition::Edition2018,
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index f3b6c900e..d1d3b12f8 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -36,11 +36,12 @@ pub fn diagnostics(path: &Path, load_output_dirs: bool, with_proc_macro: bool) -
36 for module in work { 36 for module in work {
37 let file_id = module.definition_source(db).file_id.original_file(db); 37 let file_id = module.definition_source(db).file_id.original_file(db);
38 if !visited_files.contains(&file_id) { 38 if !visited_files.contains(&file_id) {
39 let crate_name = if let Some(name) = module.krate().display_name(db) { 39 let crate_name = module
40 format!("{}", name) 40 .krate()
41 } else { 41 .declaration_name(db)
42 String::from("unknown") 42 .as_ref()
43 }; 43 .map(ToString::to_string)
44 .unwrap_or_else(|| "unknown".to_string());
44 println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id)); 45 println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id));
45 for diagnostic in analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap() 46 for diagnostic in analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap()
46 { 47 {
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 0ab4c37bf..dcbc11c14 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -10,6 +10,7 @@
10use std::{ffi::OsString, path::PathBuf}; 10use std::{ffi::OsString, path::PathBuf};
11 11
12use flycheck::FlycheckConfig; 12use flycheck::FlycheckConfig;
13use hir::PrefixKind;
13use ide::{ 14use ide::{
14 AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, 15 AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig,
15 MergeBehaviour, 16 MergeBehaviour,
@@ -289,6 +290,11 @@ impl Config {
289 MergeBehaviourDef::Full => Some(MergeBehaviour::Full), 290 MergeBehaviourDef::Full => Some(MergeBehaviour::Full),
290 MergeBehaviourDef::Last => Some(MergeBehaviour::Last), 291 MergeBehaviourDef::Last => Some(MergeBehaviour::Last),
291 }; 292 };
293 self.assist.insert_use.prefix_kind = match data.assist_importPrefix {
294 ImportPrefixDef::Plain => PrefixKind::Plain,
295 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
296 ImportPrefixDef::BySelf => PrefixKind::BySelf,
297 };
292 298
293 self.call_info_full = data.callInfo_full; 299 self.call_info_full = data.callInfo_full;
294 300
@@ -403,13 +409,21 @@ enum ManifestOrProjectJson {
403} 409}
404 410
405#[derive(Deserialize)] 411#[derive(Deserialize)]
406#[serde(rename_all = "lowercase")] 412#[serde(rename_all = "snake_case")]
407enum MergeBehaviourDef { 413enum MergeBehaviourDef {
408 None, 414 None,
409 Full, 415 Full,
410 Last, 416 Last,
411} 417}
412 418
419#[derive(Deserialize)]
420#[serde(rename_all = "snake_case")]
421enum ImportPrefixDef {
422 Plain,
423 BySelf,
424 ByCrate,
425}
426
413macro_rules! config_data { 427macro_rules! config_data {
414 (struct $name:ident { $($field:ident: $ty:ty = $default:expr,)*}) => { 428 (struct $name:ident { $($field:ident: $ty:ty = $default:expr,)*}) => {
415 #[allow(non_snake_case)] 429 #[allow(non_snake_case)]
@@ -434,6 +448,7 @@ macro_rules! config_data {
434config_data! { 448config_data! {
435 struct ConfigData { 449 struct ConfigData {
436 assist_importMergeBehaviour: MergeBehaviourDef = MergeBehaviourDef::None, 450 assist_importMergeBehaviour: MergeBehaviourDef = MergeBehaviourDef::None,
451 assist_importPrefix: ImportPrefixDef = ImportPrefixDef::Plain,
437 452
438 callInfo_full: bool = true, 453 callInfo_full: bool = true,
439 454
diff --git a/editors/code/package.json b/editors/code/package.json
index cc2ac3bd2..1f0e7550b 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -652,6 +652,21 @@
652 "default": "full", 652 "default": "full",
653 "description": "The strategy to use when inserting new imports or merging imports." 653 "description": "The strategy to use when inserting new imports or merging imports."
654 }, 654 },
655 "rust-analyzer.assist.importPrefix": {
656 "type": "string",
657 "enum": [
658 "plain",
659 "by_self",
660 "by_crate"
661 ],
662 "enumDescriptions": [
663 "Insert import paths relative to the current module, using up to one `super` prefix if the parent module contains the requested item.",
664 "Prefix all import paths with `self` if they don't begin with `self`, `super`, `crate` or a crate name",
665 "Force import paths to be absolute by always starting them with `crate` or the crate name they refer to."
666 ],
667 "default": "plain",
668 "description": "The path structure for newly inserted paths to use."
669 },
655 "rust-analyzer.runnables.overrideCargo": { 670 "rust-analyzer.runnables.overrideCargo": {
656 "type": [ 671 "type": [
657 "null", 672 "null",
@@ -1033,4 +1048,4 @@
1033 ] 1048 ]
1034 } 1049 }
1035 } 1050 }
1036} 1051} \ No newline at end of file
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs
index 3f0013e82..341e67c73 100644
--- a/xtask/src/codegen/gen_feature_docs.rs
+++ b/xtask/src/codegen/gen_feature_docs.rs
@@ -38,7 +38,9 @@ impl Feature {
38 38
39 for block in comment_blocks { 39 for block in comment_blocks {
40 let id = block.id; 40 let id = block.id;
41 assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id); 41 if let Err(msg) = is_valid_feature_name(&id) {
42 panic!("invalid feature name: {:?}:\n {}", id, msg)
43 }
42 let doc = block.contents.join("\n"); 44 let doc = block.contents.join("\n");
43 let location = Location::new(path.clone(), block.line); 45 let location = Location::new(path.clone(), block.line);
44 acc.push(Feature { id, location, doc }) 46 acc.push(Feature { id, location, doc })
@@ -49,7 +51,7 @@ impl Feature {
49 } 51 }
50} 52}
51 53
52fn is_valid_feature_name(feature: &str) -> bool { 54fn is_valid_feature_name(feature: &str) -> Result<(), String> {
53 'word: for word in feature.split_whitespace() { 55 'word: for word in feature.split_whitespace() {
54 for &short in ["to", "and"].iter() { 56 for &short in ["to", "and"].iter() {
55 if word == short { 57 if word == short {
@@ -58,14 +60,14 @@ fn is_valid_feature_name(feature: &str) -> bool {
58 } 60 }
59 for &short in ["To", "And"].iter() { 61 for &short in ["To", "And"].iter() {
60 if word == short { 62 if word == short {
61 return false; 63 return Err(format!("Don't capitalize {:?}", word));
62 } 64 }
63 } 65 }
64 if !word.starts_with(char::is_uppercase) { 66 if !word.starts_with(char::is_uppercase) {
65 return false; 67 return Err(format!("Capitalize {:?}", word));
66 } 68 }
67 } 69 }
68 true 70 Ok(())
69} 71}
70 72
71impl fmt::Display for Feature { 73impl fmt::Display for Feature {
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 02b3afc96..01f04a17c 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -38,6 +38,13 @@ fn check_code_formatting() {
38} 38}
39 39
40#[test] 40#[test]
41fn smoke_test_docs_generation() {
42 // We don't commit docs to the repo, so we can just overwrite in tests.
43 codegen::generate_assists_docs(Mode::Overwrite).unwrap();
44 codegen::generate_feature_docs(Mode::Overwrite).unwrap();
45}
46
47#[test]
41fn rust_files_are_tidy() { 48fn rust_files_are_tidy() {
42 let mut tidy_docs = TidyDocs::default(); 49 let mut tidy_docs = TidyDocs::default();
43 for path in rust_files(&project_root().join("crates")) { 50 for path in rust_files(&project_root().join("crates")) {