aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-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.rs131
-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.rs47
-rw-r--r--crates/hir/src/lib.rs10
-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_expand/src/name.rs1
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/hir_ty/src/display.rs29
-rw-r--r--crates/hir_ty/src/traits/chalk.rs14
-rw-r--r--crates/hir_ty/src/traits/chalk/mapping.rs2
-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.rs83
-rw-r--r--crates/ide/src/inlay_hints.rs233
-rw-r--r--crates/ide/src/lib.rs4
-rw-r--r--crates/ide/src/link_rewrite.rs6
-rw-r--r--crates/ide/src/markdown_remove.rs23
-rw-r--r--crates/ide/src/status.rs2
-rw-r--r--crates/ide/src/typing/on_enter.rs29
-rw-r--r--crates/mbe/src/subtree_source.rs30
-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.rs23
-rw-r--r--crates/rust-analyzer/src/handlers.rs6
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs11
-rw-r--r--crates/rust-analyzer/src/main_loop.rs2
-rw-r--r--crates/syntax/src/parsing/lexer.rs23
-rw-r--r--crates/syntax/src/ptr.rs2
-rw-r--r--crates/syntax/src/validation.rs45
-rw-r--r--crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast192
-rw-r--r--crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rs6
-rw-r--r--crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast200
-rw-r--r--crates/syntax/test_data/parser/ok/0069_multi_trait_object.rs6
51 files changed, 2083 insertions, 310 deletions
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..c1847f601 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -3,7 +3,7 @@ pub(crate) mod insert_use;
3 3
4use std::{iter, ops}; 4use std::{iter, ops};
5 5
6use hir::{Adt, Crate, Enum, ScopeDef, Semantics, Trait, Type}; 6use hir::{Adt, Crate, Enum, Module, ScopeDef, Semantics, Trait, Type};
7use ide_db::RootDatabase; 7use ide_db::RootDatabase;
8use itertools::Itertools; 8use itertools::Itertools;
9use rustc_hash::FxHashSet; 9use rustc_hash::FxHashSet;
@@ -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'))
@@ -253,15 +274,79 @@ impl TryEnum {
253/// somewhat similar to the known paths infra inside hir, but it different; We 274/// somewhat similar to the known paths infra inside hir, but it different; We
254/// want to make sure that IDE specific paths don't become interesting inside 275/// want to make sure that IDE specific paths don't become interesting inside
255/// the compiler itself as well. 276/// the compiler itself as well.
256pub(crate) struct FamousDefs<'a, 'b>(pub(crate) &'a Semantics<'b, RootDatabase>, pub(crate) Crate); 277pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Crate);
257 278
258#[allow(non_snake_case)] 279#[allow(non_snake_case)]
259impl FamousDefs<'_, '_> { 280impl FamousDefs<'_, '_> {
260 #[cfg(test)] 281 pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core
261 pub(crate) const FIXTURE: &'static str = r#"//- /libcore.rs crate:core
262pub mod convert { 282pub mod convert {
263 pub trait From<T> { 283 pub trait From<T> {
264 fn from(T) -> Self; 284 fn from(t: T) -> Self;
285 }
286}
287
288pub mod iter {
289 pub use self::traits::{collect::IntoIterator, iterator::Iterator};
290 mod traits {
291 pub(crate) mod iterator {
292 use crate::option::Option;
293 pub trait Iterator {
294 type Item;
295 fn next(&mut self) -> Option<Self::Item>;
296 fn by_ref(&mut self) -> &mut Self {
297 self
298 }
299 fn take(self, n: usize) -> crate::iter::Take<Self> {
300 crate::iter::Take { inner: self }
301 }
302 }
303
304 impl<I: Iterator> Iterator for &mut I {
305 type Item = I::Item;
306 fn next(&mut self) -> Option<I::Item> {
307 (**self).next()
308 }
309 }
310 }
311 pub(crate) mod collect {
312 pub trait IntoIterator {
313 type Item;
314 }
315 }
316 }
317
318 pub use self::sources::*;
319 pub(crate) mod sources {
320 use super::Iterator;
321 use crate::option::Option::{self, *};
322 pub struct Repeat<A> {
323 element: A,
324 }
325
326 pub fn repeat<T>(elt: T) -> Repeat<T> {
327 Repeat { element: elt }
328 }
329
330 impl<A> Iterator for Repeat<A> {
331 type Item = A;
332
333 fn next(&mut self) -> Option<A> {
334 None
335 }
336 }
337 }
338
339 pub use self::adapters::*;
340 pub(crate) mod adapters {
341 use super::Iterator;
342 use crate::option::Option::{self, *};
343 pub struct Take<I> { pub(crate) inner: I }
344 impl<I> Iterator for Take<I> where I: Iterator {
345 type Item = <I as Iterator>::Item;
346 fn next(&mut self) -> Option<<I as Iterator>::Item> {
347 None
348 }
349 }
265 } 350 }
266} 351}
267 352
@@ -270,7 +355,7 @@ pub mod option {
270} 355}
271 356
272pub mod prelude { 357pub mod prelude {
273 pub use crate::{convert::From, option::Option::{self, *}}; 358 pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}};
274} 359}
275#[prelude_import] 360#[prelude_import]
276pub use prelude::*; 361pub use prelude::*;
@@ -284,6 +369,14 @@ pub use prelude::*;
284 self.find_enum("core:option:Option") 369 self.find_enum("core:option:Option")
285 } 370 }
286 371
372 pub fn core_iter_Iterator(&self) -> Option<Trait> {
373 self.find_trait("core:iter:traits:iterator:Iterator")
374 }
375
376 pub fn core_iter(&self) -> Option<Module> {
377 self.find_module("core:iter")
378 }
379
287 fn find_trait(&self, path: &str) -> Option<Trait> { 380 fn find_trait(&self, path: &str) -> Option<Trait> {
288 match self.find_def(path)? { 381 match self.find_def(path)? {
289 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), 382 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
@@ -298,23 +391,33 @@ pub use prelude::*;
298 } 391 }
299 } 392 }
300 393
394 fn find_module(&self, path: &str) -> Option<Module> {
395 match self.find_def(path)? {
396 hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(it)) => Some(it),
397 _ => None,
398 }
399 }
400
301 fn find_def(&self, path: &str) -> Option<ScopeDef> { 401 fn find_def(&self, path: &str) -> Option<ScopeDef> {
302 let db = self.0.db; 402 let db = self.0.db;
303 let mut path = path.split(':'); 403 let mut path = path.split(':');
304 let trait_ = path.next_back()?; 404 let trait_ = path.next_back()?;
305 let std_crate = path.next()?; 405 let std_crate = path.next()?;
306 let std_crate = self 406 let std_crate = if self
307 .1 407 .1
308 .dependencies(db) 408 .declaration_name(db)
309 .into_iter() 409 .map(|name| name.to_string() == std_crate)
310 .find(|dep| &dep.name.to_string() == std_crate)? 410 .unwrap_or(false)
311 .krate; 411 {
312 412 self.1
413 } else {
414 self.1.dependencies(db).into_iter().find(|dep| dep.name.to_string() == std_crate)?.krate
415 };
313 let mut module = std_crate.root_module(db); 416 let mut module = std_crate.root_module(db);
314 for segment in path { 417 for segment in path {
315 module = module.children(db).find_map(|child| { 418 module = module.children(db).find_map(|child| {
316 let name = child.name(db)?; 419 let name = child.name(db)?;
317 if &name.to_string() == segment { 420 if name.to_string() == segment {
318 Some(child) 421 Some(child)
319 } else { 422 } else {
320 None 423 None
@@ -322,7 +425,7 @@ pub use prelude::*;
322 })?; 425 })?;
323 } 426 }
324 let def = 427 let def =
325 module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1; 428 module.scope(db, None).into_iter().find(|(name, _def)| name.to_string() == trait_)?.1;
326 Some(def) 429 Some(def)
327 } 430 }
328} 431}
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..031c91ccf 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,
@@ -29,8 +30,12 @@ use hir_expand::{
29use hir_ty::{ 30use hir_ty::{
30 autoderef, 31 autoderef,
31 display::{HirDisplayError, HirFormatter}, 32 display::{HirDisplayError, HirFormatter},
32 method_resolution, ApplicationTy, CallableDefId, Canonical, FnSig, GenericPredicate, 33 method_resolution,
33 InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, 34 traits::Solution,
35 traits::SolutionVariables,
36 ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate,
37 InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, Ty,
38 TyDefId, TyKind, TypeCtor,
34}; 39};
35use rustc_hash::FxHashSet; 40use rustc_hash::FxHashSet;
36use stdx::impl_from; 41use stdx::impl_from;
@@ -98,8 +103,8 @@ impl Crate {
98 db.crate_graph()[self.id].edition 103 db.crate_graph()[self.id].edition
99 } 104 }
100 105
101 pub fn display_name(self, db: &dyn HirDatabase) -> Option<String> { 106 pub fn declaration_name(self, db: &dyn HirDatabase) -> Option<CrateName> {
102 db.crate_graph()[self.id].display_name.clone() 107 db.crate_graph()[self.id].declaration_name.clone()
103 } 108 }
104 109
105 pub fn query_external_importables( 110 pub fn query_external_importables(
@@ -390,8 +395,9 @@ impl Module {
390 self, 395 self,
391 db: &dyn DefDatabase, 396 db: &dyn DefDatabase,
392 item: impl Into<ItemInNs>, 397 item: impl Into<ItemInNs>,
398 prefix_kind: PrefixKind,
393 ) -> Option<ModPath> { 399 ) -> Option<ModPath> {
394 hir_def::find_path::find_path_prefixed(db, item.into(), self.into()) 400 hir_def::find_path::find_path_prefixed(db, item.into(), self.into(), prefix_kind)
395 } 401 }
396} 402}
397 403
@@ -1360,6 +1366,35 @@ impl Type {
1360 db.trait_solve(self.krate, goal).is_some() 1366 db.trait_solve(self.krate, goal).is_some()
1361 } 1367 }
1362 1368
1369 pub fn normalize_trait_assoc_type(
1370 &self,
1371 db: &dyn HirDatabase,
1372 r#trait: Trait,
1373 args: &[Type],
1374 alias: TypeAlias,
1375 ) -> Option<Ty> {
1376 let subst = Substs::build_for_def(db, r#trait.id)
1377 .push(self.ty.value.clone())
1378 .fill(args.iter().map(|t| t.ty.value.clone()))
1379 .build();
1380 let predicate = ProjectionPredicate {
1381 projection_ty: ProjectionTy { associated_ty: alias.id, parameters: subst },
1382 ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)),
1383 };
1384 let goal = Canonical {
1385 value: InEnvironment::new(
1386 self.ty.environment.clone(),
1387 Obligation::Projection(predicate),
1388 ),
1389 kinds: Arc::new([TyKind::General]),
1390 };
1391
1392 match db.trait_solve(self.krate, goal)? {
1393 Solution::Unique(SolutionVariables(subst)) => subst.value.first().cloned(),
1394 Solution::Ambig(_) => None,
1395 }
1396 }
1397
1363 pub fn is_copy(&self, db: &dyn HirDatabase) -> bool { 1398 pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
1364 let lang_item = db.lang_item(self.krate, SmolStr::new("copy")); 1399 let lang_item = db.lang_item(self.krate, SmolStr::new("copy"));
1365 let copy_trait = match lang_item { 1400 let copy_trait = match lang_item {
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b9d9c7e25..4094a76cb 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -48,13 +48,14 @@ 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::{
57 name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, 58 name::known, name::AsName, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc,
58 /* FIXME */ MacroDefId, MacroFile, Origin, 59 /* FIXME */ MacroDefId, MacroFile, Origin,
59}; 60};
60pub use hir_ty::display::HirDisplay; 61pub use hir_ty::display::HirDisplay;
@@ -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_expand/src/name.rs b/crates/hir_expand/src/name.rs
index a5750d829..63f828707 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -164,6 +164,7 @@ pub mod known {
164 result, 164 result,
165 boxed, 165 boxed,
166 // Components of known path (type name) 166 // Components of known path (type name)
167 Iterator,
167 IntoIterator, 168 IntoIterator,
168 Item, 169 Item,
169 Try, 170 Try,
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index ed1c911c2..0f3c85926 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -17,9 +17,9 @@ ena = "0.14.0"
17log = "0.4.8" 17log = "0.4.8"
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19scoped-tls = "1" 19scoped-tls = "1"
20chalk-solve = { version = "0.30.0" } 20chalk-solve = "0.32"
21chalk-ir = { version = "0.30.0" } 21chalk-ir = "0.32"
22chalk-recursive = { version = "0.30.0" } 22chalk-recursive = "0.32"
23 23
24stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
25hir_def = { path = "../hir_def", version = "0.0.0" } 25hir_def = { path = "../hir_def", version = "0.0.0" }
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/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 009b17a7f..cbe5cd7dd 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -330,6 +330,20 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
330 fn fn_def_name(&self, fn_def_id: chalk_ir::FnDefId<Interner>) -> String { 330 fn fn_def_name(&self, fn_def_id: chalk_ir::FnDefId<Interner>) -> String {
331 format!("fn_{}", fn_def_id.0) 331 format!("fn_{}", fn_def_id.0)
332 } 332 }
333 fn generator_datum(
334 &self,
335 _: chalk_ir::GeneratorId<Interner>,
336 ) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorDatum<Interner>> {
337 // FIXME
338 unimplemented!()
339 }
340 fn generator_witness_datum(
341 &self,
342 _: chalk_ir::GeneratorId<Interner>,
343 ) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<Interner>> {
344 // FIXME
345 unimplemented!()
346 }
333} 347}
334 348
335pub(crate) fn program_clauses_for_chalk_env_query( 349pub(crate) fn program_clauses_for_chalk_env_query(
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs
index d42f4bba9..be3301313 100644
--- a/crates/hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/hir_ty/src/traits/chalk/mapping.rs
@@ -399,6 +399,8 @@ impl ToChalk for TypeCtor {
399 // this should not be reached, since we don't represent TypeName::Error with TypeCtor 399 // this should not be reached, since we don't represent TypeName::Error with TypeCtor
400 unreachable!() 400 unreachable!()
401 } 401 }
402 TypeName::Generator(_) => unimplemented!(), // FIXME
403 TypeName::GeneratorWitness(_) => unimplemented!(), // FIXME
402 } 404 }
403 } 405 }
404} 406}
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..53265488e 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -15,6 +15,7 @@ use test_utils::mark;
15use crate::{ 15use crate::{
16 display::{macro_label, ShortLabel, ToNav, TryToNav}, 16 display::{macro_label, ShortLabel, ToNav, TryToNav},
17 link_rewrite::{remove_links, rewrite_links}, 17 link_rewrite::{remove_links, rewrite_links},
18 markdown_remove::remove_markdown,
18 markup::Markup, 19 markup::Markup,
19 runnables::runnable, 20 runnables::runnable,
20 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, 21 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
@@ -27,6 +28,7 @@ pub struct HoverConfig {
27 pub debug: bool, 28 pub debug: bool,
28 pub goto_type_def: bool, 29 pub goto_type_def: bool,
29 pub links_in_hover: bool, 30 pub links_in_hover: bool,
31 pub markdown: bool,
30} 32}
31 33
32impl Default for HoverConfig { 34impl Default for HoverConfig {
@@ -37,6 +39,7 @@ impl Default for HoverConfig {
37 debug: true, 39 debug: true,
38 goto_type_def: true, 40 goto_type_def: true,
39 links_in_hover: true, 41 links_in_hover: true,
42 markdown: true,
40 } 43 }
41 } 44 }
42} 45}
@@ -48,6 +51,7 @@ impl HoverConfig {
48 debug: false, 51 debug: false,
49 goto_type_def: false, 52 goto_type_def: false,
50 links_in_hover: true, 53 links_in_hover: true,
54 markdown: true,
51 }; 55 };
52 56
53 pub fn any(&self) -> bool { 57 pub fn any(&self) -> bool {
@@ -91,6 +95,7 @@ pub(crate) fn hover(
91 db: &RootDatabase, 95 db: &RootDatabase,
92 position: FilePosition, 96 position: FilePosition,
93 links_in_hover: bool, 97 links_in_hover: bool,
98 markdown: bool,
94) -> Option<RangeInfo<HoverResult>> { 99) -> Option<RangeInfo<HoverResult>> {
95 let sema = Semantics::new(db); 100 let sema = Semantics::new(db);
96 let file = sema.parse(position.file_id).syntax().clone(); 101 let file = sema.parse(position.file_id).syntax().clone();
@@ -109,7 +114,9 @@ pub(crate) fn hover(
109 }; 114 };
110 if let Some(definition) = definition { 115 if let Some(definition) = definition {
111 if let Some(markup) = hover_for_definition(db, definition) { 116 if let Some(markup) = hover_for_definition(db, definition) {
112 let markup = if links_in_hover { 117 let markup = if !markdown {
118 remove_markdown(&markup.as_str())
119 } else if links_in_hover {
113 rewrite_links(db, &markup.as_str(), &definition) 120 rewrite_links(db, &markup.as_str(), &definition)
114 } else { 121 } else {
115 remove_links(&markup.as_str()) 122 remove_links(&markup.as_str())
@@ -147,7 +154,11 @@ pub(crate) fn hover(
147 } 154 }
148 }; 155 };
149 156
150 res.markup = Markup::fenced_block(&ty.display(db)); 157 res.markup = if markdown {
158 Markup::fenced_block(&ty.display(db))
159 } else {
160 ty.display(db).to_string().into()
161 };
151 let range = sema.original_range(&node).range; 162 let range = sema.original_range(&node).range;
152 Some(RangeInfo::new(range, res)) 163 Some(RangeInfo::new(range, res))
153} 164}
@@ -289,7 +300,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
289 300
290fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String { 301fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String {
291 let crate_name = 302 let crate_name =
292 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);
293 let module_path = module 304 let module_path = module
294 .path_to_root(db) 305 .path_to_root(db)
295 .into_iter() 306 .into_iter()
@@ -383,12 +394,12 @@ mod tests {
383 394
384 fn check_hover_no_result(ra_fixture: &str) { 395 fn check_hover_no_result(ra_fixture: &str) {
385 let (analysis, position) = fixture::position(ra_fixture); 396 let (analysis, position) = fixture::position(ra_fixture);
386 assert!(analysis.hover(position, true).unwrap().is_none()); 397 assert!(analysis.hover(position, true, true).unwrap().is_none());
387 } 398 }
388 399
389 fn check(ra_fixture: &str, expect: Expect) { 400 fn check(ra_fixture: &str, expect: Expect) {
390 let (analysis, position) = fixture::position(ra_fixture); 401 let (analysis, position) = fixture::position(ra_fixture);
391 let hover = analysis.hover(position, true).unwrap().unwrap(); 402 let hover = analysis.hover(position, true, true).unwrap().unwrap();
392 403
393 let content = analysis.db.file_text(position.file_id); 404 let content = analysis.db.file_text(position.file_id);
394 let hovered_element = &content[hover.range]; 405 let hovered_element = &content[hover.range];
@@ -399,7 +410,18 @@ mod tests {
399 410
400 fn check_hover_no_links(ra_fixture: &str, expect: Expect) { 411 fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
401 let (analysis, position) = fixture::position(ra_fixture); 412 let (analysis, position) = fixture::position(ra_fixture);
402 let hover = analysis.hover(position, false).unwrap().unwrap(); 413 let hover = analysis.hover(position, false, true).unwrap().unwrap();
414
415 let content = analysis.db.file_text(position.file_id);
416 let hovered_element = &content[hover.range];
417
418 let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
419 expect.assert_eq(&actual)
420 }
421
422 fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
423 let (analysis, position) = fixture::position(ra_fixture);
424 let hover = analysis.hover(position, true, false).unwrap().unwrap();
403 425
404 let content = analysis.db.file_text(position.file_id); 426 let content = analysis.db.file_text(position.file_id);
405 let hovered_element = &content[hover.range]; 427 let hovered_element = &content[hover.range];
@@ -410,7 +432,7 @@ mod tests {
410 432
411 fn check_actions(ra_fixture: &str, expect: Expect) { 433 fn check_actions(ra_fixture: &str, expect: Expect) {
412 let (analysis, position) = fixture::position(ra_fixture); 434 let (analysis, position) = fixture::position(ra_fixture);
413 let hover = analysis.hover(position, true).unwrap().unwrap(); 435 let hover = analysis.hover(position, true, true).unwrap().unwrap();
414 expect.assert_debug_eq(&hover.info.actions) 436 expect.assert_debug_eq(&hover.info.actions)
415 } 437 }
416 438
@@ -434,6 +456,23 @@ fn main() {
434 } 456 }
435 457
436 #[test] 458 #[test]
459 fn hover_remove_markdown_if_configured() {
460 check_hover_no_markdown(
461 r#"
462pub fn foo() -> u32 { 1 }
463
464fn main() {
465 let foo_test = foo()<|>;
466}
467"#,
468 expect![[r#"
469 *foo()*
470 u32
471 "#]],
472 );
473 }
474
475 #[test]
437 fn hover_shows_long_type_of_an_expression() { 476 fn hover_shows_long_type_of_an_expression() {
438 check( 477 check(
439 r#" 478 r#"
@@ -3163,4 +3202,34 @@ fn main() { let s<|>t = test().get(); }
3163 "#]], 3202 "#]],
3164 ); 3203 );
3165 } 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 }
3166} 3235}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 1d7e8de56..7d716577e 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1,4 +1,5 @@
1use hir::{Adt, Callable, HirDisplay, Semantics, Type}; 1use assists::utils::FamousDefs;
2use hir::{known, HirDisplay, Semantics};
2use ide_db::RootDatabase; 3use ide_db::RootDatabase;
3use stdx::to_lower_snake_case; 4use stdx::to_lower_snake_case;
4use syntax::{ 5use syntax::{
@@ -119,17 +120,18 @@ fn get_chaining_hints(
119 return None; 120 return None;
120 } 121 }
121 if matches!(expr, ast::Expr::PathExpr(_)) { 122 if matches!(expr, ast::Expr::PathExpr(_)) {
122 if let Some(Adt::Struct(st)) = ty.as_adt() { 123 if let Some(hir::Adt::Struct(st)) = ty.as_adt() {
123 if st.fields(sema.db).is_empty() { 124 if st.fields(sema.db).is_empty() {
124 return None; 125 return None;
125 } 126 }
126 } 127 }
127 } 128 }
128 let label = ty.display_truncated(sema.db, config.max_length).to_string();
129 acc.push(InlayHint { 129 acc.push(InlayHint {
130 range: expr.syntax().text_range(), 130 range: expr.syntax().text_range(),
131 kind: InlayKind::ChainingHint, 131 kind: InlayKind::ChainingHint,
132 label: label.into(), 132 label: hint_iterator(sema, config, &ty).unwrap_or_else(|| {
133 ty.display_truncated(sema.db, config.max_length).to_string().into()
134 }),
133 }); 135 });
134 } 136 }
135 Some(()) 137 Some(())
@@ -192,17 +194,58 @@ fn get_bind_pat_hints(
192 if should_not_display_type_hint(sema, &pat, &ty) { 194 if should_not_display_type_hint(sema, &pat, &ty) {
193 return None; 195 return None;
194 } 196 }
195
196 acc.push(InlayHint { 197 acc.push(InlayHint {
197 range: pat.syntax().text_range(), 198 range: pat.syntax().text_range(),
198 kind: InlayKind::TypeHint, 199 kind: InlayKind::TypeHint,
199 label: ty.display_truncated(sema.db, config.max_length).to_string().into(), 200 label: hint_iterator(sema, config, &ty)
201 .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string().into()),
200 }); 202 });
203
201 Some(()) 204 Some(())
202} 205}
203 206
204fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Type) -> bool { 207/// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`.
205 if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() { 208fn hint_iterator(
209 sema: &Semantics<RootDatabase>,
210 config: &InlayHintsConfig,
211 ty: &hir::Type,
212) -> Option<SmolStr> {
213 let db = sema.db;
214 let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref())
215 .last()
216 .and_then(|strukt| strukt.as_adt())?;
217 let krate = strukt.krate(db)?;
218 if krate.declaration_name(db).as_deref() != Some("core") {
219 return None;
220 }
221 let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
222 let iter_mod = FamousDefs(sema, krate).core_iter()?;
223 // assert this type comes from `core::iter`
224 iter_mod.visibility_of(db, &iter_trait.into()).filter(|&vis| vis == hir::Visibility::Public)?;
225 if ty.impls_trait(db, iter_trait, &[]) {
226 let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item {
227 hir::AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias),
228 _ => None,
229 })?;
230 if let Some(ty) = ty.normalize_trait_assoc_type(db, iter_trait, &[], assoc_type_item) {
231 const LABEL_START: &str = "impl Iterator<Item = ";
232 const LABEL_END: &str = ">";
233
234 let ty_display = ty.display_truncated(
235 db,
236 config
237 .max_length
238 .map(|len| len.saturating_sub(LABEL_START.len() + LABEL_END.len())),
239 );
240 return Some(format!("{}{}{}", LABEL_START, ty_display, LABEL_END).into());
241 }
242 }
243
244 None
245}
246
247fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool {
248 if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() {
206 let pat_text = bind_pat.to_string(); 249 let pat_text = bind_pat.to_string();
207 enum_data 250 enum_data
208 .variants(db) 251 .variants(db)
@@ -217,7 +260,7 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Typ
217fn should_not_display_type_hint( 260fn should_not_display_type_hint(
218 sema: &Semantics<RootDatabase>, 261 sema: &Semantics<RootDatabase>,
219 bind_pat: &ast::IdentPat, 262 bind_pat: &ast::IdentPat,
220 pat_ty: &Type, 263 pat_ty: &hir::Type,
221) -> bool { 264) -> bool {
222 let db = sema.db; 265 let db = sema.db;
223 266
@@ -225,7 +268,7 @@ fn should_not_display_type_hint(
225 return true; 268 return true;
226 } 269 }
227 270
228 if let Some(Adt::Struct(s)) = pat_ty.as_adt() { 271 if let Some(hir::Adt::Struct(s)) = pat_ty.as_adt() {
229 if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.to_string() { 272 if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.to_string() {
230 return true; 273 return true;
231 } 274 }
@@ -269,7 +312,7 @@ fn should_not_display_type_hint(
269 312
270fn should_show_param_name_hint( 313fn should_show_param_name_hint(
271 sema: &Semantics<RootDatabase>, 314 sema: &Semantics<RootDatabase>,
272 callable: &Callable, 315 callable: &hir::Callable,
273 param_name: &str, 316 param_name: &str,
274 argument: &ast::Expr, 317 argument: &ast::Expr,
275) -> bool { 318) -> bool {
@@ -316,7 +359,7 @@ fn is_enum_name_similar_to_param_name(
316 param_name: &str, 359 param_name: &str,
317) -> bool { 360) -> bool {
318 match sema.type_of_expr(argument).and_then(|t| t.as_adt()) { 361 match sema.type_of_expr(argument).and_then(|t| t.as_adt()) {
319 Some(Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name, 362 Some(hir::Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name,
320 _ => false, 363 _ => false,
321 } 364 }
322} 365}
@@ -337,7 +380,7 @@ fn is_obvious_param(param_name: &str) -> bool {
337 param_name.len() == 1 || is_obvious_param_name 380 param_name.len() == 1 || is_obvious_param_name
338} 381}
339 382
340fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<Callable> { 383fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Callable> {
341 match expr { 384 match expr {
342 ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db), 385 ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db),
343 ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr), 386 ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr),
@@ -347,6 +390,7 @@ fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<Call
347 390
348#[cfg(test)] 391#[cfg(test)]
349mod tests { 392mod tests {
393 use assists::utils::FamousDefs;
350 use expect_test::{expect, Expect}; 394 use expect_test::{expect, Expect};
351 use test_utils::extract_annotations; 395 use test_utils::extract_annotations;
352 396
@@ -357,7 +401,9 @@ mod tests {
357 } 401 }
358 402
359 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { 403 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
360 let (analysis, file_id) = fixture::file(ra_fixture); 404 let ra_fixture =
405 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
406 let (analysis, file_id) = fixture::file(&ra_fixture);
361 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); 407 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
362 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); 408 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
363 let actual = 409 let actual =
@@ -366,7 +412,9 @@ mod tests {
366 } 412 }
367 413
368 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { 414 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
369 let (analysis, file_id) = fixture::file(ra_fixture); 415 let ra_fixture =
416 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
417 let (analysis, file_id) = fixture::file(&ra_fixture);
370 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); 418 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
371 expect.assert_debug_eq(&inlay_hints) 419 expect.assert_debug_eq(&inlay_hints)
372 } 420 }
@@ -798,12 +846,12 @@ fn main() {
798 expect![[r#" 846 expect![[r#"
799 [ 847 [
800 InlayHint { 848 InlayHint {
801 range: 147..172, 849 range: 148..173,
802 kind: ChainingHint, 850 kind: ChainingHint,
803 label: "B", 851 label: "B",
804 }, 852 },
805 InlayHint { 853 InlayHint {
806 range: 147..154, 854 range: 148..155,
807 kind: ChainingHint, 855 kind: ChainingHint,
808 label: "A", 856 label: "A",
809 }, 857 },
@@ -864,12 +912,12 @@ fn main() {
864 expect![[r#" 912 expect![[r#"
865 [ 913 [
866 InlayHint { 914 InlayHint {
867 range: 143..190, 915 range: 144..191,
868 kind: ChainingHint, 916 kind: ChainingHint,
869 label: "C", 917 label: "C",
870 }, 918 },
871 InlayHint { 919 InlayHint {
872 range: 143..179, 920 range: 144..180,
873 kind: ChainingHint, 921 kind: ChainingHint,
874 label: "B", 922 label: "B",
875 }, 923 },
@@ -909,12 +957,12 @@ fn main() {
909 expect![[r#" 957 expect![[r#"
910 [ 958 [
911 InlayHint { 959 InlayHint {
912 range: 246..283, 960 range: 247..284,
913 kind: ChainingHint, 961 kind: ChainingHint,
914 label: "B<X<i32, bool>>", 962 label: "B<X<i32, bool>>",
915 }, 963 },
916 InlayHint { 964 InlayHint {
917 range: 246..265, 965 range: 247..266,
918 kind: ChainingHint, 966 kind: ChainingHint,
919 label: "A<X<i32, bool>>", 967 label: "A<X<i32, bool>>",
920 }, 968 },
@@ -935,7 +983,6 @@ fn main() {
935 ); 983 );
936 check( 984 check(
937 r#" 985 r#"
938//- /main.rs crate:main deps:core
939pub struct Vec<T> {} 986pub struct Vec<T> {}
940 987
941impl<T> Vec<T> { 988impl<T> Vec<T> {
@@ -956,13 +1003,6 @@ fn main() {
956 println!("Unit expr"); 1003 println!("Unit expr");
957} 1004}
958 1005
959//- /core.rs crate:core
960#[prelude_import] use iter::*;
961mod iter {
962 trait IntoIterator {
963 type Item;
964 }
965}
966//- /alloc.rs crate:alloc deps:core 1006//- /alloc.rs crate:alloc deps:core
967mod collections { 1007mod collections {
968 struct Vec<T> {} 1008 struct Vec<T> {}
@@ -982,7 +1022,6 @@ mod collections {
982 fn complete_for_hint() { 1022 fn complete_for_hint() {
983 check( 1023 check(
984 r#" 1024 r#"
985//- /main.rs crate:main deps:core
986pub struct Vec<T> {} 1025pub struct Vec<T> {}
987 1026
988impl<T> Vec<T> { 1027impl<T> Vec<T> {
@@ -1004,14 +1043,6 @@ fn main() {
1004 //^ &str 1043 //^ &str
1005 } 1044 }
1006} 1045}
1007
1008//- /core.rs crate:core
1009#[prelude_import] use iter::*;
1010mod iter {
1011 trait IntoIterator {
1012 type Item;
1013 }
1014}
1015//- /alloc.rs crate:alloc deps:core 1046//- /alloc.rs crate:alloc deps:core
1016mod collections { 1047mod collections {
1017 struct Vec<T> {} 1048 struct Vec<T> {}
@@ -1026,4 +1057,130 @@ mod collections {
1026"#, 1057"#,
1027 ); 1058 );
1028 } 1059 }
1060
1061 #[test]
1062 fn multi_dyn_trait_bounds() {
1063 check_with_config(
1064 InlayHintsConfig {
1065 type_hints: true,
1066 parameter_hints: false,
1067 chaining_hints: false,
1068 max_length: None,
1069 },
1070 r#"
1071pub struct Vec<T> {}
1072
1073impl<T> Vec<T> {
1074 pub fn new() -> Self { Vec {} }
1075}
1076
1077pub struct Box<T> {}
1078
1079trait Display {}
1080trait Sync {}
1081
1082fn main() {
1083 let _v = Vec::<Box<&(dyn Display + Sync)>>::new();
1084 //^^ Vec<Box<&(dyn Display + Sync)>>
1085 let _v = Vec::<Box<*const (dyn Display + Sync)>>::new();
1086 //^^ Vec<Box<*const (dyn Display + Sync)>>
1087 let _v = Vec::<Box<dyn Display + Sync>>::new();
1088 //^^ Vec<Box<dyn Display + Sync>>
1089}
1090"#,
1091 );
1092 }
1093
1094 #[test]
1095 fn shorten_iterator_hints() {
1096 check_with_config(
1097 InlayHintsConfig {
1098 parameter_hints: false,
1099 type_hints: true,
1100 chaining_hints: false,
1101 max_length: None,
1102 },
1103 r#"
1104use core::iter;
1105
1106struct MyIter;
1107
1108impl Iterator for MyIter {
1109 type Item = ();
1110 fn next(&mut self) -> Option<Self::Item> {
1111 None
1112 }
1113}
1114
1115fn main() {
1116 let _x = MyIter;
1117 //^^ MyIter
1118 let _x = iter::repeat(0);
1119 //^^ impl Iterator<Item = i32>
1120 fn generic<T: Clone>(t: T) {
1121 let _x = iter::repeat(t);
1122 //^^ impl Iterator<Item = T>
1123 let _chained = iter::repeat(t).take(10);
1124 //^^^^^^^^ impl Iterator<Item = T>
1125 }
1126}
1127"#,
1128 );
1129 }
1130
1131 #[test]
1132 fn shorten_iterator_chaining_hints() {
1133 check_expect(
1134 InlayHintsConfig {
1135 parameter_hints: false,
1136 type_hints: false,
1137 chaining_hints: true,
1138 max_length: None,
1139 },
1140 r#"
1141use core::iter;
1142
1143struct MyIter;
1144
1145impl Iterator for MyIter {
1146 type Item = ();
1147 fn next(&mut self) -> Option<Self::Item> {
1148 None
1149 }
1150}
1151
1152fn main() {
1153 let _x = MyIter.by_ref()
1154 .take(5)
1155 .by_ref()
1156 .take(5)
1157 .by_ref();
1158}
1159"#,
1160 expect![[r#"
1161 [
1162 InlayHint {
1163 range: 175..242,
1164 kind: ChainingHint,
1165 label: "impl Iterator<Item = ()>",
1166 },
1167 InlayHint {
1168 range: 175..225,
1169 kind: ChainingHint,
1170 label: "impl Iterator<Item = ()>",
1171 },
1172 InlayHint {
1173 range: 175..207,
1174 kind: ChainingHint,
1175 label: "impl Iterator<Item = ()>",
1176 },
1177 InlayHint {
1178 range: 175..190,
1179 kind: ChainingHint,
1180 label: "&mut MyIter",
1181 },
1182 ]
1183 "#]],
1184 );
1185 }
1029} 1186}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 1aa673cf8..57f3581b6 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -46,6 +46,7 @@ mod syntax_highlighting;
46mod syntax_tree; 46mod syntax_tree;
47mod typing; 47mod typing;
48mod link_rewrite; 48mod link_rewrite;
49mod markdown_remove;
49 50
50use std::sync::Arc; 51use std::sync::Arc;
51 52
@@ -376,8 +377,9 @@ impl Analysis {
376 &self, 377 &self,
377 position: FilePosition, 378 position: FilePosition,
378 links_in_hover: bool, 379 links_in_hover: bool,
380 markdown: bool,
379 ) -> Cancelable<Option<RangeInfo<HoverResult>>> { 381 ) -> Cancelable<Option<RangeInfo<HoverResult>>> {
380 self.with_db(|db| hover::hover(db, position, links_in_hover)) 382 self.with_db(|db| hover::hover(db, position, links_in_hover, markdown))
381 } 383 }
382 384
383 /// Computes parameter information for the given call expression. 385 /// Computes parameter information for the given call expression.
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/markdown_remove.rs b/crates/ide/src/markdown_remove.rs
new file mode 100644
index 000000000..02ad39dfb
--- /dev/null
+++ b/crates/ide/src/markdown_remove.rs
@@ -0,0 +1,23 @@
1//! Removes markdown from strings.
2
3use pulldown_cmark::{Event, Parser, Tag};
4
5/// Removes all markdown, keeping the text and code blocks
6///
7/// Currently limited in styling, i.e. no ascii tables or lists
8pub fn remove_markdown(markdown: &str) -> String {
9 let mut out = String::new();
10 let parser = Parser::new(markdown);
11
12 for event in parser {
13 match event {
14 Event::Text(text) | Event::Code(text) => out.push_str(&text),
15 Event::SoftBreak | Event::HardBreak | Event::Rule | Event::End(Tag::CodeBlock(_)) => {
16 out.push('\n')
17 }
18 _ => {}
19 }
20 }
21
22 out
23}
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/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index a0dc4b9df..98adef1d6 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -51,12 +51,12 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text
51 return None; 51 return None;
52 } 52 }
53 53
54 let mut remove_last_space = false; 54 let mut remove_trailing_whitespace = false;
55 // Continuing single-line non-doc comments (like this one :) ) is annoying 55 // Continuing single-line non-doc comments (like this one :) ) is annoying
56 if prefix == "//" && comment_range.end() == position.offset { 56 if prefix == "//" && comment_range.end() == position.offset {
57 if comment.text().ends_with(' ') { 57 if comment.text().ends_with(' ') {
58 mark::hit!(continues_end_of_line_comment_with_space); 58 mark::hit!(continues_end_of_line_comment_with_space);
59 remove_last_space = true; 59 remove_trailing_whitespace = true;
60 } else if !followed_by_comment(&comment) { 60 } else if !followed_by_comment(&comment) {
61 return None; 61 return None;
62 } 62 }
@@ -64,8 +64,10 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text
64 64
65 let indent = node_indent(&file, comment.syntax())?; 65 let indent = node_indent(&file, comment.syntax())?;
66 let inserted = format!("\n{}{} $0", indent, prefix); 66 let inserted = format!("\n{}{} $0", indent, prefix);
67 let delete = if remove_last_space { 67 let delete = if remove_trailing_whitespace {
68 TextRange::new(position.offset - TextSize::of(' '), position.offset) 68 let trimmed_len = comment.text().trim_end().len() as u32;
69 let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len;
70 TextRange::new(position.offset - TextSize::from(trailing_whitespace_len), position.offset)
69 } else { 71 } else {
70 TextRange::empty(position.offset) 72 TextRange::empty(position.offset)
71 }; 73 };
@@ -253,4 +255,23 @@ fn main() {
253"#, 255"#,
254 ); 256 );
255 } 257 }
258
259 #[test]
260 fn trims_all_trailing_whitespace() {
261 do_check(
262 "
263fn main() {
264 // Fix me \t\t <|>
265 let x = 1 + 1;
266}
267",
268 "
269fn main() {
270 // Fix me
271 // $0
272 let x = 1 + 1;
273}
274",
275 );
276 }
256} 277}
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs
index 41461b315..396ce8b16 100644
--- a/crates/mbe/src/subtree_source.rs
+++ b/crates/mbe/src/subtree_source.rs
@@ -155,9 +155,14 @@ fn convert_delim(d: Option<tt::DelimiterKind>, closing: bool) -> TtToken {
155} 155}
156 156
157fn convert_literal(l: &tt::Literal) -> TtToken { 157fn convert_literal(l: &tt::Literal) -> TtToken {
158 let kind = lex_single_syntax_kind(&l.text) 158 let is_negated = l.text.starts_with('-');
159 let inner_text = &l.text[if is_negated { 1 } else { 0 }..];
160
161 let kind = lex_single_syntax_kind(inner_text)
159 .map(|(kind, _error)| kind) 162 .map(|(kind, _error)| kind)
160 .filter(|kind| kind.is_literal()) 163 .filter(|kind| {
164 kind.is_literal() && (!is_negated || matches!(kind, FLOAT_NUMBER | INT_NUMBER))
165 })
161 .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l)); 166 .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l));
162 167
163 TtToken { kind, is_joint_to_next: false, text: l.text.clone() } 168 TtToken { kind, is_joint_to_next: false, text: l.text.clone() }
@@ -195,3 +200,24 @@ fn convert_leaf(leaf: &tt::Leaf) -> TtToken {
195 tt::Leaf::Punct(punct) => convert_punct(*punct), 200 tt::Leaf::Punct(punct) => convert_punct(*punct),
196 } 201 }
197} 202}
203
204#[cfg(test)]
205mod tests {
206 use super::{convert_literal, TtToken};
207 use syntax::{SmolStr, SyntaxKind};
208
209 #[test]
210 fn test_negative_literal() {
211 assert_eq!(
212 convert_literal(&tt::Literal {
213 id: tt::TokenId::unspecified(),
214 text: SmolStr::new("-42.0")
215 }),
216 TtToken {
217 kind: SyntaxKind::FLOAT_NUMBER,
218 is_joint_to_next: false,
219 text: SmolStr::new("-42.0")
220 }
221 );
222 }
223}
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..1b9b24698 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -10,11 +10,12 @@
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,
16}; 17};
17use lsp_types::ClientCapabilities; 18use lsp_types::{ClientCapabilities, MarkupKind};
18use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; 19use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest};
19use rustc_hash::FxHashSet; 20use rustc_hash::FxHashSet;
20use serde::Deserialize; 21use serde::Deserialize;
@@ -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
@@ -327,6 +333,7 @@ impl Config {
327 debug: data.hoverActions_enable && data.hoverActions_debug, 333 debug: data.hoverActions_enable && data.hoverActions_debug,
328 goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef, 334 goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef,
329 links_in_hover: data.hoverActions_linksInHover, 335 links_in_hover: data.hoverActions_linksInHover,
336 markdown: true,
330 }; 337 };
331 338
332 log::info!("Config::update() = {:#?}", self); 339 log::info!("Config::update() = {:#?}", self);
@@ -334,6 +341,9 @@ impl Config {
334 341
335 pub fn update_caps(&mut self, caps: &ClientCapabilities) { 342 pub fn update_caps(&mut self, caps: &ClientCapabilities) {
336 if let Some(doc_caps) = caps.text_document.as_ref() { 343 if let Some(doc_caps) = caps.text_document.as_ref() {
344 if let Some(value) = doc_caps.hover.as_ref().and_then(|it| it.content_format.as_ref()) {
345 self.hover.markdown = value.contains(&MarkupKind::Markdown)
346 }
337 if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) { 347 if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) {
338 self.client_caps.location_link = value; 348 self.client_caps.location_link = value;
339 } 349 }
@@ -403,13 +413,21 @@ enum ManifestOrProjectJson {
403} 413}
404 414
405#[derive(Deserialize)] 415#[derive(Deserialize)]
406#[serde(rename_all = "lowercase")] 416#[serde(rename_all = "snake_case")]
407enum MergeBehaviourDef { 417enum MergeBehaviourDef {
408 None, 418 None,
409 Full, 419 Full,
410 Last, 420 Last,
411} 421}
412 422
423#[derive(Deserialize)]
424#[serde(rename_all = "snake_case")]
425enum ImportPrefixDef {
426 Plain,
427 BySelf,
428 ByCrate,
429}
430
413macro_rules! config_data { 431macro_rules! config_data {
414 (struct $name:ident { $($field:ident: $ty:ty = $default:expr,)*}) => { 432 (struct $name:ident { $($field:ident: $ty:ty = $default:expr,)*}) => {
415 #[allow(non_snake_case)] 433 #[allow(non_snake_case)]
@@ -434,6 +452,7 @@ macro_rules! config_data {
434config_data! { 452config_data! {
435 struct ConfigData { 453 struct ConfigData {
436 assist_importMergeBehaviour: MergeBehaviourDef = MergeBehaviourDef::None, 454 assist_importMergeBehaviour: MergeBehaviourDef = MergeBehaviourDef::None,
455 assist_importPrefix: ImportPrefixDef = ImportPrefixDef::Plain,
437 456
438 callInfo_full: bool = true, 457 callInfo_full: bool = true,
439 458
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index e970abb7c..468655f9c 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -618,7 +618,11 @@ pub(crate) fn handle_hover(
618) -> Result<Option<lsp_ext::Hover>> { 618) -> Result<Option<lsp_ext::Hover>> {
619 let _p = profile::span("handle_hover"); 619 let _p = profile::span("handle_hover");
620 let position = from_proto::file_position(&snap, params.text_document_position_params)?; 620 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
621 let info = match snap.analysis.hover(position, snap.config.hover.links_in_hover)? { 621 let info = match snap.analysis.hover(
622 position,
623 snap.config.hover.links_in_hover,
624 snap.config.hover.markdown,
625 )? {
622 None => return Ok(None), 626 None => return Ok(None),
623 Some(info) => info, 627 Some(info) => info,
624 }; 628 };
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 85c661571..bd888f634 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -25,8 +25,9 @@ pub(crate) enum Progress {
25} 25}
26 26
27impl Progress { 27impl Progress {
28 pub(crate) fn percentage(done: usize, total: usize) -> f64 { 28 pub(crate) fn fraction(done: usize, total: usize) -> f64 {
29 (done as f64 / total.max(1) as f64) * 100.0 29 assert!(done <= total);
30 done as f64 / total.max(1) as f64
30 } 31 }
31} 32}
32 33
@@ -43,11 +44,15 @@ impl GlobalState {
43 title: &str, 44 title: &str,
44 state: Progress, 45 state: Progress,
45 message: Option<String>, 46 message: Option<String>,
46 percentage: Option<f64>, 47 fraction: Option<f64>,
47 ) { 48 ) {
48 if !self.config.client_caps.work_done_progress { 49 if !self.config.client_caps.work_done_progress {
49 return; 50 return;
50 } 51 }
52 let percentage = fraction.map(|f| {
53 assert!(0.0 <= f && f <= 1.0);
54 f * 100.0
55 });
51 let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); 56 let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
52 let work_done_progress = match state { 57 let work_done_progress = match state {
53 Progress::Begin => { 58 Progress::Begin => {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index c2d0ac791..4b7ac8224 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -230,7 +230,7 @@ impl GlobalState {
230 "roots scanned", 230 "roots scanned",
231 state, 231 state,
232 Some(format!("{}/{}", n_done, n_total)), 232 Some(format!("{}/{}", n_done, n_total)),
233 Some(Progress::percentage(n_done, n_total)), 233 Some(Progress::fraction(n_done, n_total)),
234 ) 234 )
235 } 235 }
236 } 236 }
diff --git a/crates/syntax/src/parsing/lexer.rs b/crates/syntax/src/parsing/lexer.rs
index f1202113b..7e38c32cc 100644
--- a/crates/syntax/src/parsing/lexer.rs
+++ b/crates/syntax/src/parsing/lexer.rs
@@ -1,10 +1,10 @@
1//! Lexer analyzes raw input string and produces lexemes (tokens). 1//! Lexer analyzes raw input string and produces lexemes (tokens).
2//! It is just a bridge to `rustc_lexer`. 2//! It is just a bridge to `rustc_lexer`.
3 3
4use rustc_lexer::{LiteralKind as LK, RawStrError};
5
6use std::convert::TryInto; 4use std::convert::TryInto;
7 5
6use rustc_lexer::{LiteralKind as LK, RawStrError};
7
8use crate::{ 8use crate::{
9 SyntaxError, 9 SyntaxError,
10 SyntaxKind::{self, *}, 10 SyntaxKind::{self, *},
@@ -61,17 +61,18 @@ pub fn tokenize(text: &str) -> (Vec<Token>, Vec<SyntaxError>) {
61 (tokens, errors) 61 (tokens, errors)
62} 62}
63 63
64/// Returns `SyntaxKind` and `Option<SyntaxError>` of the first token 64/// Returns `SyntaxKind` and `Option<SyntaxError>` if `text` parses as a single token.
65/// encountered at the beginning of the string.
66/// 65///
67/// Returns `None` if the string contains zero *or two or more* tokens. 66/// Returns `None` if the string contains zero *or two or more* tokens.
68/// The token is malformed if the returned error is not `None`. 67/// The token is malformed if the returned error is not `None`.
69/// 68///
70/// Beware that unescape errors are not checked at tokenization time. 69/// Beware that unescape errors are not checked at tokenization time.
71pub fn lex_single_syntax_kind(text: &str) -> Option<(SyntaxKind, Option<SyntaxError>)> { 70pub fn lex_single_syntax_kind(text: &str) -> Option<(SyntaxKind, Option<SyntaxError>)> {
72 lex_first_token(text) 71 let (first_token, err) = lex_first_token(text)?;
73 .filter(|(token, _)| token.len == TextSize::of(text)) 72 if first_token.len != TextSize::of(text) {
74 .map(|(token, error)| (token.kind, error)) 73 return None;
74 }
75 Some((first_token.kind, err))
75} 76}
76 77
77/// The same as `lex_single_syntax_kind()` but returns only `SyntaxKind` and 78/// The same as `lex_single_syntax_kind()` but returns only `SyntaxKind` and
@@ -79,9 +80,11 @@ pub fn lex_single_syntax_kind(text: &str) -> Option<(SyntaxKind, Option<SyntaxEr
79/// 80///
80/// Beware that unescape errors are not checked at tokenization time. 81/// Beware that unescape errors are not checked at tokenization time.
81pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> { 82pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> {
82 lex_first_token(text) 83 let (single_token, err) = lex_single_syntax_kind(text)?;
83 .filter(|(token, error)| !error.is_some() && token.len == TextSize::of(text)) 84 if err.is_some() {
84 .map(|(token, _error)| token.kind) 85 return None;
86 }
87 Some(single_token)
85} 88}
86 89
87/// Returns `SyntaxKind` and `Option<SyntaxError>` of the first token 90/// Returns `SyntaxKind` and `Option<SyntaxError>` of the first token
diff --git a/crates/syntax/src/ptr.rs b/crates/syntax/src/ptr.rs
index ca7957747..d3fb7a5d9 100644
--- a/crates/syntax/src/ptr.rs
+++ b/crates/syntax/src/ptr.rs
@@ -12,6 +12,8 @@ use crate::{AstNode, SyntaxKind, SyntaxNode, TextRange};
12/// specific node across reparses of the same file. 12/// specific node across reparses of the same file.
13#[derive(Debug, Clone, PartialEq, Eq, Hash)] 13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub struct SyntaxNodePtr { 14pub struct SyntaxNodePtr {
15 // Don't expose this field further. At some point, we might want to replace
16 // range with node id.
15 pub(crate) range: TextRange, 17 pub(crate) range: TextRange,
16 kind: SyntaxKind, 18 kind: SyntaxKind,
17} 19}
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 2dddaf09a..0f9a5e8ae 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -3,10 +3,11 @@
3mod block; 3mod block;
4 4
5use crate::{ 5use crate::{
6 ast, match_ast, AstNode, SyntaxError, 6 algo, ast, match_ast, AstNode, SyntaxError,
7 SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST, FN, INT_NUMBER, STRING, TYPE_ALIAS}, 7 SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST, FN, INT_NUMBER, STRING, TYPE_ALIAS},
8 SyntaxNode, SyntaxToken, TextSize, T, 8 SyntaxNode, SyntaxToken, TextSize, T,
9}; 9};
10use rowan::Direction;
10use rustc_lexer::unescape::{ 11use rustc_lexer::unescape::{
11 self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode, 12 self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode,
12}; 13};
@@ -95,6 +96,9 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
95 ast::Visibility(it) => validate_visibility(it, &mut errors), 96 ast::Visibility(it) => validate_visibility(it, &mut errors),
96 ast::RangeExpr(it) => validate_range_expr(it, &mut errors), 97 ast::RangeExpr(it) => validate_range_expr(it, &mut errors),
97 ast::PathSegment(it) => validate_path_keywords(it, &mut errors), 98 ast::PathSegment(it) => validate_path_keywords(it, &mut errors),
99 ast::RefType(it) => validate_trait_object_ref_ty(it, &mut errors),
100 ast::PtrType(it) => validate_trait_object_ptr_ty(it, &mut errors),
101 ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, &mut errors),
98 _ => (), 102 _ => (),
99 } 103 }
100 } 104 }
@@ -301,3 +305,42 @@ fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxErro
301 return true; 305 return true;
302 } 306 }
303} 307}
308
309fn validate_trait_object_ref_ty(ty: ast::RefType, errors: &mut Vec<SyntaxError>) {
310 if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
311 if let Some(err) = validate_trait_object_ty(ty) {
312 errors.push(err);
313 }
314 }
315}
316
317fn validate_trait_object_ptr_ty(ty: ast::PtrType, errors: &mut Vec<SyntaxError>) {
318 if let Some(ast::Type::DynTraitType(ty)) = ty.ty() {
319 if let Some(err) = validate_trait_object_ty(ty) {
320 errors.push(err);
321 }
322 }
323}
324
325fn validate_trait_object_fn_ptr_ret_ty(ty: ast::FnPtrType, errors: &mut Vec<SyntaxError>) {
326 if let Some(ast::Type::DynTraitType(ty)) = ty.ret_type().and_then(|ty| ty.ty()) {
327 if let Some(err) = validate_trait_object_ty(ty) {
328 errors.push(err);
329 }
330 }
331}
332
333fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> {
334 let tbl = ty.type_bound_list()?;
335
336 if tbl.bounds().count() > 1 {
337 let dyn_token = ty.dyn_token()?;
338 let potential_parentheses =
339 algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?;
340 let kind = potential_parentheses.kind();
341 if !matches!(kind, T!['('] | T![<] | T![=]) {
342 return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()));
343 }
344 }
345 None
346}
diff --git a/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast
new file mode 100644
index 000000000..592741cdb
--- /dev/null
+++ b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rast
@@ -0,0 +1,192 @@
1[email protected]
2 [email protected]
3 [email protected] "type"
4 [email protected] " "
5 [email protected]
6 [email protected] "Foo"
7 [email protected]
8 [email protected] "<"
9 [email protected]
10 [email protected] "\'a"
11 [email protected] ">"
12 [email protected] " "
13 [email protected] "="
14 [email protected] " "
15 [email protected]
16 [email protected] "&"
17 [email protected] "\'a"
18 [email protected] " "
19 [email protected]
20 [email protected] "dyn"
21 [email protected] " "
22 [email protected]
23 [email protected]
24 [email protected]
25 [email protected]
26 [email protected]
27 [email protected]
28 [email protected] "Send"
29 [email protected] " "
30 [email protected] "+"
31 [email protected] " "
32 [email protected]
33 [email protected]
34 [email protected]
35 [email protected]
36 [email protected]
37 [email protected] "Sync"
38 [email protected] ";"
39 [email protected] "\n"
40 [email protected]
41 [email protected] "type"
42 [email protected] " "
43 [email protected]
44 [email protected] "Foo"
45 [email protected] " "
46 [email protected] "="
47 [email protected] " "
48 [email protected]
49 [email protected] "*"
50 [email protected] "const"
51 [email protected] " "
52 [email protected]
53 [email protected] "dyn"
54 [email protected] " "
55 [email protected]
56 [email protected]
57 [email protected]
58 [email protected]
59 [email protected]
60 [email protected]
61 [email protected] "Send"
62 [email protected] " "
63 [email protected] "+"
64 [email protected] " "
65 [email protected]
66 [email protected]
67 [email protected]
68 [email protected]
69 [email protected]
70 [email protected] "Sync"
71 [email protected] ";"
72 [email protected] "\n"
73 [email protected]
74 [email protected] "type"
75 [email protected] " "
76 [email protected]
77 [email protected] "Foo"
78 [email protected] " "
79 [email protected] "="
80 [email protected] " "
81 [email protected]
82 [email protected] "fn"
83 [email protected]
84 [email protected] "("
85 [email protected] ")"
86 [email protected] " "
87 [email protected]
88 [email protected] "->"
89 [email protected] " "
90 [email protected]
91 [email protected] "dyn"
92 [email protected] " "
93 [email protected]
94 [email protected]
95 [email protected]
96 [email protected]
97 [email protected]
98 [email protected]
99 [email protected] "Send"
100 [email protected] " "
101 [email protected] "+"
102 [email protected] " "
103 [email protected]
104 [email protected] "\'static"
105 [email protected] ";"
106 [email protected] "\n"
107 [email protected]
108 [email protected] "fn"
109 [email protected] " "
110 [email protected]
111 [email protected] "main"
112 [email protected]
113 [email protected] "("
114 [email protected] ")"
115 [email protected] " "
116 [email protected]
117 [email protected] "{"
118 [email protected] "\n "
119 [email protected]
120 [email protected] "let"
121 [email protected] " "
122 [email protected]
123 [email protected]
124 [email protected] "b"
125 [email protected] " "
126 [email protected] "="
127 [email protected] " "
128 [email protected]
129 [email protected]
130 [email protected] "("
131 [email protected]
132 [email protected] "&"
133 [email protected]
134 [email protected]
135 [email protected]
136 [email protected]
137 [email protected] "a"
138 [email protected] ")"
139 [email protected] " "
140 [email protected] "as"
141 [email protected] " "
142 [email protected]
143 [email protected] "&"
144 [email protected]
145 [email protected] "dyn"
146 [email protected] " "
147 [email protected]
148 [email protected]
149 [email protected]
150 [email protected]
151 [email protected]
152 [email protected]
153 [email protected] "Add"
154 [email protected]
155 [email protected] "<"
156 [email protected]
157 [email protected]
158 [email protected]
159 [email protected]
160 [email protected]
161 [email protected] "Other"
162 [email protected] ","
163 [email protected] " "
164 [email protected]
165 [email protected]
166 [email protected] "Output"
167 [email protected] " "
168 [email protected] "="
169 [email protected] " "
170 [email protected]
171 [email protected]
172 [email protected]
173 [email protected]
174 [email protected] "Addable"
175 [email protected] ">"
176 [email protected] " "
177 [email protected] "+"
178 [email protected] " "
179 [email protected]
180 [email protected]
181 [email protected]
182 [email protected]
183 [email protected]
184 [email protected] "Other"
185 [email protected] ";"
186 [email protected] "\n"
187 [email protected] "}"
188 [email protected] "\n"
189error 19..34: ambiguous `+` in a type
190error 54..69: ambiguous `+` in a type
191error 90..108: ambiguous `+` in a type
192error 143..183: ambiguous `+` in a type
diff --git a/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rs b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rs
new file mode 100644
index 000000000..3a73d81bb
--- /dev/null
+++ b/crates/syntax/test_data/parser/err/0046_ambiguous_trait_object.rs
@@ -0,0 +1,6 @@
1type Foo<'a> = &'a dyn Send + Sync;
2type Foo = *const dyn Send + Sync;
3type Foo = fn() -> dyn Send + 'static;
4fn main() {
5 let b = (&a) as &dyn Add<Other, Output = Addable> + Other;
6}
diff --git a/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast
new file mode 100644
index 000000000..0cd868a83
--- /dev/null
+++ b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rast
@@ -0,0 +1,200 @@
1[email protected]
2 [email protected]
3 [email protected] "type"
4 [email protected] " "
5 [email protected]
6 [email protected] "Foo"
7 [email protected]
8 [email protected] "<"
9 [email protected]
10 [email protected] "\'a"
11 [email protected] ">"
12 [email protected] " "
13 [email protected] "="
14 [email protected] " "
15 [email protected]
16 [email protected] "&"
17 [email protected] "\'a"
18 [email protected] " "
19 [email protected]
20 [email protected] "("
21 [email protected]
22 [email protected] "dyn"
23 [email protected] " "
24 [email protected]
25 [email protected]
26 [email protected]
27 [email protected]
28 [email protected]
29 [email protected]
30 [email protected] "Send"
31 [email protected] " "
32 [email protected] "+"
33 [email protected] " "
34 [email protected]
35 [email protected]
36 [email protected]
37 [email protected]
38 [email protected]
39 [email protected] "Sync"
40 [email protected] ")"
41 [email protected] ";"
42 [email protected] "\n"
43 [email protected]
44 [email protected] "type"
45 [email protected] " "
46 [email protected]
47 [email protected] "Foo"
48 [email protected] " "
49 [email protected] "="
50 [email protected] " "
51 [email protected]
52 [email protected] "*"
53 [email protected] "const"
54 [email protected] " "
55 [email protected]
56 [email protected] "("
57 [email protected]
58 [email protected] "dyn"
59 [email protected] " "
60 [email protected]
61 [email protected]
62 [email protected]
63 [email protected]
64 [email protected]
65 [email protected]
66 [email protected] "Send"
67 [email protected] " "
68 [email protected] "+"
69 [email protected] " "
70 [email protected]
71 [email protected]
72 [email protected]
73 [email protected]
74 [email protected]
75 [email protected] "Sync"
76 [email protected] ")"
77 [email protected] ";"
78 [email protected] "\n"
79 [email protected]
80 [email protected] "type"
81 [email protected] " "
82 [email protected]
83 [email protected] "Foo"
84 [email protected] " "
85 [email protected] "="
86 [email protected] " "
87 [email protected]
88 [email protected] "fn"
89 [email protected]
90 [email protected] "("
91 [email protected] ")"
92 [email protected] " "
93 [email protected]
94 [email protected] "->"
95 [email protected] " "
96 [email protected]
97 [email protected] "("
98 [email protected]
99 [email protected] "dyn"
100 [email protected] " "
101 [email protected]
102 [email protected]
103 [email protected]
104 [email protected]
105 [email protected]
106 [email protected]
107 [email protected] "Send"
108 [email protected] " "
109 [email protected] "+"
110 [email protected] " "
111 [email protected]
112 [email protected] "\'static"
113 [email protected] ")"
114 [email protected] ";"
115 [email protected] "\n"
116 [email protected]
117 [email protected] "fn"
118 [email protected] " "
119 [email protected]
120 [email protected] "main"
121 [email protected]
122 [email protected] "("
123 [email protected] ")"
124 [email protected] " "
125 [email protected]
126 [email protected] "{"
127 [email protected] "\n "
128 [email protected]
129 [email protected] "let"
130 [email protected] " "
131 [email protected]
132 [email protected]
133 [email protected] "b"
134 [email protected] " "
135 [email protected] "="
136 [email protected] " "
137 [email protected]
138 [email protected]
139 [email protected] "("
140 [email protected]
141 [email protected] "&"
142 [email protected]
143 [email protected]
144 [email protected]
145 [email protected]
146 [email protected] "a"
147 [email protected] ")"
148 [email protected] " "
149 [email protected] "as"
150 [email protected] " "
151 [email protected]
152 [email protected] "&"
153 [email protected]
154 [email protected] "("
155 [email protected]
156 [email protected] "dyn"
157 [email protected] " "
158 [email protected]
159 [email protected]
160 [email protected]
161 [email protected]
162 [email protected]
163 [email protected]
164 [email protected] "Add"
165 [email protected]
166 [email protected] "<"
167 [email protected]
168 [email protected]
169 [email protected]
170 [email protected]
171 [email protected]
172 [email protected] "Other"
173 [email protected] ","
174 [email protected] " "
175 [email protected]
176 [email protected]
177 [email protected] "Output"
178 [email protected] " "
179 [email protected] "="
180 [email protected] " "
181 [email protected]
182 [email protected]
183 [email protected]
184 [email protected]
185 [email protected] "Addable"
186 [email protected] ">"
187 [email protected] " "
188 [email protected] "+"
189 [email protected] " "
190 [email protected]
191 [email protected]
192 [email protected]
193 [email protected]
194 [email protected]
195 [email protected] "Other"
196 [email protected] ")"
197 [email protected] ";"
198 [email protected] "\n"
199 [email protected] "}"
200 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rs b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rs
new file mode 100644
index 000000000..97eb79c48
--- /dev/null
+++ b/crates/syntax/test_data/parser/ok/0069_multi_trait_object.rs
@@ -0,0 +1,6 @@
1type Foo<'a> = &'a (dyn Send + Sync);
2type Foo = *const (dyn Send + Sync);
3type Foo = fn() -> (dyn Send + 'static);
4fn main() {
5 let b = (&a) as &(dyn Add<Other, Output = Addable> + Other);
6}