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/ast_transform.rs17
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs25
-rw-r--r--crates/assists/src/handlers/auto_import.rs23
-rw-r--r--crates/assists/src/handlers/convert_integer_literal.rs701
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs8
-rw-r--r--crates/assists/src/handlers/fill_match_arms.rs4
-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/assists/src/utils.rs21
-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.rs8
-rw-r--r--crates/hir_def/src/find_path.rs249
-rw-r--r--crates/hir_def/src/import_map.rs46
-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/hir_def/src/path.rs22
-rw-r--r--crates/hir_ty/src/display.rs29
-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/inlay_hints.rs34
-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
37 files changed, 1211 insertions, 228 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/ast_transform.rs b/crates/assists/src/ast_transform.rs
index 4307e0191..ac72f3f02 100644
--- a/crates/assists/src/ast_transform.rs
+++ b/crates/assists/src/ast_transform.rs
@@ -1,13 +1,14 @@
1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. 1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined.
2use rustc_hash::FxHashMap;
3
4use hir::{HirDisplay, PathResolution, SemanticsScope}; 2use hir::{HirDisplay, PathResolution, SemanticsScope};
3use rustc_hash::FxHashMap;
5use syntax::{ 4use syntax::{
6 algo::SyntaxRewriter, 5 algo::SyntaxRewriter,
7 ast::{self, AstNode}, 6 ast::{self, AstNode},
8 SyntaxNode, 7 SyntaxNode,
9}; 8};
10 9
10use crate::utils::mod_path_to_ast;
11
11pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { 12pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
12 SyntaxRewriter::from_fn(|element| match element { 13 SyntaxRewriter::from_fn(|element| match element {
13 syntax::SyntaxElement::Node(n) => { 14 syntax::SyntaxElement::Node(n) => {
@@ -189,7 +190,7 @@ impl<'a> AstTransform<'a> for QualifyPaths<'a> {
189 match resolution { 190 match resolution {
190 PathResolution::Def(def) => { 191 PathResolution::Def(def) => {
191 let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?; 192 let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?;
192 let mut path = path_to_ast(found_path); 193 let mut path = mod_path_to_ast(&found_path);
193 194
194 let type_args = p 195 let type_args = p
195 .segment() 196 .segment()
@@ -210,13 +211,3 @@ impl<'a> AstTransform<'a> for QualifyPaths<'a> {
210 } 211 }
211 } 212 }
212} 213}
213
214pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path {
215 let parse = ast::SourceFile::parse(&path.to_string());
216 parse
217 .tree()
218 .syntax()
219 .descendants()
220 .find_map(ast::Path::cast)
221 .unwrap_or_else(|| panic!("failed to parse path {:?}, `{}`", path, path))
222}
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs
index 51b5a2eb0..4c400f287 100644
--- a/crates/assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/assists/src/handlers/add_missing_impl_members.rs
@@ -820,4 +820,29 @@ impl Tr for () {
820}"#, 820}"#,
821 ) 821 )
822 } 822 }
823
824 #[test]
825 fn weird_path() {
826 check_assist(
827 add_missing_impl_members,
828 r#"
829trait Test {
830 fn foo(&self, x: crate)
831}
832impl Test for () {
833 <|>
834}
835"#,
836 r#"
837trait Test {
838 fn foo(&self, x: crate)
839}
840impl Test for () {
841 fn foo(&self, x: crate) {
842 ${0:todo!()}
843 }
844}
845"#,
846 )
847 }
823} 848}
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index fa524ffd9..d3ee98e5f 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -13,7 +13,10 @@ use syntax::{
13 SyntaxNode, 13 SyntaxNode,
14}; 14};
15 15
16use crate::{utils::insert_use, AssistContext, AssistId, AssistKind, Assists, GroupLabel}; 16use crate::{
17 utils::insert_use, utils::mod_path_to_ast, AssistContext, AssistId, AssistKind, Assists,
18 GroupLabel,
19};
17 20
18// Assist: auto_import 21// Assist: auto_import
19// 22//
@@ -54,7 +57,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
54 range, 57 range,
55 |builder| { 58 |builder| {
56 let new_syntax = 59 let new_syntax =
57 insert_use(&scope, import.to_ast_path(), ctx.config.insert_use.merge); 60 insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use.merge);
58 builder.replace(syntax.text_range(), new_syntax.to_string()) 61 builder.replace(syntax.text_range(), new_syntax.to_string())
59 }, 62 },
60 ); 63 );
@@ -191,12 +194,16 @@ impl AutoImportAssets {
191 _ => Some(candidate), 194 _ => Some(candidate),
192 }) 195 })
193 .filter_map(|candidate| match candidate { 196 .filter_map(|candidate| match candidate {
194 Either::Left(module_def) => { 197 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) 198 db,
196 } 199 module_def,
197 Either::Right(macro_def) => { 200 ctx.config.insert_use.prefix_kind,
198 self.module_with_name_to_import.find_use_path_prefixed(db, macro_def) 201 ),
199 } 202 Either::Right(macro_def) => self.module_with_name_to_import.find_use_path_prefixed(
203 db,
204 macro_def,
205 ctx.config.insert_use.prefix_kind,
206 ),
200 }) 207 })
201 .filter(|use_path| !use_path.segments.is_empty()) 208 .filter(|use_path| !use_path.segments.is_empty())
202 .take(20) 209 .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/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index f5f03ef36..7f4f80b23 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -10,9 +10,10 @@ use syntax::{
10}; 10};
11 11
12use crate::{ 12use crate::{
13 assist_context::AssistBuilder, utils::insert_use, AssistContext, AssistId, AssistKind, Assists, 13 assist_context::AssistBuilder,
14 utils::{insert_use, mod_path_to_ast, ImportScope},
15 AssistContext, AssistId, AssistKind, Assists,
14}; 16};
15use insert_use::ImportScope;
16 17
17// Assist: extract_struct_from_enum_variant 18// Assist: extract_struct_from_enum_variant
18// 19//
@@ -111,7 +112,8 @@ fn insert_import(
111 let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; 112 let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?;
112 let syntax = scope.as_syntax_node(); 113 let syntax = scope.as_syntax_node();
113 114
114 let new_syntax = insert_use(&scope, mod_path.to_ast_path(), ctx.config.insert_use.merge); 115 let new_syntax =
116 insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge);
115 // FIXME: this will currently panic as multiple imports will have overlapping text ranges 117 // FIXME: this will currently panic as multiple imports will have overlapping text ranges
116 builder.replace(syntax.text_range(), new_syntax.to_string()) 118 builder.replace(syntax.text_range(), new_syntax.to_string())
117 } 119 }
diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs
index 3d9bdb2bf..676f5ad92 100644
--- a/crates/assists/src/handlers/fill_match_arms.rs
+++ b/crates/assists/src/handlers/fill_match_arms.rs
@@ -7,7 +7,7 @@ use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
7use test_utils::mark; 7use test_utils::mark;
8 8
9use crate::{ 9use crate::{
10 utils::{render_snippet, Cursor, FamousDefs}, 10 utils::{mod_path_to_ast, render_snippet, Cursor, FamousDefs},
11 AssistContext, AssistId, AssistKind, Assists, 11 AssistContext, AssistId, AssistKind, Assists,
12}; 12};
13 13
@@ -192,7 +192,7 @@ fn resolve_tuple_of_enum_def(
192} 192}
193 193
194fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { 194fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
195 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, ModuleDef::from(var))?); 195 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
196 196
197 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 197 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
198 let pat: ast::Pat = match var.source(db).value.kind() { 198 let pat: ast::Pat = match var.source(db).value.kind() {
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/assists/src/utils.rs b/crates/assists/src/utils.rs
index b0511ceb6..eb69c49a4 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -19,6 +19,27 @@ use crate::assist_config::SnippetCap;
19pub use insert_use::MergeBehaviour; 19pub use insert_use::MergeBehaviour;
20pub(crate) use insert_use::{insert_use, ImportScope}; 20pub(crate) use insert_use::{insert_use, ImportScope};
21 21
22pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
23 let mut segments = Vec::new();
24 let mut is_abs = false;
25 match path.kind {
26 hir::PathKind::Plain => {}
27 hir::PathKind::Super(0) => segments.push(make::path_segment_self()),
28 hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())),
29 hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => {
30 segments.push(make::path_segment_crate())
31 }
32 hir::PathKind::Abs => is_abs = true,
33 }
34
35 segments.extend(
36 path.segments
37 .iter()
38 .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))),
39 );
40 make::path_from_segments(segments, is_abs)
41}
42
22pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { 43pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
23 extract_trivial_expression(&block) 44 extract_trivial_expression(&block)
24 .filter(|expr| !expr.syntax().text().contains_char('\n')) 45 .filter(|expr| !expr.syntax().text().contains_char('\n'))
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..171118d98 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -48,9 +48,10 @@ 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, PathKind},
54 type_ref::{Mutability, TypeRef}, 55 type_ref::{Mutability, TypeRef},
55}; 56};
56pub use hir_expand::{ 57pub use hir_expand::{
@@ -62,7 +63,4 @@ pub use hir_ty::display::HirDisplay;
62// These are negative re-exports: pub using these names is forbidden, they 63// These are negative re-exports: pub using these names is forbidden, they
63// should remain private to hir internals. 64// should remain private to hir internals.
64#[allow(unused)] 65#[allow(unused)]
65use { 66use {hir_def::path::Path, hir_expand::hygiene::Hygiene};
66 hir_def::path::{Path, PathKind},
67 hir_expand::hygiene::Hygiene,
68};
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index baf374144..02613c4c4 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
@@ -211,6 +222,7 @@ fn find_path_inner(
211 best_path_len - 1, 222 best_path_len - 1,
212 prefixed, 223 prefixed,
213 )?; 224 )?;
225 mark::hit!(partially_imported);
214 path.segments.push(info.path.segments.last().unwrap().clone()); 226 path.segments.push(info.path.segments.last().unwrap().clone());
215 Some(path) 227 Some(path)
216 }) 228 })
@@ -226,7 +238,7 @@ fn find_path_inner(
226 } 238 }
227 } 239 }
228 240
229 if let Some(prefix) = prefixed.prefix() { 241 if let Some(prefix) = prefixed.map(PrefixKind::prefix) {
230 best_path.or_else(|| { 242 best_path.or_else(|| {
231 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name])) 243 scope_name.map(|scope_name| ModPath::from_segments(prefix, vec![scope_name]))
232 }) 244 })
@@ -355,7 +367,7 @@ mod tests {
355 /// `code` needs to contain a cursor marker; checks that `find_path` for the 367 /// `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 368 /// item the `path` refers to returns that same path when called from the
357 /// module the cursor is in. 369 /// module the cursor is in.
358 fn check_found_path_(ra_fixture: &str, path: &str, absolute: bool) { 370 fn check_found_path_(ra_fixture: &str, path: &str, prefix_kind: Option<PrefixKind>) {
359 let (db, pos) = TestDB::with_position(ra_fixture); 371 let (db, pos) = TestDB::with_position(ra_fixture);
360 let module = db.module_for_file(pos.file_id); 372 let module = db.module_for_file(pos.file_id);
361 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path)); 373 let parsed_path_file = syntax::SourceFile::parse(&format!("use {};", path));
@@ -375,20 +387,22 @@ mod tests {
375 .take_types() 387 .take_types()
376 .unwrap(); 388 .unwrap();
377 389
378 let found_path = if absolute { find_path_prefixed } else { find_path }( 390 let found_path =
379 &db, 391 find_path_inner(&db, ItemInNs::Types(resolved), module, MAX_PATH_LEN, prefix_kind);
380 ItemInNs::Types(resolved), 392 assert_eq!(found_path, Some(mod_path), "{:?}", prefix_kind);
381 module,
382 );
383 assert_eq!(found_path, Some(mod_path), "absolute {}", absolute);
384 } 393 }
385 394
386 fn check_found_path(ra_fixture: &str, path: &str) { 395 fn check_found_path(
387 check_found_path_(ra_fixture, path, false); 396 ra_fixture: &str,
388 } 397 unprefixed: &str,
389 398 prefixed: &str,
390 fn check_found_path_abs(ra_fixture: &str, path: &str) { 399 absolute: &str,
391 check_found_path_(ra_fixture, path, true); 400 self_prefixed: &str,
401 ) {
402 check_found_path_(ra_fixture, unprefixed, None);
403 check_found_path_(ra_fixture, prefixed, Some(PrefixKind::Plain));
404 check_found_path_(ra_fixture, absolute, Some(PrefixKind::ByCrate));
405 check_found_path_(ra_fixture, self_prefixed, Some(PrefixKind::BySelf));
392 } 406 }
393 407
394 #[test] 408 #[test]
@@ -398,8 +412,7 @@ mod tests {
398 struct S; 412 struct S;
399 <|> 413 <|>
400 "#; 414 "#;
401 check_found_path(code, "S"); 415 check_found_path(code, "S", "S", "crate::S", "self::S");
402 check_found_path_abs(code, "S");
403 } 416 }
404 417
405 #[test] 418 #[test]
@@ -409,8 +422,7 @@ mod tests {
409 enum E { A } 422 enum E { A }
410 <|> 423 <|>
411 "#; 424 "#;
412 check_found_path(code, "E::A"); 425 check_found_path(code, "E::A", "E::A", "E::A", "E::A");
413 check_found_path_abs(code, "E::A");
414 } 426 }
415 427
416 #[test] 428 #[test]
@@ -422,8 +434,7 @@ mod tests {
422 } 434 }
423 <|> 435 <|>
424 "#; 436 "#;
425 check_found_path(code, "foo::S"); 437 check_found_path(code, "foo::S", "foo::S", "crate::foo::S", "self::foo::S");
426 check_found_path_abs(code, "foo::S");
427 } 438 }
428 439
429 #[test] 440 #[test]
@@ -437,8 +448,7 @@ mod tests {
437 //- /foo/bar.rs 448 //- /foo/bar.rs
438 <|> 449 <|>
439 "#; 450 "#;
440 check_found_path(code, "super::S"); 451 check_found_path(code, "super::S", "super::S", "crate::foo::S", "super::S");
441 check_found_path_abs(code, "super::S");
442 } 452 }
443 453
444 #[test] 454 #[test]
@@ -449,8 +459,7 @@ mod tests {
449 //- /foo.rs 459 //- /foo.rs
450 <|> 460 <|>
451 "#; 461 "#;
452 check_found_path(code, "self"); 462 check_found_path(code, "self", "self", "crate::foo", "self");
453 check_found_path_abs(code, "self");
454 } 463 }
455 464
456 #[test] 465 #[test]
@@ -461,8 +470,7 @@ mod tests {
461 //- /foo.rs 470 //- /foo.rs
462 <|> 471 <|>
463 "#; 472 "#;
464 check_found_path(code, "crate"); 473 check_found_path(code, "crate", "crate", "crate", "crate");
465 check_found_path_abs(code, "crate");
466 } 474 }
467 475
468 #[test] 476 #[test]
@@ -474,8 +482,7 @@ mod tests {
474 //- /foo.rs 482 //- /foo.rs
475 <|> 483 <|>
476 "#; 484 "#;
477 check_found_path(code, "crate::S"); 485 check_found_path(code, "crate::S", "crate::S", "crate::S", "crate::S");
478 check_found_path_abs(code, "crate::S");
479 } 486 }
480 487
481 #[test] 488 #[test]
@@ -486,8 +493,7 @@ mod tests {
486 //- /std.rs crate:std 493 //- /std.rs crate:std
487 pub struct S; 494 pub struct S;
488 "#; 495 "#;
489 check_found_path(code, "std::S"); 496 check_found_path(code, "std::S", "std::S", "std::S", "std::S");
490 check_found_path_abs(code, "std::S");
491 } 497 }
492 498
493 #[test] 499 #[test]
@@ -499,12 +505,18 @@ mod tests {
499 //- /std.rs crate:std 505 //- /std.rs crate:std
500 pub struct S; 506 pub struct S;
501 "#; 507 "#;
502 check_found_path(code, "std_renamed::S"); 508 check_found_path(
503 check_found_path_abs(code, "std_renamed::S"); 509 code,
510 "std_renamed::S",
511 "std_renamed::S",
512 "std_renamed::S",
513 "std_renamed::S",
514 );
504 } 515 }
505 516
506 #[test] 517 #[test]
507 fn partially_imported() { 518 fn partially_imported() {
519 mark::check!(partially_imported);
508 // Tests that short paths are used even for external items, when parts of the path are 520 // Tests that short paths are used even for external items, when parts of the path are
509 // already in scope. 521 // already in scope.
510 let code = r#" 522 let code = r#"
@@ -520,8 +532,13 @@ mod tests {
520 } 532 }
521 } 533 }
522 "#; 534 "#;
523 check_found_path(code, "ast::ModuleItem"); 535 check_found_path(
524 check_found_path_abs(code, "syntax::ast::ModuleItem"); 536 code,
537 "ast::ModuleItem",
538 "syntax::ast::ModuleItem",
539 "syntax::ast::ModuleItem",
540 "syntax::ast::ModuleItem",
541 );
525 542
526 let code = r#" 543 let code = r#"
527 //- /main.rs crate:main deps:syntax 544 //- /main.rs crate:main deps:syntax
@@ -535,8 +552,13 @@ mod tests {
535 } 552 }
536 } 553 }
537 "#; 554 "#;
538 check_found_path(code, "syntax::ast::ModuleItem"); 555 check_found_path(
539 check_found_path_abs(code, "syntax::ast::ModuleItem"); 556 code,
557 "syntax::ast::ModuleItem",
558 "syntax::ast::ModuleItem",
559 "syntax::ast::ModuleItem",
560 "syntax::ast::ModuleItem",
561 );
540 } 562 }
541 563
542 #[test] 564 #[test]
@@ -549,8 +571,7 @@ mod tests {
549 } 571 }
550 <|> 572 <|>
551 "#; 573 "#;
552 check_found_path(code, "bar::S"); 574 check_found_path(code, "bar::S", "bar::S", "crate::bar::S", "self::bar::S");
553 check_found_path_abs(code, "bar::S");
554 } 575 }
555 576
556 #[test] 577 #[test]
@@ -563,8 +584,7 @@ mod tests {
563 } 584 }
564 <|> 585 <|>
565 "#; 586 "#;
566 check_found_path(code, "bar::U"); 587 check_found_path(code, "bar::U", "bar::U", "crate::bar::U", "self::bar::U");
567 check_found_path_abs(code, "bar::U");
568 } 588 }
569 589
570 #[test] 590 #[test]
@@ -577,8 +597,7 @@ mod tests {
577 //- /core.rs crate:core 597 //- /core.rs crate:core
578 pub struct S; 598 pub struct S;
579 "#; 599 "#;
580 check_found_path(code, "std::S"); 600 check_found_path(code, "std::S", "std::S", "std::S", "std::S");
581 check_found_path_abs(code, "std::S");
582 } 601 }
583 602
584 #[test] 603 #[test]
@@ -591,8 +610,7 @@ mod tests {
591 #[prelude_import] 610 #[prelude_import]
592 pub use prelude::*; 611 pub use prelude::*;
593 "#; 612 "#;
594 check_found_path(code, "S"); 613 check_found_path(code, "S", "S", "S", "S");
595 check_found_path_abs(code, "S");
596 } 614 }
597 615
598 #[test] 616 #[test]
@@ -608,10 +626,8 @@ mod tests {
608 #[prelude_import] 626 #[prelude_import]
609 pub use prelude::*; 627 pub use prelude::*;
610 "#; 628 "#;
611 check_found_path(code, "None"); 629 check_found_path(code, "None", "None", "None", "None");
612 check_found_path(code, "Some"); 630 check_found_path(code, "Some", "Some", "Some", "Some");
613 check_found_path_abs(code, "None");
614 check_found_path_abs(code, "Some");
615 } 631 }
616 632
617 #[test] 633 #[test]
@@ -627,8 +643,7 @@ mod tests {
627 //- /baz.rs 643 //- /baz.rs
628 pub use crate::foo::bar::S; 644 pub use crate::foo::bar::S;
629 "#; 645 "#;
630 check_found_path(code, "baz::S"); 646 check_found_path(code, "baz::S", "baz::S", "crate::baz::S", "self::baz::S");
631 check_found_path_abs(code, "baz::S");
632 } 647 }
633 648
634 #[test] 649 #[test]
@@ -642,8 +657,7 @@ mod tests {
642 <|> 657 <|>
643 "#; 658 "#;
644 // crate::S would be shorter, but using private imports seems wrong 659 // crate::S would be shorter, but using private imports seems wrong
645 check_found_path(code, "crate::bar::S"); 660 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 } 661 }
648 662
649 #[test] 663 #[test]
@@ -661,8 +675,7 @@ mod tests {
661 //- /baz.rs 675 //- /baz.rs
662 pub use super::foo; 676 pub use super::foo;
663 "#; 677 "#;
664 check_found_path(code, "crate::foo::S"); 678 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 } 679 }
667 680
668 #[test] 681 #[test]
@@ -682,8 +695,13 @@ mod tests {
682 pub struct Arc; 695 pub struct Arc;
683 } 696 }
684 "#; 697 "#;
685 check_found_path(code, "std::sync::Arc"); 698 check_found_path(
686 check_found_path_abs(code, "std::sync::Arc"); 699 code,
700 "std::sync::Arc",
701 "std::sync::Arc",
702 "std::sync::Arc",
703 "std::sync::Arc",
704 );
687 } 705 }
688 706
689 #[test] 707 #[test]
@@ -707,8 +725,13 @@ mod tests {
707 pub struct Error; 725 pub struct Error;
708 } 726 }
709 "#; 727 "#;
710 check_found_path(code, "core::fmt::Error"); 728 check_found_path(
711 check_found_path_abs(code, "core::fmt::Error"); 729 code,
730 "core::fmt::Error",
731 "core::fmt::Error",
732 "core::fmt::Error",
733 "core::fmt::Error",
734 );
712 } 735 }
713 736
714 #[test] 737 #[test]
@@ -731,8 +754,13 @@ mod tests {
731 pub struct Arc; 754 pub struct Arc;
732 } 755 }
733 "#; 756 "#;
734 check_found_path(code, "alloc::sync::Arc"); 757 check_found_path(
735 check_found_path_abs(code, "alloc::sync::Arc"); 758 code,
759 "alloc::sync::Arc",
760 "alloc::sync::Arc",
761 "alloc::sync::Arc",
762 "alloc::sync::Arc",
763 );
736 } 764 }
737 765
738 #[test] 766 #[test]
@@ -749,8 +777,13 @@ mod tests {
749 //- /zzz.rs crate:megaalloc 777 //- /zzz.rs crate:megaalloc
750 pub struct Arc; 778 pub struct Arc;
751 "#; 779 "#;
752 check_found_path(code, "megaalloc::Arc"); 780 check_found_path(
753 check_found_path_abs(code, "megaalloc::Arc"); 781 code,
782 "megaalloc::Arc",
783 "megaalloc::Arc",
784 "megaalloc::Arc",
785 "megaalloc::Arc",
786 );
754 } 787 }
755 788
756 #[test] 789 #[test]
@@ -763,9 +796,7 @@ mod tests {
763 pub use u8; 796 pub use u8;
764 } 797 }
765 "#; 798 "#;
766 check_found_path(code, "u8"); 799 check_found_path(code, "u8", "u8", "u8", "u8");
767 check_found_path(code, "u16"); 800 check_found_path(code, "u16", "u16", "u16", "u16");
768 check_found_path_abs(code, "u8");
769 check_found_path_abs(code, "u16");
770 } 801 }
771} 802}
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index a442fb63a..028cae2e7 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -4,17 +4,16 @@ use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
4 4
5use base_db::CrateId; 5use base_db::CrateId;
6use fst::{self, Streamer}; 6use fst::{self, Streamer};
7use hir_expand::name::Name;
7use indexmap::{map::Entry, IndexMap}; 8use indexmap::{map::Entry, IndexMap};
9use itertools::Itertools;
8use rustc_hash::{FxHashMap, FxHasher}; 10use rustc_hash::{FxHashMap, FxHasher};
9use smallvec::SmallVec; 11use smallvec::SmallVec;
10use syntax::SmolStr; 12use syntax::SmolStr;
11 13
12use crate::{ 14use crate::{
13 db::DefDatabase, 15 db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
14 item_scope::ItemInNs, 16 ModuleId, TraitId,
15 path::{ModPath, PathKind},
16 visibility::Visibility,
17 AssocItemId, ModuleDefId, ModuleId, TraitId,
18}; 17};
19 18
20type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; 19type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
@@ -23,11 +22,28 @@ type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
23#[derive(Debug, Clone, Eq, PartialEq)] 22#[derive(Debug, Clone, Eq, PartialEq)]
24pub struct ImportInfo { 23pub struct ImportInfo {
25 /// A path that can be used to import the item, relative to the crate's root. 24 /// A path that can be used to import the item, relative to the crate's root.
26 pub path: ModPath, 25 pub path: ImportPath,
27 /// The module containing this item. 26 /// The module containing this item.
28 pub container: ModuleId, 27 pub container: ModuleId,
29} 28}
30 29
30#[derive(Debug, Clone, Eq, PartialEq)]
31pub struct ImportPath {
32 pub segments: Vec<Name>,
33}
34
35impl fmt::Display for ImportPath {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 fmt::Display::fmt(&self.segments.iter().format("::"), f)
38 }
39}
40
41impl ImportPath {
42 fn len(&self) -> usize {
43 self.segments.len()
44 }
45}
46
31/// A map from publicly exported items to the path needed to import/name them from a downstream 47/// A map from publicly exported items to the path needed to import/name them from a downstream
32/// crate. 48/// crate.
33/// 49///
@@ -61,7 +77,7 @@ impl ImportMap {
61 let mut import_map = Self::default(); 77 let mut import_map = Self::default();
62 78
63 // We look only into modules that are public(ly reexported), starting with the crate root. 79 // We look only into modules that are public(ly reexported), starting with the crate root.
64 let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; 80 let empty = ImportPath { segments: vec![] };
65 let root = ModuleId { krate, local_id: def_map.root }; 81 let root = ModuleId { krate, local_id: def_map.root };
66 let mut worklist = vec![(root, empty)]; 82 let mut worklist = vec![(root, empty)];
67 while let Some((module, mod_path)) = worklist.pop() { 83 while let Some((module, mod_path)) = worklist.pop() {
@@ -152,8 +168,8 @@ impl ImportMap {
152 } 168 }
153 169
154 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. 170 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
155 pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> { 171 pub fn path_of(&self, item: ItemInNs) -> Option<&ImportPath> {
156 Some(&self.map.get(&item)?.path) 172 self.import_info_for(item).map(|it| &it.path)
157 } 173 }
158 174
159 pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { 175 pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
@@ -197,7 +213,7 @@ impl fmt::Debug for ImportMap {
197 } 213 }
198} 214}
199 215
200fn fst_path(path: &ModPath) -> String { 216fn fst_path(path: &ImportPath) -> String {
201 let mut s = path.to_string(); 217 let mut s = path.to_string();
202 s.make_ascii_lowercase(); 218 s.make_ascii_lowercase();
203 s 219 s
@@ -334,14 +350,14 @@ mod tests {
334 350
335 use super::*; 351 use super::*;
336 352
337 fn check_search(ra_fixture: &str, krate_name: &str, query: Query, expect: Expect) { 353 fn check_search(ra_fixture: &str, crate_name: &str, query: Query, expect: Expect) {
338 let db = TestDB::with_files(ra_fixture); 354 let db = TestDB::with_files(ra_fixture);
339 let crate_graph = db.crate_graph(); 355 let crate_graph = db.crate_graph();
340 let krate = crate_graph 356 let krate = crate_graph
341 .iter() 357 .iter()
342 .find(|krate| { 358 .find(|krate| {
343 crate_graph[*krate].display_name.as_ref().map(|n| n.to_string()) 359 crate_graph[*krate].declaration_name.as_ref().map(|n| n.to_string())
344 == Some(krate_name.to_string()) 360 == Some(crate_name.to_string())
345 }) 361 })
346 .unwrap(); 362 .unwrap();
347 363
@@ -359,7 +375,7 @@ mod tests {
359 let path = map.path_of(item).unwrap(); 375 let path = map.path_of(item).unwrap();
360 format!( 376 format!(
361 "{}::{} ({})\n", 377 "{}::{} ({})\n",
362 crate_graph[krate].display_name.as_ref().unwrap(), 378 crate_graph[krate].declaration_name.as_ref().unwrap(),
363 path, 379 path,
364 mark 380 mark
365 ) 381 )
@@ -400,7 +416,7 @@ mod tests {
400 .iter() 416 .iter()
401 .filter_map(|krate| { 417 .filter_map(|krate| {
402 let cdata = &crate_graph[krate]; 418 let cdata = &crate_graph[krate];
403 let name = cdata.display_name.as_ref()?; 419 let name = cdata.declaration_name.as_ref()?;
404 420
405 let map = db.import_map(krate); 421 let map = db.import_map(krate);
406 422
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/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index 209b18e78..5b8c1e449 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -13,7 +13,7 @@ use hir_expand::{
13 hygiene::Hygiene, 13 hygiene::Hygiene,
14 name::{AsName, Name}, 14 name::{AsName, Name},
15}; 15};
16use syntax::ast::{self, make}; 16use syntax::ast::{self};
17 17
18use crate::{ 18use crate::{
19 type_ref::{TypeBound, TypeRef}, 19 type_ref::{TypeBound, TypeRef},
@@ -100,26 +100,6 @@ impl ModPath {
100 } 100 }
101 self.segments.first() 101 self.segments.first()
102 } 102 }
103
104 pub fn to_ast_path(&self) -> ast::Path {
105 let mut segments = Vec::new();
106 let mut is_abs = false;
107 match self.kind {
108 PathKind::Plain => {}
109 PathKind::Super(0) => segments.push(make::path_segment_self()),
110 PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())),
111 PathKind::Crate => segments.push(make::path_segment_crate()),
112 PathKind::Abs => is_abs = true,
113 PathKind::DollarCrate(_) => (),
114 }
115
116 segments.extend(
117 self.segments
118 .iter()
119 .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))),
120 );
121 make::path_from_segments(segments, is_abs)
122 }
123} 103}
124 104
125#[derive(Debug, Clone, PartialEq, Eq, Hash)] 105#[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index f389c5a4b..d2e151f25 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -221,7 +221,16 @@ impl HirDisplay for ApplicationTy {
221 } 221 }
222 TypeCtor::RawPtr(m) => { 222 TypeCtor::RawPtr(m) => {
223 let t = self.parameters.as_single(); 223 let t = self.parameters.as_single();
224 write!(f, "*{}{}", m.as_keyword_for_ptr(), t.display(f.db))?; 224 let ty_display = t.display(f.db);
225
226 write!(f, "*{}", m.as_keyword_for_ptr())?;
227 if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) {
228 write!(f, "(")?;
229 write!(f, "{}", ty_display)?;
230 write!(f, ")")?;
231 } else {
232 write!(f, "{}", ty_display)?;
233 }
225 } 234 }
226 TypeCtor::Ref(m) => { 235 TypeCtor::Ref(m) => {
227 let t = self.parameters.as_single(); 236 let t = self.parameters.as_single();
@@ -230,7 +239,15 @@ impl HirDisplay for ApplicationTy {
230 } else { 239 } else {
231 t.display(f.db) 240 t.display(f.db)
232 }; 241 };
233 write!(f, "&{}{}", m.as_keyword_for_ref(), ty_display)?; 242
243 write!(f, "&{}", m.as_keyword_for_ref())?;
244 if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) {
245 write!(f, "(")?;
246 write!(f, "{}", ty_display)?;
247 write!(f, ")")?;
248 } else {
249 write!(f, "{}", ty_display)?;
250 }
234 } 251 }
235 TypeCtor::Never => write!(f, "!")?, 252 TypeCtor::Never => write!(f, "!")?,
236 TypeCtor::Tuple { .. } => { 253 TypeCtor::Tuple { .. } => {
@@ -636,14 +653,14 @@ impl HirDisplay for GenericPredicate {
636 653
637impl HirDisplay for Obligation { 654impl HirDisplay for Obligation {
638 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { 655 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
639 Ok(match self { 656 match self {
640 Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db))?, 657 Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)),
641 Obligation::Projection(proj) => write!( 658 Obligation::Projection(proj) => write!(
642 f, 659 f,
643 "Normalize({} => {})", 660 "Normalize({} => {})",
644 proj.projection_ty.display(f.db), 661 proj.projection_ty.display(f.db),
645 proj.ty.display(f.db) 662 proj.ty.display(f.db)
646 )?, 663 ),
647 }) 664 }
648 } 665 }
649} 666}
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 ba67dd9f8..53265488e 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -300,7 +300,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
300 300
301fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String { 301fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String {
302 let crate_name = 302 let crate_name =
303 db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string); 303 db.crate_graph()[module.krate().into()].declaration_name.as_ref().map(ToString::to_string);
304 let module_path = module 304 let module_path = module
305 .path_to_root(db) 305 .path_to_root(db)
306 .into_iter() 306 .into_iter()
@@ -3202,4 +3202,34 @@ fn main() { let s<|>t = test().get(); }
3202 "#]], 3202 "#]],
3203 ); 3203 );
3204 } 3204 }
3205
3206 #[test]
3207 fn hover_displays_normalized_crate_names() {
3208 check(
3209 r#"
3210//- /lib.rs crate:name-with-dashes
3211pub mod wrapper {
3212 pub struct Thing { x: u32 }
3213
3214 impl Thing {
3215 pub fn new() -> Thing { Thing { x: 0 } }
3216 }
3217}
3218
3219//- /main.rs crate:main deps:name-with-dashes
3220fn main() { let foo_test = name_with_dashes::wrapper::Thing::new<|>(); }
3221"#,
3222 expect![[r#"
3223 *new*
3224
3225 ```rust
3226 name_with_dashes::wrapper::Thing
3227 ```
3228
3229 ```rust
3230 pub fn new() -> Thing
3231 ```
3232 "#]],
3233 )
3234 }
3205} 3235}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 1d7e8de56..3a4dc6a84 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1026,4 +1026,38 @@ mod collections {
1026"#, 1026"#,
1027 ); 1027 );
1028 } 1028 }
1029
1030 #[test]
1031 fn multi_dyn_trait_bounds() {
1032 check_with_config(
1033 InlayHintsConfig {
1034 type_hints: true,
1035 parameter_hints: false,
1036 chaining_hints: false,
1037 max_length: None,
1038 },
1039 r#"
1040//- /main.rs crate:main
1041pub struct Vec<T> {}
1042
1043impl<T> Vec<T> {
1044 pub fn new() -> Self { Vec {} }
1045}
1046
1047pub struct Box<T> {}
1048
1049trait Display {}
1050trait Sync {}
1051
1052fn main() {
1053 let _v = Vec::<Box<&(dyn Display + Sync)>>::new();
1054 //^^ Vec<Box<&(dyn Display + Sync)>>
1055 let _v = Vec::<Box<*const (dyn Display + Sync)>>::new();
1056 //^^ Vec<Box<*const (dyn Display + Sync)>>
1057 let _v = Vec::<Box<dyn Display + Sync>>::new();
1058 //^^ Vec<Box<dyn Display + Sync>>
1059}
1060"#,
1061 );
1062 }
1029} 1063}
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 f66487789..1b9b24698 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
@@ -407,13 +413,21 @@ enum ManifestOrProjectJson {
407} 413}
408 414
409#[derive(Deserialize)] 415#[derive(Deserialize)]
410#[serde(rename_all = "lowercase")] 416#[serde(rename_all = "snake_case")]
411enum MergeBehaviourDef { 417enum MergeBehaviourDef {
412 None, 418 None,
413 Full, 419 Full,
414 Last, 420 Last,
415} 421}
416 422
423#[derive(Deserialize)]
424#[serde(rename_all = "snake_case")]
425enum ImportPrefixDef {
426 Plain,
427 BySelf,
428 ByCrate,
429}
430
417macro_rules! config_data { 431macro_rules! config_data {
418 (struct $name:ident { $($field:ident: $ty:ty = $default:expr,)*}) => { 432 (struct $name:ident { $($field:ident: $ty:ty = $default:expr,)*}) => {
419 #[allow(non_snake_case)] 433 #[allow(non_snake_case)]
@@ -438,6 +452,7 @@ macro_rules! config_data {
438config_data! { 452config_data! {
439 struct ConfigData { 453 struct ConfigData {
440 assist_importMergeBehaviour: MergeBehaviourDef = MergeBehaviourDef::None, 454 assist_importMergeBehaviour: MergeBehaviourDef = MergeBehaviourDef::None,
455 assist_importPrefix: ImportPrefixDef = ImportPrefixDef::Plain,
441 456
442 callInfo_full: bool = true, 457 callInfo_full: bool = true,
443 458
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")) {