aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock33
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs120
-rw-r--r--crates/assists/src/handlers/remove_unused_param.rs83
-rw-r--r--crates/assists/src/utils/insert_use.rs96
-rw-r--r--crates/project_model/src/cargo_workspace.rs3
-rw-r--r--crates/project_model/src/lib.rs271
-rw-r--r--crates/rust-analyzer/src/config.rs16
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt27
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt9
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt36
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt9
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt9
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt18
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt18
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt18
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt18
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt27
-rw-r--r--crates/rust-analyzer/src/reload.rs4
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast.rs19
-rw-r--r--crates/syntax/src/ast/token_ext.rs51
-rw-r--r--crates/syntax/src/parsing/text_tree_sink.rs27
-rw-r--r--crates/syntax/test_data/parser/ok/0037_mod.rast6
-rw-r--r--editors/code/package.json8
24 files changed, 731 insertions, 197 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c5645b2d2..494011068 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -139,9 +139,9 @@ dependencies = [
139 139
140[[package]] 140[[package]]
141name = "cc" 141name = "cc"
142version = "1.0.61" 142version = "1.0.62"
143source = "registry+https://github.com/rust-lang/crates.io-index" 143source = "registry+https://github.com/rust-lang/crates.io-index"
144checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" 144checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40"
145 145
146[[package]] 146[[package]]
147name = "cfg" 147name = "cfg"
@@ -398,11 +398,11 @@ dependencies = [
398 398
399[[package]] 399[[package]]
400name = "filetime" 400name = "filetime"
401version = "0.2.12" 401version = "0.2.13"
402source = "registry+https://github.com/rust-lang/crates.io-index" 402source = "registry+https://github.com/rust-lang/crates.io-index"
403checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e" 403checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe"
404dependencies = [ 404dependencies = [
405 "cfg-if 0.1.10", 405 "cfg-if 1.0.0",
406 "libc", 406 "libc",
407 "redox_syscall", 407 "redox_syscall",
408 "winapi 0.3.9", 408 "winapi 0.3.9",
@@ -439,6 +439,16 @@ dependencies = [
439] 439]
440 440
441[[package]] 441[[package]]
442name = "form_urlencoded"
443version = "1.0.0"
444source = "registry+https://github.com/rust-lang/crates.io-index"
445checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00"
446dependencies = [
447 "matches",
448 "percent-encoding",
449]
450
451[[package]]
442name = "fsevent" 452name = "fsevent"
443version = "2.0.2" 453version = "2.0.2"
444source = "registry+https://github.com/rust-lang/crates.io-index" 454source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -715,9 +725,9 @@ dependencies = [
715 725
716[[package]] 726[[package]]
717name = "inotify-sys" 727name = "inotify-sys"
718version = "0.1.3" 728version = "0.1.4"
719source = "registry+https://github.com/rust-lang/crates.io-index" 729source = "registry+https://github.com/rust-lang/crates.io-index"
720checksum = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" 730checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55"
721dependencies = [ 731dependencies = [
722 "libc", 732 "libc",
723] 733]
@@ -1384,9 +1394,9 @@ dependencies = [
1384 1394
1385[[package]] 1395[[package]]
1386name = "rustc-ap-rustc_lexer" 1396name = "rustc-ap-rustc_lexer"
1387version = "686.0.0" 1397version = "688.0.0"
1388source = "registry+https://github.com/rust-lang/crates.io-index" 1398source = "registry+https://github.com/rust-lang/crates.io-index"
1389checksum = "a5b04cd2159495584d976d501c5394498470c2e94e4f0cebb8186562d407a678" 1399checksum = "ebbdcc99bd015349093fcbae4780fda21416fec5d8843acfb3d1733e130cd4db"
1390dependencies = [ 1400dependencies = [
1391 "unicode-xid", 1401 "unicode-xid",
1392] 1402]
@@ -1889,10 +1899,11 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
1889 1899
1890[[package]] 1900[[package]]
1891name = "url" 1901name = "url"
1892version = "2.1.1" 1902version = "2.2.0"
1893source = "registry+https://github.com/rust-lang/crates.io-index" 1903source = "registry+https://github.com/rust-lang/crates.io-index"
1894checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" 1904checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
1895dependencies = [ 1905dependencies = [
1906 "form_urlencoded",
1896 "idna", 1907 "idna",
1897 "matches", 1908 "matches",
1898 "percent-encoding", 1909 "percent-encoding",
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 84662d832..067afabf2 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -5,10 +5,9 @@ use hir::{AsName, EnumVariant, Module, ModuleDef, Name};
5use ide_db::{defs::Definition, search::Reference, RootDatabase}; 5use ide_db::{defs::Definition, search::Reference, RootDatabase};
6use rustc_hash::{FxHashMap, FxHashSet}; 6use rustc_hash::{FxHashMap, FxHashSet};
7use syntax::{ 7use syntax::{
8 algo::find_node_at_offset, 8 algo::{find_node_at_offset, SyntaxRewriter},
9 algo::SyntaxRewriter, 9 ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner},
10 ast::{self, edit::IndentLevel, make, ArgListOwner, AstNode, NameOwner, VisibilityOwner}, 10 SourceFile, SyntaxElement, SyntaxNode, T,
11 SourceFile, SyntaxElement,
12}; 11};
13 12
14use crate::{ 13use crate::{
@@ -130,17 +129,21 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &En
130fn insert_import( 129fn insert_import(
131 ctx: &AssistContext, 130 ctx: &AssistContext,
132 rewriter: &mut SyntaxRewriter, 131 rewriter: &mut SyntaxRewriter,
133 path: &ast::PathExpr, 132 scope_node: &SyntaxNode,
134 module: &Module, 133 module: &Module,
135 enum_module_def: &ModuleDef, 134 enum_module_def: &ModuleDef,
136 variant_hir_name: &Name, 135 variant_hir_name: &Name,
137) -> Option<()> { 136) -> Option<()> {
138 let db = ctx.db(); 137 let db = ctx.db();
139 let mod_path = module.find_use_path(db, enum_module_def.clone()); 138 let mod_path = module.find_use_path_prefixed(
139 db,
140 enum_module_def.clone(),
141 ctx.config.insert_use.prefix_kind,
142 );
140 if let Some(mut mod_path) = mod_path { 143 if let Some(mut mod_path) = mod_path {
141 mod_path.segments.pop(); 144 mod_path.segments.pop();
142 mod_path.segments.push(variant_hir_name.clone()); 145 mod_path.segments.push(variant_hir_name.clone());
143 let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; 146 let scope = ImportScope::find_insert_use_container(scope_node, ctx)?;
144 147
145 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); 148 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge);
146 } 149 }
@@ -204,27 +207,31 @@ fn update_reference(
204 variant_hir_name: &Name, 207 variant_hir_name: &Name,
205 visited_modules_set: &mut FxHashSet<Module>, 208 visited_modules_set: &mut FxHashSet<Module>,
206) -> Option<()> { 209) -> Option<()> {
207 let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>( 210 let offset = reference.file_range.range.start();
208 source_file.syntax(), 211 let (segment, expr) = if let Some(path_expr) =
209 reference.file_range.range.start(), 212 find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset)
210 )?; 213 {
211 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; 214 // tuple variant
212 let list = call.arg_list()?; 215 (path_expr.path()?.segment()?, path_expr.syntax().parent()?.clone())
213 let segment = path_expr.path()?.segment()?; 216 } else if let Some(record_expr) =
214 let module = ctx.sema.scope(&path_expr.syntax()).module()?; 217 find_node_at_offset::<ast::RecordExpr>(source_file.syntax(), offset)
218 {
219 // record variant
220 (record_expr.path()?.segment()?, record_expr.syntax().clone())
221 } else {
222 return None;
223 };
224
225 let module = ctx.sema.scope(&expr).module()?;
215 if !visited_modules_set.contains(&module) { 226 if !visited_modules_set.contains(&module) {
216 if insert_import(ctx, rewriter, &path_expr, &module, enum_module_def, variant_hir_name) 227 if insert_import(ctx, rewriter, &expr, &module, enum_module_def, variant_hir_name).is_some()
217 .is_some()
218 { 228 {
219 visited_modules_set.insert(module); 229 visited_modules_set.insert(module);
220 } 230 }
221 } 231 }
222 232 rewriter.insert_after(segment.syntax(), &make::token(T!['(']));
223 let lparen = syntax::SyntaxElement::from(list.l_paren_token()?); 233 rewriter.insert_after(segment.syntax(), segment.syntax());
224 let rparen = syntax::SyntaxElement::from(list.r_paren_token()?); 234 rewriter.insert_after(&expr, &make::token(T![')']));
225 rewriter.insert_after(&lparen, segment.syntax());
226 rewriter.insert_after(&lparen, &lparen);
227 rewriter.insert_before(&rparen, &rparen);
228 Some(()) 235 Some(())
229} 236}
230 237
@@ -320,7 +327,7 @@ fn another_fn() {
320 r#"use my_mod::my_other_mod::MyField; 327 r#"use my_mod::my_other_mod::MyField;
321 328
322mod my_mod { 329mod my_mod {
323 use my_other_mod::MyField; 330 use self::my_other_mod::MyField;
324 331
325 fn another_fn() { 332 fn another_fn() {
326 let m = my_other_mod::MyEnum::MyField(MyField(1, 1)); 333 let m = my_other_mod::MyEnum::MyField(MyField(1, 1));
@@ -346,6 +353,33 @@ fn another_fn() {
346 } 353 }
347 354
348 #[test] 355 #[test]
356 fn extract_record_fix_references() {
357 check_assist(
358 extract_struct_from_enum_variant,
359 r#"
360enum E {
361 <|>V { i: i32, j: i32 }
362}
363
364fn f() {
365 let e = E::V { i: 9, j: 2 };
366}
367"#,
368 r#"
369struct V{ pub i: i32, pub j: i32 }
370
371enum E {
372 V(V)
373}
374
375fn f() {
376 let e = E::V(V { i: 9, j: 2 });
377}
378"#,
379 )
380 }
381
382 #[test]
349 fn test_several_files() { 383 fn test_several_files() {
350 check_assist( 384 check_assist(
351 extract_struct_from_enum_variant, 385 extract_struct_from_enum_variant,
@@ -372,9 +406,7 @@ enum E {
372mod foo; 406mod foo;
373 407
374//- /foo.rs 408//- /foo.rs
375use V; 409use crate::{E, V};
376
377use crate::E;
378fn f() { 410fn f() {
379 let e = E::V(V(9, 2)); 411 let e = E::V(V(9, 2));
380} 412}
@@ -384,7 +416,6 @@ fn f() {
384 416
385 #[test] 417 #[test]
386 fn test_several_files_record() { 418 fn test_several_files_record() {
387 // FIXME: this should fix the usage as well!
388 check_assist( 419 check_assist(
389 extract_struct_from_enum_variant, 420 extract_struct_from_enum_variant,
390 r#" 421 r#"
@@ -401,6 +432,7 @@ fn f() {
401} 432}
402"#, 433"#,
403 r#" 434 r#"
435//- /main.rs
404struct V{ pub i: i32, pub j: i32 } 436struct V{ pub i: i32, pub j: i32 }
405 437
406enum E { 438enum E {
@@ -408,10 +440,42 @@ enum E {
408} 440}
409mod foo; 441mod foo;
410 442
443//- /foo.rs
444use crate::{E, V};
445fn f() {
446 let e = E::V(V { i: 9, j: 2 });
447}
411"#, 448"#,
412 ) 449 )
413 } 450 }
414 451
452 #[test]
453 fn test_extract_struct_record_nested_call_exp() {
454 check_assist(
455 extract_struct_from_enum_variant,
456 r#"
457enum A { <|>One { a: u32, b: u32 } }
458
459struct B(A);
460
461fn foo() {
462 let _ = B(A::One { a: 1, b: 2 });
463}
464"#,
465 r#"
466struct One{ pub a: u32, pub b: u32 }
467
468enum A { One(One) }
469
470struct B(A);
471
472fn foo() {
473 let _ = B(A::One(One { a: 1, b: 2 }));
474}
475"#,
476 );
477 }
478
415 fn check_not_applicable(ra_fixture: &str) { 479 fn check_not_applicable(ra_fixture: &str) {
416 let fixture = 480 let fixture =
417 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); 481 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs
index 5fccca54b..1ff5e92b0 100644
--- a/crates/assists/src/handlers/remove_unused_param.rs
+++ b/crates/assists/src/handlers/remove_unused_param.rs
@@ -73,7 +73,8 @@ fn process_usage(
73 let source_file = ctx.sema.parse(usage.file_range.file_id); 73 let source_file = ctx.sema.parse(usage.file_range.file_id);
74 let call_expr: ast::CallExpr = 74 let call_expr: ast::CallExpr =
75 find_node_at_range(source_file.syntax(), usage.file_range.range)?; 75 find_node_at_range(source_file.syntax(), usage.file_range.range)?;
76 if call_expr.expr()?.syntax().text_range() != usage.file_range.range { 76 let call_expr_range = call_expr.expr()?.syntax().text_range();
77 if !call_expr_range.contains_range(usage.file_range.range) {
77 return None; 78 return None;
78 } 79 }
79 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; 80 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
@@ -118,6 +119,53 @@ fn b() { foo(9, ) }
118 } 119 }
119 120
120 #[test] 121 #[test]
122 fn remove_unused_qualified_call() {
123 check_assist(
124 remove_unused_param,
125 r#"
126mod bar { pub fn foo(x: i32, <|>y: i32) { x; } }
127fn b() { bar::foo(9, 2) }
128"#,
129 r#"
130mod bar { pub fn foo(x: i32) { x; } }
131fn b() { bar::foo(9) }
132"#,
133 );
134 }
135
136 #[test]
137 fn remove_unused_turbofished_func() {
138 check_assist(
139 remove_unused_param,
140 r#"
141pub fn foo<T>(x: T, <|>y: i32) { x; }
142fn b() { foo::<i32>(9, 2) }
143"#,
144 r#"
145pub fn foo<T>(x: T) { x; }
146fn b() { foo::<i32>(9) }
147"#,
148 );
149 }
150
151 #[test]
152 fn remove_unused_generic_unused_param_func() {
153 check_assist(
154 remove_unused_param,
155 r#"
156pub fn foo<T>(x: i32, <|>y: T) { x; }
157fn b() { foo::<i32>(9, 2) }
158fn b2() { foo(9, 2) }
159"#,
160 r#"
161pub fn foo<T>(x: i32) { x; }
162fn b() { foo::<i32>(9) }
163fn b2() { foo(9) }
164"#,
165 );
166 }
167
168 #[test]
121 fn keep_used() { 169 fn keep_used() {
122 mark::check!(keep_used); 170 mark::check!(keep_used);
123 check_assist_not_applicable( 171 check_assist_not_applicable(
@@ -128,4 +176,37 @@ fn main() { foo(9, 2) }
128"#, 176"#,
129 ); 177 );
130 } 178 }
179
180 #[test]
181 fn remove_across_files() {
182 check_assist(
183 remove_unused_param,
184 r#"
185//- /main.rs
186fn foo(x: i32, <|>y: i32) { x; }
187
188mod foo;
189
190//- /foo.rs
191use super::foo;
192
193fn bar() {
194 let _ = foo(1, 2);
195}
196"#,
197 r#"
198//- /main.rs
199fn foo(x: i32) { x; }
200
201mod foo;
202
203//- /foo.rs
204use super::foo;
205
206fn bar() {
207 let _ = foo(1);
208}
209"#,
210 )
211 }
131} 212}
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index 84a0dffdd..af3fc96b6 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/assists/src/utils/insert_use.rs
@@ -9,7 +9,7 @@ use syntax::{
9 edit::{AstNodeEdit, IndentLevel}, 9 edit::{AstNodeEdit, IndentLevel},
10 make, AstNode, PathSegmentKind, VisibilityOwner, 10 make, AstNode, PathSegmentKind, VisibilityOwner,
11 }, 11 },
12 InsertPosition, SyntaxElement, SyntaxNode, 12 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
13}; 13};
14use test_utils::mark; 14use test_utils::mark;
15 15
@@ -63,27 +63,30 @@ impl ImportScope {
63 } 63 }
64 } 64 }
65 65
66 fn insert_pos_after_inner_attribute(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) { 66 fn insert_pos_after_last_inner_element(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
67 // check if the scope has inner attributes, we dont want to insert in front of them 67 self.as_syntax_node()
68 match self 68 .children_with_tokens()
69 .as_syntax_node() 69 .filter(|child| match child {
70 .children() 70 NodeOrToken::Node(node) => is_inner_attribute(node.clone()),
71 // no flat_map here cause we want to short circuit the iterator 71 NodeOrToken::Token(token) => is_inner_comment(token.clone()),
72 .map(ast::Attr::cast)
73 .take_while(|attr| {
74 attr.as_ref().map(|attr| attr.kind() == ast::AttrKind::Inner).unwrap_or(false)
75 }) 72 })
76 .last() 73 .last()
77 .flatten() 74 .map(|last_inner_element| {
78 { 75 (InsertPosition::After(last_inner_element.into()), AddBlankLine::BeforeTwice)
79 Some(attr) => { 76 })
80 (InsertPosition::After(attr.syntax().clone().into()), AddBlankLine::BeforeTwice) 77 .unwrap_or_else(|| self.first_insert_pos())
81 }
82 None => self.first_insert_pos(),
83 }
84 } 78 }
85} 79}
86 80
81fn is_inner_attribute(node: SyntaxNode) -> bool {
82 ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner)
83}
84
85fn is_inner_comment(token: SyntaxToken) -> bool {
86 ast::Comment::cast(token).and_then(|comment| comment.kind().doc)
87 == Some(ast::CommentPlacement::Inner)
88}
89
87/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. 90/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
88pub(crate) fn insert_use<'a>( 91pub(crate) fn insert_use<'a>(
89 scope: &ImportScope, 92 scope: &ImportScope,
@@ -558,7 +561,7 @@ fn find_insert_position(
558 (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice) 561 (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice)
559 } 562 }
560 // there are no imports in this file at all 563 // there are no imports in this file at all
561 None => scope.insert_pos_after_inner_attribute(), 564 None => scope.insert_pos_after_last_inner_element(),
562 }, 565 },
563 } 566 }
564 } 567 }
@@ -830,12 +833,67 @@ use foo::bar;",
830 "foo::bar", 833 "foo::bar",
831 r"#![allow(unused_imports)] 834 r"#![allow(unused_imports)]
832 835
836#![no_std]
833fn main() {}", 837fn main() {}",
834 r"#![allow(unused_imports)] 838 r"#![allow(unused_imports)]
835 839
836use foo::bar; 840#![no_std]
837 841
842use foo::bar;
838fn main() {}", 843fn main() {}",
844 );
845 }
846
847 #[test]
848 fn inserts_after_single_line_inner_comments() {
849 check_none(
850 "foo::bar::Baz",
851 "//! Single line inner comments do not allow any code before them.",
852 r#"//! Single line inner comments do not allow any code before them.
853
854use foo::bar::Baz;"#,
855 );
856 }
857
858 #[test]
859 fn inserts_after_multiline_inner_comments() {
860 check_none(
861 "foo::bar::Baz",
862 r#"/*! Multiline inner comments do not allow any code before them. */
863
864/*! Still an inner comment, cannot place any code before. */
865fn main() {}"#,
866 r#"/*! Multiline inner comments do not allow any code before them. */
867
868/*! Still an inner comment, cannot place any code before. */
869
870use foo::bar::Baz;
871fn main() {}"#,
872 )
873 }
874
875 #[test]
876 fn inserts_after_all_inner_items() {
877 check_none(
878 "foo::bar::Baz",
879 r#"#![allow(unused_imports)]
880/*! Multiline line comment 2 */
881
882
883//! Single line comment 1
884#![no_std]
885//! Single line comment 2
886fn main() {}"#,
887 r#"#![allow(unused_imports)]
888/*! Multiline line comment 2 */
889
890
891//! Single line comment 1
892#![no_std]
893//! Single line comment 2
894
895use foo::bar::Baz;
896fn main() {}"#,
839 ) 897 )
840 } 898 }
841 899
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index d5f6a4025..608a031d4 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -64,6 +64,9 @@ pub struct CargoConfig {
64 64
65 /// rustc target 65 /// rustc target
66 pub target: Option<String>, 66 pub target: Option<String>,
67
68 /// rustc private crate source
69 pub rustc_source: Option<AbsPathBuf>,
67} 70}
68 71
69pub type Package = Idx<PackageData>; 72pub type Package = Idx<PackageData>;
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index e92cfea59..4531b1928 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -9,6 +9,7 @@ use std::{
9 fmt, 9 fmt,
10 fs::{self, read_dir, ReadDir}, 10 fs::{self, read_dir, ReadDir},
11 io, 11 io,
12 path::Component,
12 process::Command, 13 process::Command,
13}; 14};
14 15
@@ -31,7 +32,7 @@ pub use proc_macro_api::ProcMacroClient;
31#[derive(Clone, Eq, PartialEq)] 32#[derive(Clone, Eq, PartialEq)]
32pub enum ProjectWorkspace { 33pub enum ProjectWorkspace {
33 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 34 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
34 Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, 35 Cargo { cargo: CargoWorkspace, sysroot: Sysroot, rustc: Option<CargoWorkspace> },
35 /// Project workspace was manually specified using a `rust-project.json` file. 36 /// Project workspace was manually specified using a `rust-project.json` file.
36 Json { project: ProjectJson, sysroot: Option<Sysroot> }, 37 Json { project: ProjectJson, sysroot: Option<Sysroot> },
37} 38}
@@ -39,10 +40,14 @@ pub enum ProjectWorkspace {
39impl fmt::Debug for ProjectWorkspace { 40impl fmt::Debug for ProjectWorkspace {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self { 42 match self {
42 ProjectWorkspace::Cargo { cargo, sysroot } => f 43 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => f
43 .debug_struct("Cargo") 44 .debug_struct("Cargo")
44 .field("n_packages", &cargo.packages().len()) 45 .field("n_packages", &cargo.packages().len())
45 .field("n_sysroot_crates", &sysroot.crates().len()) 46 .field("n_sysroot_crates", &sysroot.crates().len())
47 .field(
48 "n_rustc_compiler_crates",
49 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
50 )
46 .finish(), 51 .finish(),
47 ProjectWorkspace::Json { project, sysroot } => { 52 ProjectWorkspace::Json { project, sysroot } => {
48 let mut debug_struct = f.debug_struct("Json"); 53 let mut debug_struct = f.debug_struct("Json");
@@ -200,7 +205,19 @@ impl ProjectWorkspace {
200 } else { 205 } else {
201 Sysroot::default() 206 Sysroot::default()
202 }; 207 };
203 ProjectWorkspace::Cargo { cargo, sysroot } 208
209 let rustc = if let Some(rustc_dir) = &cargo_config.rustc_source {
210 Some(
211 CargoWorkspace::from_cargo_metadata(&rustc_dir, cargo_config)
212 .with_context(|| {
213 format!("Failed to read Cargo metadata for Rust sources")
214 })?,
215 )
216 } else {
217 None
218 };
219
220 ProjectWorkspace::Cargo { cargo, sysroot, rustc }
204 } 221 }
205 }; 222 };
206 223
@@ -238,31 +255,43 @@ impl ProjectWorkspace {
238 }) 255 })
239 })) 256 }))
240 .collect::<Vec<_>>(), 257 .collect::<Vec<_>>(),
241 ProjectWorkspace::Cargo { cargo, sysroot } => cargo 258 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
242 .packages() 259 let roots = cargo
243 .map(|pkg| { 260 .packages()
244 let is_member = cargo[pkg].is_member; 261 .map(|pkg| {
245 let pkg_root = cargo[pkg].root().to_path_buf(); 262 let is_member = cargo[pkg].is_member;
246 263 let pkg_root = cargo[pkg].root().to_path_buf();
247 let mut include = vec![pkg_root.clone()]; 264
248 include.extend(cargo[pkg].out_dir.clone()); 265 let mut include = vec![pkg_root.clone()];
249 266 include.extend(cargo[pkg].out_dir.clone());
250 let mut exclude = vec![pkg_root.join(".git")]; 267
251 if is_member { 268 let mut exclude = vec![pkg_root.join(".git")];
252 exclude.push(pkg_root.join("target")); 269 if is_member {
253 } else { 270 exclude.push(pkg_root.join("target"));
254 exclude.push(pkg_root.join("tests")); 271 } else {
255 exclude.push(pkg_root.join("examples")); 272 exclude.push(pkg_root.join("tests"));
256 exclude.push(pkg_root.join("benches")); 273 exclude.push(pkg_root.join("examples"));
257 } 274 exclude.push(pkg_root.join("benches"));
258 PackageRoot { is_member, include, exclude } 275 }
259 }) 276 PackageRoot { is_member, include, exclude }
260 .chain(sysroot.crates().map(|krate| PackageRoot { 277 })
261 is_member: false, 278 .chain(sysroot.crates().map(|krate| PackageRoot {
262 include: vec![sysroot[krate].root_dir().to_path_buf()], 279 is_member: false,
263 exclude: Vec::new(), 280 include: vec![sysroot[krate].root_dir().to_path_buf()],
264 })) 281 exclude: Vec::new(),
265 .collect(), 282 }));
283 if let Some(rustc_packages) = rustc {
284 roots
285 .chain(rustc_packages.packages().map(|krate| PackageRoot {
286 is_member: false,
287 include: vec![rustc_packages[krate].root().to_path_buf()],
288 exclude: Vec::new(),
289 }))
290 .collect()
291 } else {
292 roots.collect()
293 }
294 }
266 } 295 }
267 } 296 }
268 297
@@ -273,7 +302,7 @@ impl ProjectWorkspace {
273 .filter_map(|(_, krate)| krate.proc_macro_dylib_path.as_ref()) 302 .filter_map(|(_, krate)| krate.proc_macro_dylib_path.as_ref())
274 .cloned() 303 .cloned()
275 .collect(), 304 .collect(),
276 ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo 305 ProjectWorkspace::Cargo { cargo, sysroot: _sysroot, rustc: _rustc_crates } => cargo
277 .packages() 306 .packages()
278 .filter_map(|pkg| cargo[pkg].proc_macro_dylib_path.as_ref()) 307 .filter_map(|pkg| cargo[pkg].proc_macro_dylib_path.as_ref())
279 .cloned() 308 .cloned()
@@ -284,8 +313,9 @@ impl ProjectWorkspace {
284 pub fn n_packages(&self) -> usize { 313 pub fn n_packages(&self) -> usize {
285 match self { 314 match self {
286 ProjectWorkspace::Json { project, .. } => project.n_crates(), 315 ProjectWorkspace::Json { project, .. } => project.n_crates(),
287 ProjectWorkspace::Cargo { cargo, sysroot } => { 316 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
288 cargo.packages().len() + sysroot.crates().len() 317 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len());
318 cargo.packages().len() + sysroot.crates().len() + rustc_package_len
289 } 319 }
290 } 320 }
291 } 321 }
@@ -365,7 +395,7 @@ impl ProjectWorkspace {
365 } 395 }
366 } 396 }
367 } 397 }
368 ProjectWorkspace::Cargo { cargo, sysroot } => { 398 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
369 let (public_deps, libproc_macro) = 399 let (public_deps, libproc_macro) =
370 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); 400 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load);
371 401
@@ -373,50 +403,25 @@ impl ProjectWorkspace {
373 cfg_options.extend(get_rustc_cfg_options(target)); 403 cfg_options.extend(get_rustc_cfg_options(target));
374 404
375 let mut pkg_to_lib_crate = FxHashMap::default(); 405 let mut pkg_to_lib_crate = FxHashMap::default();
376 let mut pkg_crates = FxHashMap::default();
377 406
378 // Add test cfg for non-sysroot crates 407 // Add test cfg for non-sysroot crates
379 cfg_options.insert_atom("test".into()); 408 cfg_options.insert_atom("test".into());
380 cfg_options.insert_atom("debug_assertions".into()); 409 cfg_options.insert_atom("debug_assertions".into());
381 410
411 let mut pkg_crates = FxHashMap::default();
412
382 // Next, create crates for each package, target pair 413 // Next, create crates for each package, target pair
383 for pkg in cargo.packages() { 414 for pkg in cargo.packages() {
384 let mut lib_tgt = None; 415 let mut lib_tgt = None;
385 for &tgt in cargo[pkg].targets.iter() { 416 for &tgt in cargo[pkg].targets.iter() {
386 let root = cargo[tgt].root.as_path(); 417 if let Some(crate_id) = add_target_crate_root(
387 if let Some(file_id) = load(root) { 418 &mut crate_graph,
388 let edition = cargo[pkg].edition; 419 &cargo[pkg],
389 let cfg_options = { 420 &cargo[tgt],
390 let mut opts = cfg_options.clone(); 421 &cfg_options,
391 for feature in cargo[pkg].features.iter() { 422 proc_macro_client,
392 opts.insert_key_value("feature".into(), feature.into()); 423 load,
393 } 424 ) {
394 opts.extend(cargo[pkg].cfgs.iter().cloned());
395 opts
396 };
397 let mut env = Env::default();
398 if let Some(out_dir) = &cargo[pkg].out_dir {
399 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
400 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
401 env.set("OUT_DIR", out_dir);
402 }
403 }
404 let proc_macro = cargo[pkg]
405 .proc_macro_dylib_path
406 .as_ref()
407 .map(|it| proc_macro_client.by_dylib_path(&it))
408 .unwrap_or_default();
409
410 let display_name =
411 CrateDisplayName::from_canonical_name(cargo[pkg].name.clone());
412 let crate_id = crate_graph.add_crate_root(
413 file_id,
414 edition,
415 Some(display_name),
416 cfg_options,
417 env,
418 proc_macro.clone(),
419 );
420 if cargo[tgt].kind == TargetKind::Lib { 425 if cargo[tgt].kind == TargetKind::Lib {
421 lib_tgt = Some((crate_id, cargo[tgt].name.clone())); 426 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
422 pkg_to_lib_crate.insert(pkg, crate_id); 427 pkg_to_lib_crate.insert(pkg, crate_id);
@@ -484,6 +489,92 @@ impl ProjectWorkspace {
484 } 489 }
485 } 490 }
486 } 491 }
492
493 let mut rustc_pkg_crates = FxHashMap::default();
494
495 // If the user provided a path to rustc sources, we add all the rustc_private crates
496 // and create dependencies on them for the crates in the current workspace
497 if let Some(rustc_workspace) = rustc {
498 for pkg in rustc_workspace.packages() {
499 for &tgt in rustc_workspace[pkg].targets.iter() {
500 if rustc_workspace[tgt].kind != TargetKind::Lib {
501 continue;
502 }
503 // Exclude alloc / core / std
504 if rustc_workspace[tgt]
505 .root
506 .components()
507 .any(|c| c == Component::Normal("library".as_ref()))
508 {
509 continue;
510 }
511
512 if let Some(crate_id) = add_target_crate_root(
513 &mut crate_graph,
514 &rustc_workspace[pkg],
515 &rustc_workspace[tgt],
516 &cfg_options,
517 proc_macro_client,
518 load,
519 ) {
520 pkg_to_lib_crate.insert(pkg, crate_id);
521 // Add dependencies on the core / std / alloc for rustc
522 for (name, krate) in public_deps.iter() {
523 if let Err(_) =
524 crate_graph.add_dep(crate_id, name.clone(), *krate)
525 {
526 log::error!(
527 "cyclic dependency on {} for {}",
528 name,
529 &cargo[pkg].name
530 )
531 }
532 }
533 rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
534 }
535 }
536 }
537 // Now add a dep edge from all targets of upstream to the lib
538 // target of downstream.
539 for pkg in rustc_workspace.packages() {
540 for dep in rustc_workspace[pkg].dependencies.iter() {
541 let name = CrateName::new(&dep.name).unwrap();
542 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
543 for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
544 if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
545 log::error!(
546 "cyclic dependency {} -> {}",
547 &rustc_workspace[pkg].name,
548 &rustc_workspace[dep.pkg].name
549 )
550 }
551 }
552 }
553 }
554 }
555
556 // Add dependencies for all the crates of the current workspace to rustc_private libraries
557 for dep in rustc_workspace.packages() {
558 let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
559
560 if let Some(&to) = pkg_to_lib_crate.get(&dep) {
561 for pkg in cargo.packages() {
562 if !cargo[pkg].is_member {
563 continue;
564 }
565 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
566 if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
567 log::error!(
568 "cyclic dependency {} -> {}",
569 &cargo[pkg].name,
570 &rustc_workspace[dep].name
571 )
572 }
573 }
574 }
575 }
576 }
577 }
487 } 578 }
488 } 579 }
489 if crate_graph.patch_cfg_if() { 580 if crate_graph.patch_cfg_if() {
@@ -537,6 +628,52 @@ fn utf8_stdout(mut cmd: Command) -> Result<String> {
537 Ok(stdout.trim().to_string()) 628 Ok(stdout.trim().to_string())
538} 629}
539 630
631fn add_target_crate_root(
632 crate_graph: &mut CrateGraph,
633 pkg: &cargo_workspace::PackageData,
634 tgt: &cargo_workspace::TargetData,
635 cfg_options: &CfgOptions,
636 proc_macro_client: &ProcMacroClient,
637 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
638) -> Option<CrateId> {
639 let root = tgt.root.as_path();
640 if let Some(file_id) = load(root) {
641 let edition = pkg.edition;
642 let cfg_options = {
643 let mut opts = cfg_options.clone();
644 for feature in pkg.features.iter() {
645 opts.insert_key_value("feature".into(), feature.into());
646 }
647 opts.extend(pkg.cfgs.iter().cloned());
648 opts
649 };
650 let mut env = Env::default();
651 if let Some(out_dir) = &pkg.out_dir {
652 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
653 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
654 env.set("OUT_DIR", out_dir);
655 }
656 }
657 let proc_macro = pkg
658 .proc_macro_dylib_path
659 .as_ref()
660 .map(|it| proc_macro_client.by_dylib_path(&it))
661 .unwrap_or_default();
662
663 let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
664 let crate_id = crate_graph.add_crate_root(
665 file_id,
666 edition,
667 Some(display_name),
668 cfg_options,
669 env,
670 proc_macro.clone(),
671 );
672
673 return Some(crate_id);
674 }
675 None
676}
540fn sysroot_to_crate_graph( 677fn sysroot_to_crate_graph(
541 crate_graph: &mut CrateGraph, 678 crate_graph: &mut CrateGraph,
542 sysroot: &Sysroot, 679 sysroot: &Sysroot,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 9cc14fe82..372180ab5 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -7,7 +7,7 @@
7//! configure the server itself, feature flags are passed into analysis, and 7//! configure the server itself, feature flags are passed into analysis, and
8//! tweak things like automatic insertion of `()` in completions. 8//! tweak things like automatic insertion of `()` in completions.
9 9
10use std::{ffi::OsString, path::PathBuf}; 10use std::{convert::TryFrom, ffi::OsString, path::PathBuf};
11 11
12use flycheck::FlycheckConfig; 12use flycheck::FlycheckConfig;
13use hir::PrefixKind; 13use hir::PrefixKind;
@@ -227,12 +227,25 @@ impl Config {
227 self.notifications = 227 self.notifications =
228 NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound }; 228 NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound };
229 self.cargo_autoreload = data.cargo_autoreload; 229 self.cargo_autoreload = data.cargo_autoreload;
230
231 let rustc_source = if let Some(rustc_source) = data.rustcSource {
232 let rustpath: PathBuf = rustc_source.into();
233 AbsPathBuf::try_from(rustpath)
234 .map_err(|_| {
235 log::error!("rustc source directory must be an absolute path");
236 })
237 .ok()
238 } else {
239 None
240 };
241
230 self.cargo = CargoConfig { 242 self.cargo = CargoConfig {
231 no_default_features: data.cargo_noDefaultFeatures, 243 no_default_features: data.cargo_noDefaultFeatures,
232 all_features: data.cargo_allFeatures, 244 all_features: data.cargo_allFeatures,
233 features: data.cargo_features.clone(), 245 features: data.cargo_features.clone(),
234 load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck, 246 load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck,
235 target: data.cargo_target.clone(), 247 target: data.cargo_target.clone(),
248 rustc_source: rustc_source,
236 }; 249 };
237 self.runnables = RunnablesConfig { 250 self.runnables = RunnablesConfig {
238 override_cargo: data.runnables_overrideCargo, 251 override_cargo: data.runnables_overrideCargo,
@@ -532,5 +545,6 @@ config_data! {
532 rustfmt_overrideCommand: Option<Vec<String>> = None, 545 rustfmt_overrideCommand: Option<Vec<String>> = None,
533 546
534 withSysroot: bool = true, 547 withSysroot: bool = true,
548 rustcSource : Option<String> = None,
535 } 549 }
536} 550}
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
index 58d47d32a..5b2e5187a 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/compiler/mir/tagset.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/compiler/mir/tagset.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -29,7 +36,14 @@
29 [ 36 [
30 DiagnosticRelatedInformation { 37 DiagnosticRelatedInformation {
31 location: Location { 38 location: Location {
32 uri: "file:///test/compiler/lib.rs", 39 uri: Url {
40 scheme: "file",
41 host: None,
42 port: None,
43 path: "/test/compiler/lib.rs",
44 query: None,
45 fragment: None,
46 },
33 range: Range { 47 range: Range {
34 start: Position { 48 start: Position {
35 line: 0, 49 line: 0,
@@ -45,7 +59,14 @@
45 }, 59 },
46 DiagnosticRelatedInformation { 60 DiagnosticRelatedInformation {
47 location: Location { 61 location: Location {
48 uri: "file:///test/compiler/mir/tagset.rs", 62 uri: Url {
63 scheme: "file",
64 host: None,
65 port: None,
66 path: "/test/compiler/mir/tagset.rs",
67 query: None,
68 fragment: None,
69 },
49 range: Range { 70 range: Range {
50 start: Position { 71 start: Position {
51 line: 41, 72 line: 41,
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
index 6aa26bf63..116f0ff73 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/src/main.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/src/main.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
index 7aaffaba2..bbec6a796 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/crates/hir_def/src/data.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/crates/hir_def/src/data.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -25,7 +32,14 @@
25 [ 32 [
26 DiagnosticRelatedInformation { 33 DiagnosticRelatedInformation {
27 location: Location { 34 location: Location {
28 uri: "file:///test/crates/hir_def/src/path.rs", 35 uri: Url {
36 scheme: "file",
37 host: None,
38 port: None,
39 path: "/test/crates/hir_def/src/path.rs",
40 query: None,
41 fragment: None,
42 },
29 range: Range { 43 range: Range {
30 start: Position { 44 start: Position {
31 line: 264, 45 line: 264,
@@ -47,7 +61,14 @@
47 fixes: [], 61 fixes: [],
48 }, 62 },
49 MappedRustDiagnostic { 63 MappedRustDiagnostic {
50 url: "file:///test/crates/hir_def/src/path.rs", 64 url: Url {
65 scheme: "file",
66 host: None,
67 port: None,
68 path: "/test/crates/hir_def/src/path.rs",
69 query: None,
70 fragment: None,
71 },
51 diagnostic: Diagnostic { 72 diagnostic: Diagnostic {
52 range: Range { 73 range: Range {
53 start: Position { 74 start: Position {
@@ -72,7 +93,14 @@
72 [ 93 [
73 DiagnosticRelatedInformation { 94 DiagnosticRelatedInformation {
74 location: Location { 95 location: Location {
75 uri: "file:///test/crates/hir_def/src/data.rs", 96 uri: Url {
97 scheme: "file",
98 host: None,
99 port: None,
100 path: "/test/crates/hir_def/src/data.rs",
101 query: None,
102 fragment: None,
103 },
76 range: Range { 104 range: Range {
77 start: Position { 105 start: Position {
78 line: 79, 106 line: 79,
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
index 584213420..2cbf657e5 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/compiler/ty/list_iter.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/compiler/ty/list_iter.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
index 2610e4e20..1142dc2ac 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/runtime/compiler_support.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/runtime/compiler_support.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
index a8d85f008..c709de95f 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/driver/subcommand/repl.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/driver/subcommand/repl.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -46,7 +53,14 @@
46 SnippetWorkspaceEdit { 53 SnippetWorkspaceEdit {
47 changes: Some( 54 changes: Some(
48 { 55 {
49 "file:///test/driver/subcommand/repl.rs": [ 56 Url {
57 scheme: "file",
58 host: None,
59 port: None,
60 path: "/test/driver/subcommand/repl.rs",
61 query: None,
62 fragment: None,
63 }: [
50 TextEdit { 64 TextEdit {
51 range: Range { 65 range: Range {
52 start: Position { 66 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
index 0b8937376..632f438d7 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/driver/subcommand/repl.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/driver/subcommand/repl.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -46,7 +53,14 @@
46 SnippetWorkspaceEdit { 53 SnippetWorkspaceEdit {
47 changes: Some( 54 changes: Some(
48 { 55 {
49 "file:///test/driver/subcommand/repl.rs": [ 56 Url {
57 scheme: "file",
58 host: None,
59 port: None,
60 path: "/test/driver/subcommand/repl.rs",
61 query: None,
62 fragment: None,
63 }: [
50 TextEdit { 64 TextEdit {
51 range: Range { 65 range: Range {
52 start: Position { 66 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
index 7fa9dee62..c0b79428d 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/driver/subcommand/repl.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/driver/subcommand/repl.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -46,7 +53,14 @@
46 SnippetWorkspaceEdit { 53 SnippetWorkspaceEdit {
47 changes: Some( 54 changes: Some(
48 { 55 {
49 "file:///test/driver/subcommand/repl.rs": [ 56 Url {
57 scheme: "file",
58 host: None,
59 port: None,
60 path: "/test/driver/subcommand/repl.rs",
61 query: None,
62 fragment: None,
63 }: [
50 TextEdit { 64 TextEdit {
51 range: Range { 65 range: Range {
52 start: Position { 66 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
index ba1b98b33..782c72dbd 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/compiler/ty/select.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/compiler/ty/select.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -29,7 +36,14 @@
29 [ 36 [
30 DiagnosticRelatedInformation { 37 DiagnosticRelatedInformation {
31 location: Location { 38 location: Location {
32 uri: "file:///test/compiler/ty/select.rs", 39 uri: Url {
40 scheme: "file",
41 host: None,
42 port: None,
43 path: "/test/compiler/ty/select.rs",
44 query: None,
45 fragment: None,
46 },
33 range: Range { 47 range: Range {
34 start: Position { 48 start: Position {
35 line: 218, 49 line: 218,
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
index 3c97b2084..d3f27ab6a 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
@@ -1,6 +1,13 @@
1[ 1[
2 MappedRustDiagnostic { 2 MappedRustDiagnostic {
3 url: "file:///test/src/main.rs", 3 url: Url {
4 scheme: "file",
5 host: None,
6 port: None,
7 path: "/test/src/main.rs",
8 query: None,
9 fragment: None,
10 },
4 diagnostic: Diagnostic { 11 diagnostic: Diagnostic {
5 range: Range { 12 range: Range {
6 start: Position { 13 start: Position {
@@ -29,7 +36,14 @@
29 [ 36 [
30 DiagnosticRelatedInformation { 37 DiagnosticRelatedInformation {
31 location: Location { 38 location: Location {
32 uri: "file:///test/src/main.rs", 39 uri: Url {
40 scheme: "file",
41 host: None,
42 port: None,
43 path: "/test/src/main.rs",
44 query: None,
45 fragment: None,
46 },
33 range: Range { 47 range: Range {
34 start: Position { 48 start: Position {
35 line: 2, 49 line: 2,
@@ -61,7 +75,14 @@
61 SnippetWorkspaceEdit { 75 SnippetWorkspaceEdit {
62 changes: Some( 76 changes: Some(
63 { 77 {
64 "file:///test/src/main.rs": [ 78 Url {
79 scheme: "file",
80 host: None,
81 port: None,
82 path: "/test/src/main.rs",
83 query: None,
84 fragment: None,
85 }: [
65 TextEdit { 86 TextEdit {
66 range: Range { 87 range: Range {
67 start: Position { 88 start: Position {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 0eabd51bd..11c8d0e5f 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -246,7 +246,9 @@ impl GlobalState {
246 .iter() 246 .iter()
247 .enumerate() 247 .enumerate()
248 .filter_map(|(id, w)| match w { 248 .filter_map(|(id, w)| match w {
249 ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some((id, cargo.workspace_root())), 249 ProjectWorkspace::Cargo { cargo, sysroot: _, rustc: _ } => {
250 Some((id, cargo.workspace_root()))
251 }
250 ProjectWorkspace::Json { project, .. } => { 252 ProjectWorkspace::Json { project, .. } => {
251 // Enable flychecks for json projects if a custom flycheck command was supplied 253 // Enable flychecks for json projects if a custom flycheck command was supplied
252 // in the workspace configuration. 254 // in the workspace configuration.
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 61d2acb49..1fe907753 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13[dependencies] 13[dependencies]
14itertools = "0.9.0" 14itertools = "0.9.0"
15rowan = "0.10.0" 15rowan = "0.10.0"
16rustc_lexer = { version = "686.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "688.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
19once_cell = "1.3.1" 19once_cell = "1.3.1"
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 8a0e3d27b..7844f9ed6 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -115,10 +115,10 @@ fn test_doc_comment_none() {
115} 115}
116 116
117#[test] 117#[test]
118fn test_doc_comment_of_items() { 118fn test_outer_doc_comment_of_items() {
119 let file = SourceFile::parse( 119 let file = SourceFile::parse(
120 r#" 120 r#"
121 //! doc 121 /// doc
122 // non-doc 122 // non-doc
123 mod foo {} 123 mod foo {}
124 "#, 124 "#,
@@ -130,6 +130,21 @@ fn test_doc_comment_of_items() {
130} 130}
131 131
132#[test] 132#[test]
133fn test_inner_doc_comment_of_items() {
134 let file = SourceFile::parse(
135 r#"
136 //! doc
137 // non-doc
138 mod foo {}
139 "#,
140 )
141 .ok()
142 .unwrap();
143 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
144 assert!(module.doc_comment_text().is_none());
145}
146
147#[test]
133fn test_doc_comment_of_statics() { 148fn test_doc_comment_of_statics() {
134 let file = SourceFile::parse( 149 let file = SourceFile::parse(
135 r#" 150 r#"
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index e4e512f2e..2661c753e 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -14,16 +14,15 @@ use crate::{
14 14
15impl ast::Comment { 15impl ast::Comment {
16 pub fn kind(&self) -> CommentKind { 16 pub fn kind(&self) -> CommentKind {
17 kind_by_prefix(self.text()) 17 CommentKind::from_text(self.text())
18 } 18 }
19 19
20 pub fn prefix(&self) -> &'static str { 20 pub fn prefix(&self) -> &'static str {
21 for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() { 21 let &(prefix, _kind) = CommentKind::BY_PREFIX
22 if *k == self.kind() && self.text().starts_with(prefix) { 22 .iter()
23 return prefix; 23 .find(|&(prefix, kind)| self.kind() == *kind && self.text().starts_with(prefix))
24 } 24 .unwrap();
25 } 25 prefix
26 unreachable!()
27 } 26 }
28} 27}
29 28
@@ -55,29 +54,25 @@ pub enum CommentPlacement {
55 Outer, 54 Outer,
56} 55}
57 56
58const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = { 57impl CommentKind {
59 use {CommentPlacement::*, CommentShape::*}; 58 const BY_PREFIX: [(&'static str, CommentKind); 8] = [
60 &[ 59 ("/**/", CommentKind { shape: CommentShape::Block, doc: None }),
61 ("////", CommentKind { shape: Line, doc: None }), 60 ("////", CommentKind { shape: CommentShape::Line, doc: None }),
62 ("///", CommentKind { shape: Line, doc: Some(Outer) }), 61 ("///", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Outer) }),
63 ("//!", CommentKind { shape: Line, doc: Some(Inner) }), 62 ("//!", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Inner) }),
64 ("/**", CommentKind { shape: Block, doc: Some(Outer) }), 63 ("/**", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Outer) }),
65 ("/*!", CommentKind { shape: Block, doc: Some(Inner) }), 64 ("/*!", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Inner) }),
66 ("//", CommentKind { shape: Line, doc: None }), 65 ("//", CommentKind { shape: CommentShape::Line, doc: None }),
67 ("/*", CommentKind { shape: Block, doc: None }), 66 ("/*", CommentKind { shape: CommentShape::Block, doc: None }),
68 ] 67 ];
69};
70 68
71fn kind_by_prefix(text: &str) -> CommentKind { 69 pub(crate) fn from_text(text: &str) -> CommentKind {
72 if text == "/**/" { 70 let &(_prefix, kind) = CommentKind::BY_PREFIX
73 return CommentKind { shape: CommentShape::Block, doc: None }; 71 .iter()
74 } 72 .find(|&(prefix, _kind)| text.starts_with(prefix))
75 for (prefix, kind) in COMMENT_PREFIX_TO_KIND.iter() { 73 .unwrap();
76 if text.starts_with(prefix) { 74 kind
77 return *kind;
78 }
79 } 75 }
80 panic!("bad comment text: {:?}", text)
81} 76}
82 77
83impl ast::Whitespace { 78impl ast::Whitespace {
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs
index c1b5f246d..997bc5d28 100644
--- a/crates/syntax/src/parsing/text_tree_sink.rs
+++ b/crates/syntax/src/parsing/text_tree_sink.rs
@@ -5,6 +5,7 @@ use std::mem;
5use parser::{ParseError, TreeSink}; 5use parser::{ParseError, TreeSink};
6 6
7use crate::{ 7use crate::{
8 ast,
8 parsing::Token, 9 parsing::Token,
9 syntax_node::GreenNode, 10 syntax_node::GreenNode,
10 SmolStr, SyntaxError, 11 SmolStr, SyntaxError,
@@ -153,24 +154,22 @@ fn n_attached_trivias<'a>(
153 154
154 while let Some((i, (kind, text))) = trivias.next() { 155 while let Some((i, (kind, text))) = trivias.next() {
155 match kind { 156 match kind {
156 WHITESPACE => { 157 WHITESPACE if text.contains("\n\n") => {
157 if text.contains("\n\n") { 158 // we check whether the next token is a doc-comment
158 // we check whether the next token is a doc-comment 159 // and skip the whitespace in this case
159 // and skip the whitespace in this case 160 if let Some((COMMENT, peek_text)) = trivias.peek().map(|(_, pair)| pair) {
160 if let Some((peek_kind, peek_text)) = 161 let comment_kind = ast::CommentKind::from_text(peek_text);
161 trivias.peek().map(|(_, pair)| pair) 162 if comment_kind.doc == Some(ast::CommentPlacement::Outer) {
162 { 163 continue;
163 if *peek_kind == COMMENT
164 && peek_text.starts_with("///")
165 && !peek_text.starts_with("////")
166 {
167 continue;
168 }
169 } 164 }
170 break;
171 } 165 }
166 break;
172 } 167 }
173 COMMENT => { 168 COMMENT => {
169 let comment_kind = ast::CommentKind::from_text(text);
170 if comment_kind.doc == Some(ast::CommentPlacement::Inner) {
171 break;
172 }
174 res = i + 1; 173 res = i + 1;
175 } 174 }
176 _ => (), 175 _ => (),
diff --git a/crates/syntax/test_data/parser/ok/0037_mod.rast b/crates/syntax/test_data/parser/ok/0037_mod.rast
index 1d5d94bde..35577272e 100644
--- a/crates/syntax/test_data/parser/ok/0037_mod.rast
+++ b/crates/syntax/test_data/parser/ok/0037_mod.rast
@@ -1,9 +1,9 @@
1[email protected] 1[email protected]
2 [email protected] "// https://github.com ..." 2 [email protected] "// https://github.com ..."
3 [email protected] "\n\n" 3 [email protected] "\n\n"
4 MODULE@62..93 4 COMMENT@62..70 "//! docs"
5 COMMENT@62..70 "//! docs" 5 WHITESPACE@70..71 "\n"
6 WHITESPACE@70..71 "\n" 6 MODULE@71..93
7 [email protected] "// non-docs" 7 [email protected] "// non-docs"
8 [email protected] "\n" 8 [email protected] "\n"
9 [email protected] "mod" 9 [email protected] "mod"
diff --git a/editors/code/package.json b/editors/code/package.json
index b02c80773..3768679fe 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -692,6 +692,14 @@
692 }, 692 },
693 "default": [], 693 "default": [],
694 "description": "Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be '--release'" 694 "description": "Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be '--release'"
695 },
696 "rust-analyzer.rustcSource": {
697 "type": [
698 "null",
699 "string"
700 ],
701 "default": null,
702 "description": "Path to the rust compiler sources, for usage in rustc_private projects."
695 } 703 }
696 } 704 }
697 }, 705 },