aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/assists/add_explicit_type.rs18
-rw-r--r--crates/ra_cargo_watch/Cargo.toml17
-rw-r--r--crates/ra_cargo_watch/src/conv.rs359
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap85
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap46
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap61
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap46
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap46
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap70
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap65
-rw-r--r--crates/ra_cargo_watch/src/conv/test.rs929
-rw-r--r--crates/ra_cargo_watch/src/lib.rs394
-rw-r--r--crates/ra_hir/src/code_model.rs19
-rw-r--r--crates/ra_hir/src/lib.rs4
-rw-r--r--crates/ra_hir/src/source_binder.rs26
-rw-r--r--crates/ra_hir_def/src/adt.rs44
-rw-r--r--crates/ra_hir_def/src/body.rs6
-rw-r--r--crates/ra_hir_def/src/body/lower.rs25
-rw-r--r--crates/ra_hir_def/src/item_scope.rs33
-rw-r--r--crates/ra_hir_def/src/lang_item.rs44
-rw-r--r--crates/ra_hir_def/src/lib.rs2
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs109
-rw-r--r--crates/ra_hir_def/src/nameres/path_resolution.rs72
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs35
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs6
-rw-r--r--crates/ra_hir_def/src/nameres/tests/globs.rs97
-rw-r--r--crates/ra_hir_def/src/nameres/tests/incremental.rs4
-rw-r--r--crates/ra_hir_def/src/path.rs4
-rw-r--r--crates/ra_hir_def/src/per_ns.rs50
-rw-r--r--crates/ra_hir_def/src/resolver.rs24
-rw-r--r--crates/ra_hir_def/src/visibility.rs120
-rw-r--r--crates/ra_hir_ty/src/infer.rs23
-rw-r--r--crates/ra_hir_ty/src/tests.rs80
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs36
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs4
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs5
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs57
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html6
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html6
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs27
-rw-r--r--crates/ra_lsp_server/Cargo.toml1
-rw-r--r--crates/ra_lsp_server/src/caps.rs4
-rw-r--r--crates/ra_lsp_server/src/config.rs9
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs59
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs28
-rw-r--r--crates/ra_lsp_server/src/req.rs8
-rw-r--r--crates/ra_lsp_server/src/world.rs72
-rw-r--r--crates/ra_syntax/src/ast.rs4
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs29
-rw-r--r--crates/ra_syntax/src/ast/generated.rs3
-rw-r--r--crates/ra_syntax/src/grammar.ron6
51 files changed, 3073 insertions, 254 deletions
diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/assists/add_explicit_type.rs
index eeb4ff39f..2c602a79e 100644
--- a/crates/ra_assists/src/assists/add_explicit_type.rs
+++ b/crates/ra_assists/src/assists/add_explicit_type.rs
@@ -74,6 +74,24 @@ mod tests {
74 } 74 }
75 75
76 #[test] 76 #[test]
77 fn add_explicit_type_works_for_macro_call() {
78 check_assist(
79 add_explicit_type,
80 "macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }",
81 "macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }",
82 );
83 }
84
85 #[test]
86 fn add_explicit_type_works_for_macro_call_recursive() {
87 check_assist(
88 add_explicit_type,
89 "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }",
90 "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }",
91 );
92 }
93
94 #[test]
77 fn add_explicit_type_not_applicable_if_ty_not_inferred() { 95 fn add_explicit_type_not_applicable_if_ty_not_inferred() {
78 check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }"); 96 check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }");
79 } 97 }
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_cargo_watch/Cargo.toml
new file mode 100644
index 000000000..bcc4648ff
--- /dev/null
+++ b/crates/ra_cargo_watch/Cargo.toml
@@ -0,0 +1,17 @@
1[package]
2edition = "2018"
3name = "ra_cargo_watch"
4version = "0.1.0"
5authors = ["rust-analyzer developers"]
6
7[dependencies]
8crossbeam-channel = "0.4"
9lsp-types = { version = "0.67.0", features = ["proposed"] }
10log = "0.4.3"
11cargo_metadata = "0.9.1"
12jod-thread = "0.1.0"
13parking_lot = "0.10.0"
14
15[dev-dependencies]
16insta = "0.12.0"
17serde_json = "1.0" \ No newline at end of file
diff --git a/crates/ra_cargo_watch/src/conv.rs b/crates/ra_cargo_watch/src/conv.rs
new file mode 100644
index 000000000..ac0f1d28a
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv.rs
@@ -0,0 +1,359 @@
1//! This module provides the functionality needed to convert diagnostics from
2//! `cargo check` json format to the LSP diagnostic format.
3use cargo_metadata::diagnostic::{
4 Applicability, Diagnostic as RustDiagnostic, DiagnosticLevel, DiagnosticSpan,
5 DiagnosticSpanMacroExpansion,
6};
7use lsp_types::{
8 Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location,
9 NumberOrString, Position, Range, Url,
10};
11use std::{
12 fmt::Write,
13 path::{Component, Path, PathBuf, Prefix},
14 str::FromStr,
15};
16
17#[cfg(test)]
18mod test;
19
20/// Converts a Rust level string to a LSP severity
21fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
22 match val {
23 DiagnosticLevel::Ice => Some(DiagnosticSeverity::Error),
24 DiagnosticLevel::Error => Some(DiagnosticSeverity::Error),
25 DiagnosticLevel::Warning => Some(DiagnosticSeverity::Warning),
26 DiagnosticLevel::Note => Some(DiagnosticSeverity::Information),
27 DiagnosticLevel::Help => Some(DiagnosticSeverity::Hint),
28 DiagnosticLevel::Unknown => None,
29 }
30}
31
32/// Check whether a file name is from macro invocation
33fn is_from_macro(file_name: &str) -> bool {
34 file_name.starts_with('<') && file_name.ends_with('>')
35}
36
37/// Converts a Rust macro span to a LSP location recursively
38fn map_macro_span_to_location(
39 span_macro: &DiagnosticSpanMacroExpansion,
40 workspace_root: &PathBuf,
41) -> Option<Location> {
42 if !is_from_macro(&span_macro.span.file_name) {
43 return Some(map_span_to_location(&span_macro.span, workspace_root));
44 }
45
46 if let Some(expansion) = &span_macro.span.expansion {
47 return map_macro_span_to_location(&expansion, workspace_root);
48 }
49
50 None
51}
52
53/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
54fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location {
55 if span.expansion.is_some() {
56 let expansion = span.expansion.as_ref().unwrap();
57 if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) {
58 return macro_range;
59 }
60 }
61
62 map_span_to_location_naive(span, workspace_root)
63}
64
65/// Converts a Rust span to a LSP location
66fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location {
67 let mut file_name = workspace_root.clone();
68 file_name.push(&span.file_name);
69 let uri = url_from_path_with_drive_lowercasing(file_name).unwrap();
70
71 let range = Range::new(
72 Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
73 Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
74 );
75
76 Location { uri, range }
77}
78
79/// Converts a secondary Rust span to a LSP related information
80///
81/// If the span is unlabelled this will return `None`.
82fn map_secondary_span_to_related(
83 span: &DiagnosticSpan,
84 workspace_root: &PathBuf,
85) -> Option<DiagnosticRelatedInformation> {
86 if let Some(label) = &span.label {
87 let location = map_span_to_location(span, workspace_root);
88 Some(DiagnosticRelatedInformation { location, message: label.clone() })
89 } else {
90 // Nothing to label this with
91 None
92 }
93}
94
95/// Determines if diagnostic is related to unused code
96fn is_unused_or_unnecessary(rd: &RustDiagnostic) -> bool {
97 if let Some(code) = &rd.code {
98 match code.code.as_str() {
99 "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes"
100 | "unused_imports" | "unused_macros" | "unused_variables" => true,
101 _ => false,
102 }
103 } else {
104 false
105 }
106}
107
108/// Determines if diagnostic is related to deprecated code
109fn is_deprecated(rd: &RustDiagnostic) -> bool {
110 if let Some(code) = &rd.code {
111 match code.code.as_str() {
112 "deprecated" => true,
113 _ => false,
114 }
115 } else {
116 false
117 }
118}
119
120#[derive(Debug)]
121pub struct SuggestedFix {
122 pub title: String,
123 pub location: Location,
124 pub replacement: String,
125 pub applicability: Applicability,
126 pub diagnostics: Vec<Diagnostic>,
127}
128
129impl std::cmp::PartialEq<SuggestedFix> for SuggestedFix {
130 fn eq(&self, other: &SuggestedFix) -> bool {
131 if self.title == other.title
132 && self.location == other.location
133 && self.replacement == other.replacement
134 {
135 // Applicability doesn't impl PartialEq...
136 match (&self.applicability, &other.applicability) {
137 (Applicability::MachineApplicable, Applicability::MachineApplicable) => true,
138 (Applicability::HasPlaceholders, Applicability::HasPlaceholders) => true,
139 (Applicability::MaybeIncorrect, Applicability::MaybeIncorrect) => true,
140 (Applicability::Unspecified, Applicability::Unspecified) => true,
141 _ => false,
142 }
143 } else {
144 false
145 }
146 }
147}
148
149enum MappedRustChildDiagnostic {
150 Related(DiagnosticRelatedInformation),
151 SuggestedFix(SuggestedFix),
152 MessageLine(String),
153}
154
155fn map_rust_child_diagnostic(
156 rd: &RustDiagnostic,
157 workspace_root: &PathBuf,
158) -> MappedRustChildDiagnostic {
159 let span: &DiagnosticSpan = match rd.spans.iter().find(|s| s.is_primary) {
160 Some(span) => span,
161 None => {
162 // `rustc` uses these spanless children as a way to print multi-line
163 // messages
164 return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
165 }
166 };
167
168 // If we have a primary span use its location, otherwise use the parent
169 let location = map_span_to_location(&span, workspace_root);
170
171 if let Some(suggested_replacement) = &span.suggested_replacement {
172 // Include our replacement in the title unless it's empty
173 let title = if !suggested_replacement.is_empty() {
174 format!("{}: '{}'", rd.message, suggested_replacement)
175 } else {
176 rd.message.clone()
177 };
178
179 MappedRustChildDiagnostic::SuggestedFix(SuggestedFix {
180 title,
181 location,
182 replacement: suggested_replacement.clone(),
183 applicability: span.suggestion_applicability.clone().unwrap_or(Applicability::Unknown),
184 diagnostics: vec![],
185 })
186 } else {
187 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation {
188 location,
189 message: rd.message.clone(),
190 })
191 }
192}
193
194#[derive(Debug)]
195pub(crate) struct MappedRustDiagnostic {
196 pub location: Location,
197 pub diagnostic: Diagnostic,
198 pub suggested_fixes: Vec<SuggestedFix>,
199}
200
201/// Converts a Rust root diagnostic to LSP form
202///
203/// This flattens the Rust diagnostic by:
204///
205/// 1. Creating a LSP diagnostic with the root message and primary span.
206/// 2. Adding any labelled secondary spans to `relatedInformation`
207/// 3. Categorising child diagnostics as either `SuggestedFix`es,
208/// `relatedInformation` or additional message lines.
209///
210/// If the diagnostic has no primary span this will return `None`
211pub(crate) fn map_rust_diagnostic_to_lsp(
212 rd: &RustDiagnostic,
213 workspace_root: &PathBuf,
214) -> Option<MappedRustDiagnostic> {
215 let primary_span = rd.spans.iter().find(|s| s.is_primary)?;
216
217 let location = map_span_to_location(&primary_span, workspace_root);
218
219 let severity = map_level_to_severity(rd.level);
220 let mut primary_span_label = primary_span.label.as_ref();
221
222 let mut source = String::from("rustc");
223 let mut code = rd.code.as_ref().map(|c| c.code.clone());
224 if let Some(code_val) = &code {
225 // See if this is an RFC #2103 scoped lint (e.g. from Clippy)
226 let scoped_code: Vec<&str> = code_val.split("::").collect();
227 if scoped_code.len() == 2 {
228 source = String::from(scoped_code[0]);
229 code = Some(String::from(scoped_code[1]));
230 }
231 }
232
233 let mut related_information = vec![];
234 let mut tags = vec![];
235
236 // If error occurs from macro expansion, add related info pointing to
237 // where the error originated
238 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
239 let def_loc = map_span_to_location_naive(&primary_span, workspace_root);
240 related_information.push(DiagnosticRelatedInformation {
241 location: def_loc,
242 message: "Error originated from macro here".to_string(),
243 });
244 }
245
246 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
247 let related = map_secondary_span_to_related(secondary_span, workspace_root);
248 if let Some(related) = related {
249 related_information.push(related);
250 }
251 }
252
253 let mut suggested_fixes = vec![];
254 let mut message = rd.message.clone();
255 for child in &rd.children {
256 let child = map_rust_child_diagnostic(&child, workspace_root);
257 match child {
258 MappedRustChildDiagnostic::Related(related) => related_information.push(related),
259 MappedRustChildDiagnostic::SuggestedFix(suggested_fix) => {
260 suggested_fixes.push(suggested_fix)
261 }
262 MappedRustChildDiagnostic::MessageLine(message_line) => {
263 write!(&mut message, "\n{}", message_line).unwrap();
264
265 // These secondary messages usually duplicate the content of the
266 // primary span label.
267 primary_span_label = None;
268 }
269 }
270 }
271
272 if let Some(primary_span_label) = primary_span_label {
273 write!(&mut message, "\n{}", primary_span_label).unwrap();
274 }
275
276 if is_unused_or_unnecessary(rd) {
277 tags.push(DiagnosticTag::Unnecessary);
278 }
279
280 if is_deprecated(rd) {
281 tags.push(DiagnosticTag::Deprecated);
282 }
283
284 let diagnostic = Diagnostic {
285 range: location.range,
286 severity,
287 code: code.map(NumberOrString::String),
288 source: Some(source),
289 message,
290 related_information: if !related_information.is_empty() {
291 Some(related_information)
292 } else {
293 None
294 },
295 tags: if !tags.is_empty() { Some(tags) } else { None },
296 };
297
298 Some(MappedRustDiagnostic { location, diagnostic, suggested_fixes })
299}
300
301/// Returns a `Url` object from a given path, will lowercase drive letters if present.
302/// This will only happen when processing windows paths.
303///
304/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
305pub fn url_from_path_with_drive_lowercasing(
306 path: impl AsRef<Path>,
307) -> Result<Url, Box<dyn std::error::Error + Send + Sync>> {
308 let component_has_windows_drive = path.as_ref().components().any(|comp| {
309 if let Component::Prefix(c) = comp {
310 match c.kind() {
311 Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true,
312 _ => return false,
313 }
314 }
315 false
316 });
317
318 // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
319 if component_has_windows_drive {
320 let url_original = Url::from_file_path(&path)
321 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;
322
323 let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect();
324
325 // There is a drive partition, but we never found a colon.
326 // This should not happen, but in this case we just pass it through.
327 if drive_partition.len() == 1 {
328 return Ok(url_original);
329 }
330
331 let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
332 let url = Url::from_str(&joined).expect("This came from a valid `Url`");
333
334 Ok(url)
335 } else {
336 Ok(Url::from_file_path(&path)
337 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?)
338 }
339}
340
341// `Url` is not able to parse windows paths on unix machines.
342#[cfg(target_os = "windows")]
343#[cfg(test)]
344mod path_conversion_windows_tests {
345 use super::url_from_path_with_drive_lowercasing;
346 #[test]
347 fn test_lowercase_drive_letter_with_drive() {
348 let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap();
349
350 assert_eq!(url.to_string(), "file:///c:/Test");
351 }
352
353 #[test]
354 fn test_drive_without_colon_passthrough() {
355 let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap();
356
357 assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
358 }
359}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap
new file mode 100644
index 000000000..cb0920914
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_clippy_pass_by_ref.snap
@@ -0,0 +1,85 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/compiler/mir/tagset.rs",
8 range: Range {
9 start: Position {
10 line: 41,
11 character: 23,
12 },
13 end: Position {
14 line: 41,
15 character: 28,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 41,
23 character: 23,
24 },
25 end: Position {
26 line: 41,
27 character: 28,
28 },
29 },
30 severity: Some(
31 Warning,
32 ),
33 code: Some(
34 String(
35 "trivially_copy_pass_by_ref",
36 ),
37 ),
38 source: Some(
39 "clippy",
40 ),
41 message: "this argument is passed by reference, but would be more efficient if passed by value\n#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
42 related_information: Some(
43 [
44 DiagnosticRelatedInformation {
45 location: Location {
46 uri: "file:///test/compiler/lib.rs",
47 range: Range {
48 start: Position {
49 line: 0,
50 character: 8,
51 },
52 end: Position {
53 line: 0,
54 character: 19,
55 },
56 },
57 },
58 message: "lint level defined here",
59 },
60 ],
61 ),
62 tags: None,
63 },
64 suggested_fixes: [
65 SuggestedFix {
66 title: "consider passing by value instead: \'self\'",
67 location: Location {
68 uri: "file:///test/compiler/mir/tagset.rs",
69 range: Range {
70 start: Position {
71 line: 41,
72 character: 23,
73 },
74 end: Position {
75 line: 41,
76 character: 28,
77 },
78 },
79 },
80 replacement: "self",
81 applicability: Unspecified,
82 diagnostics: [],
83 },
84 ],
85}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap
new file mode 100644
index 000000000..19510ecc1
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_handles_macro_location.snap
@@ -0,0 +1,46 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/src/main.rs",
8 range: Range {
9 start: Position {
10 line: 1,
11 character: 4,
12 },
13 end: Position {
14 line: 1,
15 character: 26,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 1,
23 character: 4,
24 },
25 end: Position {
26 line: 1,
27 character: 26,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0277",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
42 related_information: None,
43 tags: None,
44 },
45 suggested_fixes: [],
46}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap
new file mode 100644
index 000000000..92f7eec05
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_macro_compiler_error.snap
@@ -0,0 +1,61 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/crates/ra_hir_def/src/data.rs",
8 range: Range {
9 start: Position {
10 line: 79,
11 character: 15,
12 },
13 end: Position {
14 line: 79,
15 character: 41,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 79,
23 character: 15,
24 },
25 end: Position {
26 line: 79,
27 character: 41,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: None,
34 source: Some(
35 "rustc",
36 ),
37 message: "Please register your known path in the path module",
38 related_information: Some(
39 [
40 DiagnosticRelatedInformation {
41 location: Location {
42 uri: "file:///test/crates/ra_hir_def/src/path.rs",
43 range: Range {
44 start: Position {
45 line: 264,
46 character: 8,
47 },
48 end: Position {
49 line: 264,
50 character: 76,
51 },
52 },
53 },
54 message: "Error originated from macro here",
55 },
56 ],
57 ),
58 tags: None,
59 },
60 suggested_fixes: [],
61}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap
new file mode 100644
index 000000000..cf683e4b6
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_incompatible_type_for_trait.snap
@@ -0,0 +1,46 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/compiler/ty/list_iter.rs",
8 range: Range {
9 start: Position {
10 line: 51,
11 character: 4,
12 },
13 end: Position {
14 line: 51,
15 character: 47,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 51,
23 character: 4,
24 },
25 end: Position {
26 line: 51,
27 character: 47,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0053",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "method `next` has an incompatible type for trait\nexpected type `fn(&mut ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<\'list, M>) -> std::option::Option<&\'list ty::Ref<M>>`",
42 related_information: None,
43 tags: None,
44 },
45 suggested_fixes: [],
46}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap
new file mode 100644
index 000000000..8c1483c74
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_mismatched_type.snap
@@ -0,0 +1,46 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/runtime/compiler_support.rs",
8 range: Range {
9 start: Position {
10 line: 47,
11 character: 64,
12 },
13 end: Position {
14 line: 47,
15 character: 69,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 47,
23 character: 64,
24 },
25 end: Position {
26 line: 47,
27 character: 69,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0308",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "mismatched types\nexpected usize, found u32",
42 related_information: None,
43 tags: None,
44 },
45 suggested_fixes: [],
46}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap
new file mode 100644
index 000000000..eb5a2247b
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_unused_variable.snap
@@ -0,0 +1,70 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/driver/subcommand/repl.rs",
8 range: Range {
9 start: Position {
10 line: 290,
11 character: 8,
12 },
13 end: Position {
14 line: 290,
15 character: 11,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 290,
23 character: 8,
24 },
25 end: Position {
26 line: 290,
27 character: 11,
28 },
29 },
30 severity: Some(
31 Warning,
32 ),
33 code: Some(
34 String(
35 "unused_variables",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
42 related_information: None,
43 tags: Some(
44 [
45 Unnecessary,
46 ],
47 ),
48 },
49 suggested_fixes: [
50 SuggestedFix {
51 title: "consider prefixing with an underscore: \'_foo\'",
52 location: Location {
53 uri: "file:///test/driver/subcommand/repl.rs",
54 range: Range {
55 start: Position {
56 line: 290,
57 character: 8,
58 },
59 end: Position {
60 line: 290,
61 character: 11,
62 },
63 },
64 },
65 replacement: "_foo",
66 applicability: MachineApplicable,
67 diagnostics: [],
68 },
69 ],
70}
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap
new file mode 100644
index 000000000..2f4518931
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/snapshots/test__snap_rustc_wrong_number_of_parameters.snap
@@ -0,0 +1,65 @@
1---
2source: crates/ra_cargo_watch/src/conv/test.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/compiler/ty/select.rs",
8 range: Range {
9 start: Position {
10 line: 103,
11 character: 17,
12 },
13 end: Position {
14 line: 103,
15 character: 29,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 103,
23 character: 17,
24 },
25 end: Position {
26 line: 103,
27 character: 29,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0061",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "this function takes 2 parameters but 3 parameters were supplied\nexpected 2 parameters",
42 related_information: Some(
43 [
44 DiagnosticRelatedInformation {
45 location: Location {
46 uri: "file:///test/compiler/ty/select.rs",
47 range: Range {
48 start: Position {
49 line: 218,
50 character: 4,
51 },
52 end: Position {
53 line: 230,
54 character: 5,
55 },
56 },
57 },
58 message: "defined here",
59 },
60 ],
61 ),
62 tags: None,
63 },
64 suggested_fixes: [],
65}
diff --git a/crates/ra_cargo_watch/src/conv/test.rs b/crates/ra_cargo_watch/src/conv/test.rs
new file mode 100644
index 000000000..381992388
--- /dev/null
+++ b/crates/ra_cargo_watch/src/conv/test.rs
@@ -0,0 +1,929 @@
1//! This module contains the large and verbose snapshot tests for the
2//! conversions between `cargo check` json and LSP diagnostics.
3use crate::*;
4
5fn parse_diagnostic(val: &str) -> cargo_metadata::diagnostic::Diagnostic {
6 serde_json::from_str::<cargo_metadata::diagnostic::Diagnostic>(val).unwrap()
7}
8
9#[test]
10fn snap_rustc_incompatible_type_for_trait() {
11 let diag = parse_diagnostic(
12 r##"{
13 "message": "method `next` has an incompatible type for trait",
14 "code": {
15 "code": "E0053",
16 "explanation": "\nThe parameters of any trait method must match between a trait implementation\nand the trait definition.\n\nHere are a couple examples of this error:\n\n```compile_fail,E0053\ntrait Foo {\n fn foo(x: u16);\n fn bar(&self);\n}\n\nstruct Bar;\n\nimpl Foo for Bar {\n // error, expected u16, found i16\n fn foo(x: i16) { }\n\n // error, types differ in mutability\n fn bar(&mut self) { }\n}\n```\n"
17 },
18 "level": "error",
19 "spans": [
20 {
21 "file_name": "compiler/ty/list_iter.rs",
22 "byte_start": 1307,
23 "byte_end": 1350,
24 "line_start": 52,
25 "line_end": 52,
26 "column_start": 5,
27 "column_end": 48,
28 "is_primary": true,
29 "text": [
30 {
31 "text": " fn next(&self) -> Option<&'list ty::Ref<M>> {",
32 "highlight_start": 5,
33 "highlight_end": 48
34 }
35 ],
36 "label": "types differ in mutability",
37 "suggested_replacement": null,
38 "suggestion_applicability": null,
39 "expansion": null
40 }
41 ],
42 "children": [
43 {
44 "message": "expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`",
45 "code": null,
46 "level": "note",
47 "spans": [],
48 "children": [],
49 "rendered": null
50 }
51 ],
52 "rendered": "error[E0053]: method `next` has an incompatible type for trait\n --> compiler/ty/list_iter.rs:52:5\n |\n52 | fn next(&self) -> Option<&'list ty::Ref<M>> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability\n |\n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`\n\n"
53 }
54 "##,
55 );
56
57 let workspace_root = PathBuf::from("/test/");
58 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
59 insta::assert_debug_snapshot!(diag);
60}
61
62#[test]
63fn snap_rustc_unused_variable() {
64 let diag = parse_diagnostic(
65 r##"{
66"message": "unused variable: `foo`",
67"code": {
68 "code": "unused_variables",
69 "explanation": null
70},
71"level": "warning",
72"spans": [
73 {
74 "file_name": "driver/subcommand/repl.rs",
75 "byte_start": 9228,
76 "byte_end": 9231,
77 "line_start": 291,
78 "line_end": 291,
79 "column_start": 9,
80 "column_end": 12,
81 "is_primary": true,
82 "text": [
83 {
84 "text": " let foo = 42;",
85 "highlight_start": 9,
86 "highlight_end": 12
87 }
88 ],
89 "label": null,
90 "suggested_replacement": null,
91 "suggestion_applicability": null,
92 "expansion": null
93 }
94],
95"children": [
96 {
97 "message": "#[warn(unused_variables)] on by default",
98 "code": null,
99 "level": "note",
100 "spans": [],
101 "children": [],
102 "rendered": null
103 },
104 {
105 "message": "consider prefixing with an underscore",
106 "code": null,
107 "level": "help",
108 "spans": [
109 {
110 "file_name": "driver/subcommand/repl.rs",
111 "byte_start": 9228,
112 "byte_end": 9231,
113 "line_start": 291,
114 "line_end": 291,
115 "column_start": 9,
116 "column_end": 12,
117 "is_primary": true,
118 "text": [
119 {
120 "text": " let foo = 42;",
121 "highlight_start": 9,
122 "highlight_end": 12
123 }
124 ],
125 "label": null,
126 "suggested_replacement": "_foo",
127 "suggestion_applicability": "MachineApplicable",
128 "expansion": null
129 }
130 ],
131 "children": [],
132 "rendered": null
133 }
134],
135"rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
136}"##,
137 );
138
139 let workspace_root = PathBuf::from("/test/");
140 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
141 insta::assert_debug_snapshot!(diag);
142}
143
144#[test]
145fn snap_rustc_wrong_number_of_parameters() {
146 let diag = parse_diagnostic(
147 r##"{
148"message": "this function takes 2 parameters but 3 parameters were supplied",
149"code": {
150 "code": "E0061",
151 "explanation": "\nThe number of arguments passed to a function must match the number of arguments\nspecified in the function signature.\n\nFor example, a function like:\n\n```\nfn f(a: u16, b: &str) {}\n```\n\nMust always be called with exactly two arguments, e.g., `f(2, \"test\")`.\n\nNote that Rust does not have a notion of optional function arguments or\nvariadic functions (except for its C-FFI).\n"
152},
153"level": "error",
154"spans": [
155 {
156 "file_name": "compiler/ty/select.rs",
157 "byte_start": 8787,
158 "byte_end": 9241,
159 "line_start": 219,
160 "line_end": 231,
161 "column_start": 5,
162 "column_end": 6,
163 "is_primary": false,
164 "text": [
165 {
166 "text": " pub fn add_evidence(",
167 "highlight_start": 5,
168 "highlight_end": 25
169 },
170 {
171 "text": " &mut self,",
172 "highlight_start": 1,
173 "highlight_end": 19
174 },
175 {
176 "text": " target_poly: &ty::Ref<ty::Poly>,",
177 "highlight_start": 1,
178 "highlight_end": 41
179 },
180 {
181 "text": " evidence_poly: &ty::Ref<ty::Poly>,",
182 "highlight_start": 1,
183 "highlight_end": 43
184 },
185 {
186 "text": " ) {",
187 "highlight_start": 1,
188 "highlight_end": 8
189 },
190 {
191 "text": " match target_poly {",
192 "highlight_start": 1,
193 "highlight_end": 28
194 },
195 {
196 "text": " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),",
197 "highlight_start": 1,
198 "highlight_end": 81
199 },
200 {
201 "text": " ty::Ref::Fixed(target_ty) => {",
202 "highlight_start": 1,
203 "highlight_end": 43
204 },
205 {
206 "text": " let evidence_ty = evidence_poly.resolve_to_ty();",
207 "highlight_start": 1,
208 "highlight_end": 65
209 },
210 {
211 "text": " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)",
212 "highlight_start": 1,
213 "highlight_end": 76
214 },
215 {
216 "text": " }",
217 "highlight_start": 1,
218 "highlight_end": 14
219 },
220 {
221 "text": " }",
222 "highlight_start": 1,
223 "highlight_end": 10
224 },
225 {
226 "text": " }",
227 "highlight_start": 1,
228 "highlight_end": 6
229 }
230 ],
231 "label": "defined here",
232 "suggested_replacement": null,
233 "suggestion_applicability": null,
234 "expansion": null
235 },
236 {
237 "file_name": "compiler/ty/select.rs",
238 "byte_start": 4045,
239 "byte_end": 4057,
240 "line_start": 104,
241 "line_end": 104,
242 "column_start": 18,
243 "column_end": 30,
244 "is_primary": true,
245 "text": [
246 {
247 "text": " self.add_evidence(target_fixed, evidence_fixed, false);",
248 "highlight_start": 18,
249 "highlight_end": 30
250 }
251 ],
252 "label": "expected 2 parameters",
253 "suggested_replacement": null,
254 "suggestion_applicability": null,
255 "expansion": null
256 }
257],
258"children": [],
259"rendered": "error[E0061]: this function takes 2 parameters but 3 parameters were supplied\n --> compiler/ty/select.rs:104:18\n |\n104 | self.add_evidence(target_fixed, evidence_fixed, false);\n | ^^^^^^^^^^^^ expected 2 parameters\n...\n219 | / pub fn add_evidence(\n220 | | &mut self,\n221 | | target_poly: &ty::Ref<ty::Poly>,\n222 | | evidence_poly: &ty::Ref<ty::Poly>,\n... |\n230 | | }\n231 | | }\n | |_____- defined here\n\n"
260}"##,
261 );
262
263 let workspace_root = PathBuf::from("/test/");
264 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
265 insta::assert_debug_snapshot!(diag);
266}
267
268#[test]
269fn snap_clippy_pass_by_ref() {
270 let diag = parse_diagnostic(
271 r##"{
272"message": "this argument is passed by reference, but would be more efficient if passed by value",
273"code": {
274 "code": "clippy::trivially_copy_pass_by_ref",
275 "explanation": null
276},
277"level": "warning",
278"spans": [
279 {
280 "file_name": "compiler/mir/tagset.rs",
281 "byte_start": 941,
282 "byte_end": 946,
283 "line_start": 42,
284 "line_end": 42,
285 "column_start": 24,
286 "column_end": 29,
287 "is_primary": true,
288 "text": [
289 {
290 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
291 "highlight_start": 24,
292 "highlight_end": 29
293 }
294 ],
295 "label": null,
296 "suggested_replacement": null,
297 "suggestion_applicability": null,
298 "expansion": null
299 }
300],
301"children": [
302 {
303 "message": "lint level defined here",
304 "code": null,
305 "level": "note",
306 "spans": [
307 {
308 "file_name": "compiler/lib.rs",
309 "byte_start": 8,
310 "byte_end": 19,
311 "line_start": 1,
312 "line_end": 1,
313 "column_start": 9,
314 "column_end": 20,
315 "is_primary": true,
316 "text": [
317 {
318 "text": "#![warn(clippy::all)]",
319 "highlight_start": 9,
320 "highlight_end": 20
321 }
322 ],
323 "label": null,
324 "suggested_replacement": null,
325 "suggestion_applicability": null,
326 "expansion": null
327 }
328 ],
329 "children": [],
330 "rendered": null
331 },
332 {
333 "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]",
334 "code": null,
335 "level": "note",
336 "spans": [],
337 "children": [],
338 "rendered": null
339 },
340 {
341 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
342 "code": null,
343 "level": "help",
344 "spans": [],
345 "children": [],
346 "rendered": null
347 },
348 {
349 "message": "consider passing by value instead",
350 "code": null,
351 "level": "help",
352 "spans": [
353 {
354 "file_name": "compiler/mir/tagset.rs",
355 "byte_start": 941,
356 "byte_end": 946,
357 "line_start": 42,
358 "line_end": 42,
359 "column_start": 24,
360 "column_end": 29,
361 "is_primary": true,
362 "text": [
363 {
364 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
365 "highlight_start": 24,
366 "highlight_end": 29
367 }
368 ],
369 "label": null,
370 "suggested_replacement": "self",
371 "suggestion_applicability": "Unspecified",
372 "expansion": null
373 }
374 ],
375 "children": [],
376 "rendered": null
377 }
378],
379"rendered": "warning: this argument is passed by reference, but would be more efficient if passed by value\n --> compiler/mir/tagset.rs:42:24\n |\n42 | pub fn is_disjoint(&self, other: Self) -> bool {\n | ^^^^^ help: consider passing by value instead: `self`\n |\nnote: lint level defined here\n --> compiler/lib.rs:1:9\n |\n1 | #![warn(clippy::all)]\n | ^^^^^^^^^^^\n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref\n\n"
380}"##,
381 );
382
383 let workspace_root = PathBuf::from("/test/");
384 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
385 insta::assert_debug_snapshot!(diag);
386}
387
388#[test]
389fn snap_rustc_mismatched_type() {
390 let diag = parse_diagnostic(
391 r##"{
392"message": "mismatched types",
393"code": {
394 "code": "E0308",
395 "explanation": "\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail,E0308\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n"
396},
397"level": "error",
398"spans": [
399 {
400 "file_name": "runtime/compiler_support.rs",
401 "byte_start": 1589,
402 "byte_end": 1594,
403 "line_start": 48,
404 "line_end": 48,
405 "column_start": 65,
406 "column_end": 70,
407 "is_primary": true,
408 "text": [
409 {
410 "text": " let layout = alloc::Layout::from_size_align_unchecked(size, align);",
411 "highlight_start": 65,
412 "highlight_end": 70
413 }
414 ],
415 "label": "expected usize, found u32",
416 "suggested_replacement": null,
417 "suggestion_applicability": null,
418 "expansion": null
419 }
420],
421"children": [],
422"rendered": "error[E0308]: mismatched types\n --> runtime/compiler_support.rs:48:65\n |\n48 | let layout = alloc::Layout::from_size_align_unchecked(size, align);\n | ^^^^^ expected usize, found u32\n\n"
423}"##,
424 );
425
426 let workspace_root = PathBuf::from("/test/");
427 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
428 insta::assert_debug_snapshot!(diag);
429}
430
431#[test]
432fn snap_handles_macro_location() {
433 let diag = parse_diagnostic(
434 r##"{
435"rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n",
436"children": [
437 {
438 "children": [],
439 "code": null,
440 "level": "help",
441 "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
442 "rendered": null,
443 "spans": []
444 }
445],
446"code": {
447 "code": "E0277",
448 "explanation": "\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n"
449},
450"level": "error",
451"message": "can't compare `{integer}` with `&str`",
452"spans": [
453 {
454 "byte_end": 155,
455 "byte_start": 153,
456 "column_end": 33,
457 "column_start": 31,
458 "expansion": {
459 "def_site_span": {
460 "byte_end": 940,
461 "byte_start": 0,
462 "column_end": 6,
463 "column_start": 1,
464 "expansion": null,
465 "file_name": "<::core::macros::assert_eq macros>",
466 "is_primary": false,
467 "label": null,
468 "line_end": 36,
469 "line_start": 1,
470 "suggested_replacement": null,
471 "suggestion_applicability": null,
472 "text": [
473 {
474 "highlight_end": 35,
475 "highlight_start": 1,
476 "text": "($ left : expr, $ right : expr) =>"
477 },
478 {
479 "highlight_end": 3,
480 "highlight_start": 1,
481 "text": "({"
482 },
483 {
484 "highlight_end": 33,
485 "highlight_start": 1,
486 "text": " match (& $ left, & $ right)"
487 },
488 {
489 "highlight_end": 7,
490 "highlight_start": 1,
491 "text": " {"
492 },
493 {
494 "highlight_end": 34,
495 "highlight_start": 1,
496 "text": " (left_val, right_val) =>"
497 },
498 {
499 "highlight_end": 11,
500 "highlight_start": 1,
501 "text": " {"
502 },
503 {
504 "highlight_end": 46,
505 "highlight_start": 1,
506 "text": " if ! (* left_val == * right_val)"
507 },
508 {
509 "highlight_end": 15,
510 "highlight_start": 1,
511 "text": " {"
512 },
513 {
514 "highlight_end": 25,
515 "highlight_start": 1,
516 "text": " panic !"
517 },
518 {
519 "highlight_end": 57,
520 "highlight_start": 1,
521 "text": " (r#\"assertion failed: `(left == right)`"
522 },
523 {
524 "highlight_end": 16,
525 "highlight_start": 1,
526 "text": " left: `{:?}`,"
527 },
528 {
529 "highlight_end": 18,
530 "highlight_start": 1,
531 "text": " right: `{:?}`\"#,"
532 },
533 {
534 "highlight_end": 47,
535 "highlight_start": 1,
536 "text": " & * left_val, & * right_val)"
537 },
538 {
539 "highlight_end": 15,
540 "highlight_start": 1,
541 "text": " }"
542 },
543 {
544 "highlight_end": 11,
545 "highlight_start": 1,
546 "text": " }"
547 },
548 {
549 "highlight_end": 7,
550 "highlight_start": 1,
551 "text": " }"
552 },
553 {
554 "highlight_end": 42,
555 "highlight_start": 1,
556 "text": " }) ; ($ left : expr, $ right : expr,) =>"
557 },
558 {
559 "highlight_end": 49,
560 "highlight_start": 1,
561 "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
562 },
563 {
564 "highlight_end": 53,
565 "highlight_start": 1,
566 "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
567 },
568 {
569 "highlight_end": 3,
570 "highlight_start": 1,
571 "text": "({"
572 },
573 {
574 "highlight_end": 37,
575 "highlight_start": 1,
576 "text": " match (& ($ left), & ($ right))"
577 },
578 {
579 "highlight_end": 7,
580 "highlight_start": 1,
581 "text": " {"
582 },
583 {
584 "highlight_end": 34,
585 "highlight_start": 1,
586 "text": " (left_val, right_val) =>"
587 },
588 {
589 "highlight_end": 11,
590 "highlight_start": 1,
591 "text": " {"
592 },
593 {
594 "highlight_end": 46,
595 "highlight_start": 1,
596 "text": " if ! (* left_val == * right_val)"
597 },
598 {
599 "highlight_end": 15,
600 "highlight_start": 1,
601 "text": " {"
602 },
603 {
604 "highlight_end": 25,
605 "highlight_start": 1,
606 "text": " panic !"
607 },
608 {
609 "highlight_end": 57,
610 "highlight_start": 1,
611 "text": " (r#\"assertion failed: `(left == right)`"
612 },
613 {
614 "highlight_end": 16,
615 "highlight_start": 1,
616 "text": " left: `{:?}`,"
617 },
618 {
619 "highlight_end": 22,
620 "highlight_start": 1,
621 "text": " right: `{:?}`: {}\"#,"
622 },
623 {
624 "highlight_end": 72,
625 "highlight_start": 1,
626 "text": " & * left_val, & * right_val, $ crate :: format_args !"
627 },
628 {
629 "highlight_end": 33,
630 "highlight_start": 1,
631 "text": " ($ ($ arg) +))"
632 },
633 {
634 "highlight_end": 15,
635 "highlight_start": 1,
636 "text": " }"
637 },
638 {
639 "highlight_end": 11,
640 "highlight_start": 1,
641 "text": " }"
642 },
643 {
644 "highlight_end": 7,
645 "highlight_start": 1,
646 "text": " }"
647 },
648 {
649 "highlight_end": 6,
650 "highlight_start": 1,
651 "text": " }) ;"
652 }
653 ]
654 },
655 "macro_decl_name": "assert_eq!",
656 "span": {
657 "byte_end": 38,
658 "byte_start": 16,
659 "column_end": 27,
660 "column_start": 5,
661 "expansion": null,
662 "file_name": "src/main.rs",
663 "is_primary": false,
664 "label": null,
665 "line_end": 2,
666 "line_start": 2,
667 "suggested_replacement": null,
668 "suggestion_applicability": null,
669 "text": [
670 {
671 "highlight_end": 27,
672 "highlight_start": 5,
673 "text": " assert_eq!(1, \"love\");"
674 }
675 ]
676 }
677 },
678 "file_name": "<::core::macros::assert_eq macros>",
679 "is_primary": true,
680 "label": "no implementation for `{integer} == &str`",
681 "line_end": 7,
682 "line_start": 7,
683 "suggested_replacement": null,
684 "suggestion_applicability": null,
685 "text": [
686 {
687 "highlight_end": 33,
688 "highlight_start": 31,
689 "text": " if ! (* left_val == * right_val)"
690 }
691 ]
692 }
693]
694}"##,
695 );
696
697 let workspace_root = PathBuf::from("/test/");
698 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
699 insta::assert_debug_snapshot!(diag);
700}
701
702#[test]
703fn snap_macro_compiler_error() {
704 let diag = parse_diagnostic(
705 r##"{
706 "rendered": "error: Please register your known path in the path module\n --> crates/ra_hir_def/src/path.rs:265:9\n |\n265 | compile_error!(\"Please register your known path in the path module\")\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | \n ::: crates/ra_hir_def/src/data.rs:80:16\n |\n80 | let path = path![std::future::Future];\n | -------------------------- in this macro invocation\n\n",
707 "children": [],
708 "code": null,
709 "level": "error",
710 "message": "Please register your known path in the path module",
711 "spans": [
712 {
713 "byte_end": 8285,
714 "byte_start": 8217,
715 "column_end": 77,
716 "column_start": 9,
717 "expansion": {
718 "def_site_span": {
719 "byte_end": 8294,
720 "byte_start": 7858,
721 "column_end": 2,
722 "column_start": 1,
723 "expansion": null,
724 "file_name": "crates/ra_hir_def/src/path.rs",
725 "is_primary": false,
726 "label": null,
727 "line_end": 267,
728 "line_start": 254,
729 "suggested_replacement": null,
730 "suggestion_applicability": null,
731 "text": [
732 {
733 "highlight_end": 28,
734 "highlight_start": 1,
735 "text": "macro_rules! __known_path {"
736 },
737 {
738 "highlight_end": 37,
739 "highlight_start": 1,
740 "text": " (std::iter::IntoIterator) => {};"
741 },
742 {
743 "highlight_end": 33,
744 "highlight_start": 1,
745 "text": " (std::result::Result) => {};"
746 },
747 {
748 "highlight_end": 29,
749 "highlight_start": 1,
750 "text": " (std::ops::Range) => {};"
751 },
752 {
753 "highlight_end": 33,
754 "highlight_start": 1,
755 "text": " (std::ops::RangeFrom) => {};"
756 },
757 {
758 "highlight_end": 33,
759 "highlight_start": 1,
760 "text": " (std::ops::RangeFull) => {};"
761 },
762 {
763 "highlight_end": 31,
764 "highlight_start": 1,
765 "text": " (std::ops::RangeTo) => {};"
766 },
767 {
768 "highlight_end": 40,
769 "highlight_start": 1,
770 "text": " (std::ops::RangeToInclusive) => {};"
771 },
772 {
773 "highlight_end": 38,
774 "highlight_start": 1,
775 "text": " (std::ops::RangeInclusive) => {};"
776 },
777 {
778 "highlight_end": 27,
779 "highlight_start": 1,
780 "text": " (std::ops::Try) => {};"
781 },
782 {
783 "highlight_end": 22,
784 "highlight_start": 1,
785 "text": " ($path:path) => {"
786 },
787 {
788 "highlight_end": 77,
789 "highlight_start": 1,
790 "text": " compile_error!(\"Please register your known path in the path module\")"
791 },
792 {
793 "highlight_end": 7,
794 "highlight_start": 1,
795 "text": " };"
796 },
797 {
798 "highlight_end": 2,
799 "highlight_start": 1,
800 "text": "}"
801 }
802 ]
803 },
804 "macro_decl_name": "$crate::__known_path!",
805 "span": {
806 "byte_end": 8427,
807 "byte_start": 8385,
808 "column_end": 51,
809 "column_start": 9,
810 "expansion": {
811 "def_site_span": {
812 "byte_end": 8611,
813 "byte_start": 8312,
814 "column_end": 2,
815 "column_start": 1,
816 "expansion": null,
817 "file_name": "crates/ra_hir_def/src/path.rs",
818 "is_primary": false,
819 "label": null,
820 "line_end": 277,
821 "line_start": 270,
822 "suggested_replacement": null,
823 "suggestion_applicability": null,
824 "text": [
825 {
826 "highlight_end": 22,
827 "highlight_start": 1,
828 "text": "macro_rules! __path {"
829 },
830 {
831 "highlight_end": 43,
832 "highlight_start": 1,
833 "text": " ($start:ident $(:: $seg:ident)*) => ({"
834 },
835 {
836 "highlight_end": 51,
837 "highlight_start": 1,
838 "text": " $crate::__known_path!($start $(:: $seg)*);"
839 },
840 {
841 "highlight_end": 87,
842 "highlight_start": 1,
843 "text": " $crate::path::ModPath::from_simple_segments($crate::path::PathKind::Abs, vec!["
844 },
845 {
846 "highlight_end": 76,
847 "highlight_start": 1,
848 "text": " $crate::path::__name![$start], $($crate::path::__name![$seg],)*"
849 },
850 {
851 "highlight_end": 11,
852 "highlight_start": 1,
853 "text": " ])"
854 },
855 {
856 "highlight_end": 8,
857 "highlight_start": 1,
858 "text": " });"
859 },
860 {
861 "highlight_end": 2,
862 "highlight_start": 1,
863 "text": "}"
864 }
865 ]
866 },
867 "macro_decl_name": "path!",
868 "span": {
869 "byte_end": 2966,
870 "byte_start": 2940,
871 "column_end": 42,
872 "column_start": 16,
873 "expansion": null,
874 "file_name": "crates/ra_hir_def/src/data.rs",
875 "is_primary": false,
876 "label": null,
877 "line_end": 80,
878 "line_start": 80,
879 "suggested_replacement": null,
880 "suggestion_applicability": null,
881 "text": [
882 {
883 "highlight_end": 42,
884 "highlight_start": 16,
885 "text": " let path = path![std::future::Future];"
886 }
887 ]
888 }
889 },
890 "file_name": "crates/ra_hir_def/src/path.rs",
891 "is_primary": false,
892 "label": null,
893 "line_end": 272,
894 "line_start": 272,
895 "suggested_replacement": null,
896 "suggestion_applicability": null,
897 "text": [
898 {
899 "highlight_end": 51,
900 "highlight_start": 9,
901 "text": " $crate::__known_path!($start $(:: $seg)*);"
902 }
903 ]
904 }
905 },
906 "file_name": "crates/ra_hir_def/src/path.rs",
907 "is_primary": true,
908 "label": null,
909 "line_end": 265,
910 "line_start": 265,
911 "suggested_replacement": null,
912 "suggestion_applicability": null,
913 "text": [
914 {
915 "highlight_end": 77,
916 "highlight_start": 9,
917 "text": " compile_error!(\"Please register your known path in the path module\")"
918 }
919 ]
920 }
921 ]
922}
923 "##,
924 );
925
926 let workspace_root = PathBuf::from("/test/");
927 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
928 insta::assert_debug_snapshot!(diag);
929}
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs
new file mode 100644
index 000000000..9bc0fd405
--- /dev/null
+++ b/crates/ra_cargo_watch/src/lib.rs
@@ -0,0 +1,394 @@
1//! cargo_check provides the functionality needed to run `cargo check` or
2//! another compatible command (f.x. clippy) in a background thread and provide
3//! LSP diagnostics based on the output of the command.
4use cargo_metadata::Message;
5use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
6use lsp_types::{
7 Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd,
8 WorkDoneProgressReport,
9};
10use parking_lot::RwLock;
11use std::{
12 collections::HashMap,
13 path::PathBuf,
14 process::{Command, Stdio},
15 sync::Arc,
16 thread::JoinHandle,
17 time::Instant,
18};
19
20mod conv;
21
22use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic, SuggestedFix};
23
24pub use crate::conv::url_from_path_with_drive_lowercasing;
25
26#[derive(Clone, Debug)]
27pub struct CheckOptions {
28 pub enable: bool,
29 pub args: Vec<String>,
30 pub command: String,
31 pub all_targets: bool,
32}
33
34/// CheckWatcher wraps the shared state and communication machinery used for
35/// running `cargo check` (or other compatible command) and providing
36/// diagnostics based on the output.
37/// The spawned thread is shut down when this struct is dropped.
38#[derive(Debug)]
39pub struct CheckWatcher {
40 pub task_recv: Receiver<CheckTask>,
41 pub shared: Arc<RwLock<CheckWatcherSharedState>>,
42 cmd_send: Option<Sender<CheckCommand>>,
43 handle: Option<JoinHandle<()>>,
44}
45
46impl CheckWatcher {
47 pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher {
48 let options = options.clone();
49 let shared = Arc::new(RwLock::new(CheckWatcherSharedState::new()));
50
51 let (task_send, task_recv) = unbounded::<CheckTask>();
52 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
53 let shared_ = shared.clone();
54 let handle = std::thread::spawn(move || {
55 let mut check = CheckWatcherState::new(options, workspace_root, shared_);
56 check.run(&task_send, &cmd_recv);
57 });
58 CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle), shared }
59 }
60
61 /// Schedule a re-start of the cargo check worker.
62 pub fn update(&self) {
63 if let Some(cmd_send) = &self.cmd_send {
64 cmd_send.send(CheckCommand::Update).unwrap();
65 }
66 }
67}
68
69impl std::ops::Drop for CheckWatcher {
70 fn drop(&mut self) {
71 if let Some(handle) = self.handle.take() {
72 // Take the sender out of the option
73 let recv = self.cmd_send.take();
74
75 // Dropping the sender finishes the thread loop
76 drop(recv);
77
78 // Join the thread, it should finish shortly. We don't really care
79 // whether it panicked, so it is safe to ignore the result
80 let _ = handle.join();
81 }
82 }
83}
84
85#[derive(Debug)]
86pub struct CheckWatcherSharedState {
87 diagnostic_collection: HashMap<Url, Vec<Diagnostic>>,
88 suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>,
89}
90
91impl CheckWatcherSharedState {
92 fn new() -> CheckWatcherSharedState {
93 CheckWatcherSharedState {
94 diagnostic_collection: HashMap::new(),
95 suggested_fix_collection: HashMap::new(),
96 }
97 }
98
99 /// Clear the cached diagnostics, and schedule updating diagnostics by the
100 /// server, to clear stale results.
101 pub fn clear(&mut self, task_send: &Sender<CheckTask>) {
102 let cleared_files: Vec<Url> = self.diagnostic_collection.keys().cloned().collect();
103
104 self.diagnostic_collection.clear();
105 self.suggested_fix_collection.clear();
106
107 for uri in cleared_files {
108 task_send.send(CheckTask::Update(uri.clone())).unwrap();
109 }
110 }
111
112 pub fn diagnostics_for(&self, uri: &Url) -> Option<&[Diagnostic]> {
113 self.diagnostic_collection.get(uri).map(|d| d.as_slice())
114 }
115
116 pub fn fixes_for(&self, uri: &Url) -> Option<&[SuggestedFix]> {
117 self.suggested_fix_collection.get(uri).map(|d| d.as_slice())
118 }
119
120 fn add_diagnostic(&mut self, file_uri: Url, diagnostic: Diagnostic) {
121 let diagnostics = self.diagnostic_collection.entry(file_uri).or_default();
122
123 // If we're building multiple targets it's possible we've already seen this diagnostic
124 let is_duplicate = diagnostics.iter().any(|d| are_diagnostics_equal(d, &diagnostic));
125 if is_duplicate {
126 return;
127 }
128
129 diagnostics.push(diagnostic);
130 }
131
132 fn add_suggested_fix_for_diagnostic(
133 &mut self,
134 mut suggested_fix: SuggestedFix,
135 diagnostic: &Diagnostic,
136 ) {
137 let file_uri = suggested_fix.location.uri.clone();
138 let file_suggestions = self.suggested_fix_collection.entry(file_uri).or_default();
139
140 let existing_suggestion: Option<&mut SuggestedFix> =
141 file_suggestions.iter_mut().find(|s| s == &&suggested_fix);
142 if let Some(existing_suggestion) = existing_suggestion {
143 // The existing suggestion also applies to this new diagnostic
144 existing_suggestion.diagnostics.push(diagnostic.clone());
145 } else {
146 // We haven't seen this suggestion before
147 suggested_fix.diagnostics.push(diagnostic.clone());
148 file_suggestions.push(suggested_fix);
149 }
150 }
151}
152
153#[derive(Debug)]
154pub enum CheckTask {
155 /// Request a update of the given files diagnostics
156 Update(Url),
157
158 /// Request check progress notification to client
159 Status(WorkDoneProgress),
160}
161
162pub enum CheckCommand {
163 /// Request re-start of check thread
164 Update,
165}
166
167struct CheckWatcherState {
168 options: CheckOptions,
169 workspace_root: PathBuf,
170 watcher: WatchThread,
171 last_update_req: Option<Instant>,
172 shared: Arc<RwLock<CheckWatcherSharedState>>,
173}
174
175impl CheckWatcherState {
176 fn new(
177 options: CheckOptions,
178 workspace_root: PathBuf,
179 shared: Arc<RwLock<CheckWatcherSharedState>>,
180 ) -> CheckWatcherState {
181 let watcher = WatchThread::new(&options, &workspace_root);
182 CheckWatcherState { options, workspace_root, watcher, last_update_req: None, shared }
183 }
184
185 fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
186 loop {
187 select! {
188 recv(&cmd_recv) -> cmd => match cmd {
189 Ok(cmd) => self.handle_command(cmd),
190 Err(RecvError) => {
191 // Command channel has closed, so shut down
192 break;
193 },
194 },
195 recv(self.watcher.message_recv) -> msg => match msg {
196 Ok(msg) => self.handle_message(msg, task_send),
197 Err(RecvError) => {
198 // Watcher finished, replace it with a never channel to
199 // avoid busy-waiting.
200 std::mem::replace(&mut self.watcher.message_recv, never());
201 },
202 }
203 };
204
205 if self.should_recheck() {
206 self.last_update_req.take();
207 self.shared.write().clear(task_send);
208
209 // By replacing the watcher, we drop the previous one which
210 // causes it to shut down automatically.
211 self.watcher = WatchThread::new(&self.options, &self.workspace_root);
212 }
213 }
214 }
215
216 fn should_recheck(&mut self) -> bool {
217 if let Some(_last_update_req) = &self.last_update_req {
218 // We currently only request an update on save, as we need up to
219 // date source on disk for cargo check to do it's magic, so we
220 // don't really need to debounce the requests at this point.
221 return true;
222 }
223 false
224 }
225
226 fn handle_command(&mut self, cmd: CheckCommand) {
227 match cmd {
228 CheckCommand::Update => self.last_update_req = Some(Instant::now()),
229 }
230 }
231
232 fn handle_message(&mut self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
233 match msg {
234 CheckEvent::Begin => {
235 task_send
236 .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
237 title: "Running 'cargo check'".to_string(),
238 cancellable: Some(false),
239 message: None,
240 percentage: None,
241 })))
242 .unwrap();
243 }
244
245 CheckEvent::End => {
246 task_send
247 .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
248 message: None,
249 })))
250 .unwrap();
251 }
252
253 CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
254 task_send
255 .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
256 cancellable: Some(false),
257 message: Some(msg.target.name),
258 percentage: None,
259 })))
260 .unwrap();
261 }
262
263 CheckEvent::Msg(Message::CompilerMessage(msg)) => {
264 let map_result =
265 match map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root) {
266 Some(map_result) => map_result,
267 None => return,
268 };
269
270 let MappedRustDiagnostic { location, diagnostic, suggested_fixes } = map_result;
271 let file_uri = location.uri.clone();
272
273 if !suggested_fixes.is_empty() {
274 for suggested_fix in suggested_fixes {
275 self.shared
276 .write()
277 .add_suggested_fix_for_diagnostic(suggested_fix, &diagnostic);
278 }
279 }
280 self.shared.write().add_diagnostic(file_uri, diagnostic);
281
282 task_send.send(CheckTask::Update(location.uri)).unwrap();
283 }
284
285 CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
286 CheckEvent::Msg(Message::Unknown) => {}
287 }
288 }
289}
290
291/// WatchThread exists to wrap around the communication needed to be able to
292/// run `cargo check` without blocking. Currently the Rust standard library
293/// doesn't provide a way to read sub-process output without blocking, so we
294/// have to wrap sub-processes output handling in a thread and pass messages
295/// back over a channel.
296/// The correct way to dispose of the thread is to drop it, on which the
297/// sub-process will be killed, and the thread will be joined.
298struct WatchThread {
299 handle: Option<JoinHandle<()>>,
300 message_recv: Receiver<CheckEvent>,
301}
302
303enum CheckEvent {
304 Begin,
305 Msg(cargo_metadata::Message),
306 End,
307}
308
309impl WatchThread {
310 fn new(options: &CheckOptions, workspace_root: &PathBuf) -> WatchThread {
311 let mut args: Vec<String> = vec![
312 options.command.clone(),
313 "--message-format=json".to_string(),
314 "--manifest-path".to_string(),
315 format!("{}/Cargo.toml", workspace_root.to_string_lossy()),
316 ];
317 if options.all_targets {
318 args.push("--all-targets".to_string());
319 }
320 args.extend(options.args.iter().cloned());
321
322 let (message_send, message_recv) = unbounded();
323 let enabled = options.enable;
324 let handle = std::thread::spawn(move || {
325 if !enabled {
326 return;
327 }
328
329 let mut command = Command::new("cargo")
330 .args(&args)
331 .stdout(Stdio::piped())
332 .stderr(Stdio::null())
333 .spawn()
334 .expect("couldn't launch cargo");
335
336 // If we trigger an error here, we will do so in the loop instead,
337 // which will break out of the loop, and continue the shutdown
338 let _ = message_send.send(CheckEvent::Begin);
339
340 for message in cargo_metadata::parse_messages(command.stdout.take().unwrap()) {
341 let message = match message {
342 Ok(message) => message,
343 Err(err) => {
344 log::error!("Invalid json from cargo check, ignoring: {}", err);
345 continue;
346 }
347 };
348
349 match message_send.send(CheckEvent::Msg(message)) {
350 Ok(()) => {}
351 Err(_err) => {
352 // The send channel was closed, so we want to shutdown
353 break;
354 }
355 }
356 }
357
358 // We can ignore any error here, as we are already in the progress
359 // of shutting down.
360 let _ = message_send.send(CheckEvent::End);
361
362 // It is okay to ignore the result, as it only errors if the process is already dead
363 let _ = command.kill();
364
365 // Again, we don't care about the exit status so just ignore the result
366 let _ = command.wait();
367 });
368 WatchThread { handle: Some(handle), message_recv }
369 }
370}
371
372impl std::ops::Drop for WatchThread {
373 fn drop(&mut self) {
374 if let Some(handle) = self.handle.take() {
375 // Replace our reciever with dummy one, so we can drop and close the
376 // one actually communicating with the thread
377 let recv = std::mem::replace(&mut self.message_recv, never());
378
379 // Dropping the original reciever initiates thread sub-process shutdown
380 drop(recv);
381
382 // Join the thread, it should finish shortly. We don't really care
383 // whether it panicked, so it is safe to ignore the result
384 let _ = handle.join();
385 }
386 }
387}
388
389fn are_diagnostics_equal(left: &Diagnostic, right: &Diagnostic) -> bool {
390 left.source == right.source
391 && left.severity == right.severity
392 && left.range == right.range
393 && left.message == right.message
394}
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 76d8f85f1..488f74cfb 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -118,7 +118,7 @@ impl_froms!(
118 BuiltinType 118 BuiltinType
119); 119);
120 120
121pub use hir_def::attr::Attrs; 121pub use hir_def::{attr::Attrs, visibility::Visibility};
122 122
123impl Module { 123impl Module {
124 pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module { 124 pub(crate) fn new(krate: Crate, crate_module_id: LocalModuleId) -> Module {
@@ -255,6 +255,15 @@ impl StructField {
255 } 255 }
256} 256}
257 257
258impl HasVisibility for StructField {
259 fn visibility(&self, db: &impl HirDatabase) -> Visibility {
260 let variant_data = self.parent.variant_data(db);
261 let visibility = &variant_data.fields()[self.id].visibility;
262 let parent_id: hir_def::VariantId = self.parent.into();
263 visibility.resolve(db, &parent_id.resolver(db))
264 }
265}
266
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 267#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
259pub struct Struct { 268pub struct Struct {
260 pub(crate) id: StructId, 269 pub(crate) id: StructId,
@@ -1041,3 +1050,11 @@ impl<T: Into<AttrDef> + Copy> Docs for T {
1041 db.documentation(def.into()) 1050 db.documentation(def.into())
1042 } 1051 }
1043} 1052}
1053
1054pub trait HasVisibility {
1055 fn visibility(&self, db: &impl HirDatabase) -> Visibility;
1056 fn is_visible_from(&self, db: &impl HirDatabase, module: Module) -> bool {
1057 let vis = self.visibility(db);
1058 vis.is_visible_from(db, module.id)
1059 }
1060}
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 0008a8858..3d13978d4 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -40,8 +40,8 @@ mod from_source;
40pub use crate::{ 40pub use crate::{
41 code_model::{ 41 code_model::{
42 Adt, AssocItem, AttrDef, Const, Crate, CrateDependency, DefWithBody, Docs, Enum, 42 Adt, AssocItem, AttrDef, Const, Crate, CrateDependency, DefWithBody, Docs, Enum,
43 EnumVariant, FieldSource, Function, GenericDef, HasAttrs, ImplBlock, Local, MacroDef, 43 EnumVariant, FieldSource, Function, GenericDef, HasAttrs, HasVisibility, ImplBlock, Local,
44 Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias, 44 MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, StructField, Trait, Type, TypeAlias,
45 TypeParam, Union, VariantDef, 45 TypeParam, Union, VariantDef,
46 }, 46 },
47 from_source::FromSource, 47 from_source::FromSource,
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 85b378483..2c422af8b 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -215,8 +215,32 @@ impl SourceAnalyzer {
215 self.body_source_map.as_ref()?.node_pat(src) 215 self.body_source_map.as_ref()?.node_pat(src)
216 } 216 }
217 217
218 fn expand_expr(
219 &self,
220 db: &impl HirDatabase,
221 expr: InFile<&ast::Expr>,
222 ) -> Option<InFile<ast::Expr>> {
223 let macro_call = ast::MacroCall::cast(expr.value.syntax().clone())?;
224 let macro_file =
225 self.body_source_map.as_ref()?.node_macro_file(expr.with_value(&macro_call))?;
226 let expanded = db.parse_or_expand(macro_file)?;
227 let kind = expanded.kind();
228 let expr = InFile::new(macro_file, ast::Expr::cast(expanded)?);
229
230 if ast::MacroCall::can_cast(kind) {
231 self.expand_expr(db, expr.as_ref())
232 } else {
233 Some(expr)
234 }
235 }
236
218 pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> { 237 pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> {
219 let expr_id = self.expr_id(expr)?; 238 let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) {
239 self.body_source_map.as_ref()?.node_expr(expr.as_ref())?
240 } else {
241 self.expr_id(expr)?
242 };
243
220 let ty = self.infer.as_ref()?[expr_id].clone(); 244 let ty = self.infer.as_ref()?[expr_id].clone();
221 let environment = TraitEnvironment::lower(db, &self.resolver); 245 let environment = TraitEnvironment::lower(db, &self.resolver);
222 Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) 246 Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } })
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs
index d9ea693e3..aac5f3e15 100644
--- a/crates/ra_hir_def/src/adt.rs
+++ b/crates/ra_hir_def/src/adt.rs
@@ -9,11 +9,12 @@ use hir_expand::{
9}; 9};
10use ra_arena::{map::ArenaMap, Arena}; 10use ra_arena::{map::ArenaMap, Arena};
11use ra_prof::profile; 11use ra_prof::profile;
12use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; 12use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner};
13 13
14use crate::{ 14use crate::{
15 db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace, type_ref::TypeRef, EnumId, 15 db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace, type_ref::TypeRef,
16 LocalEnumVariantId, LocalStructFieldId, Lookup, StructId, UnionId, VariantId, 16 visibility::RawVisibility, EnumId, LocalEnumVariantId, LocalStructFieldId, Lookup, StructId,
17 UnionId, VariantId,
17}; 18};
18 19
19/// Note that we use `StructData` for unions as well! 20/// Note that we use `StructData` for unions as well!
@@ -47,13 +48,14 @@ pub enum VariantData {
47pub struct StructFieldData { 48pub struct StructFieldData {
48 pub name: Name, 49 pub name: Name,
49 pub type_ref: TypeRef, 50 pub type_ref: TypeRef,
51 pub visibility: RawVisibility,
50} 52}
51 53
52impl StructData { 54impl StructData {
53 pub(crate) fn struct_data_query(db: &impl DefDatabase, id: StructId) -> Arc<StructData> { 55 pub(crate) fn struct_data_query(db: &impl DefDatabase, id: StructId) -> Arc<StructData> {
54 let src = id.lookup(db).source(db); 56 let src = id.lookup(db).source(db);
55 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); 57 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
56 let variant_data = VariantData::new(src.value.kind()); 58 let variant_data = VariantData::new(db, src.map(|s| s.kind()));
57 let variant_data = Arc::new(variant_data); 59 let variant_data = Arc::new(variant_data);
58 Arc::new(StructData { name, variant_data }) 60 Arc::new(StructData { name, variant_data })
59 } 61 }
@@ -61,10 +63,12 @@ impl StructData {
61 let src = id.lookup(db).source(db); 63 let src = id.lookup(db).source(db);
62 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); 64 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
63 let variant_data = VariantData::new( 65 let variant_data = VariantData::new(
64 src.value 66 db,
65 .record_field_def_list() 67 src.map(|s| {
66 .map(ast::StructKind::Record) 68 s.record_field_def_list()
67 .unwrap_or(ast::StructKind::Unit), 69 .map(ast::StructKind::Record)
70 .unwrap_or(ast::StructKind::Unit)
71 }),
68 ); 72 );
69 let variant_data = Arc::new(variant_data); 73 let variant_data = Arc::new(variant_data);
70 Arc::new(StructData { name, variant_data }) 74 Arc::new(StructData { name, variant_data })
@@ -77,7 +81,7 @@ impl EnumData {
77 let src = e.lookup(db).source(db); 81 let src = e.lookup(db).source(db);
78 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); 82 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name());
79 let mut trace = Trace::new_for_arena(); 83 let mut trace = Trace::new_for_arena();
80 lower_enum(&mut trace, &src.value); 84 lower_enum(db, &mut trace, &src);
81 Arc::new(EnumData { name, variants: trace.into_arena() }) 85 Arc::new(EnumData { name, variants: trace.into_arena() })
82 } 86 }
83 87
@@ -93,30 +97,31 @@ impl HasChildSource for EnumId {
93 fn child_source(&self, db: &impl DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> { 97 fn child_source(&self, db: &impl DefDatabase) -> InFile<ArenaMap<Self::ChildId, Self::Value>> {
94 let src = self.lookup(db).source(db); 98 let src = self.lookup(db).source(db);
95 let mut trace = Trace::new_for_map(); 99 let mut trace = Trace::new_for_map();
96 lower_enum(&mut trace, &src.value); 100 lower_enum(db, &mut trace, &src);
97 src.with_value(trace.into_map()) 101 src.with_value(trace.into_map())
98 } 102 }
99} 103}
100 104
101fn lower_enum( 105fn lower_enum(
106 db: &impl DefDatabase,
102 trace: &mut Trace<LocalEnumVariantId, EnumVariantData, ast::EnumVariant>, 107 trace: &mut Trace<LocalEnumVariantId, EnumVariantData, ast::EnumVariant>,
103 ast: &ast::EnumDef, 108 ast: &InFile<ast::EnumDef>,
104) { 109) {
105 for var in ast.variant_list().into_iter().flat_map(|it| it.variants()) { 110 for var in ast.value.variant_list().into_iter().flat_map(|it| it.variants()) {
106 trace.alloc( 111 trace.alloc(
107 || var.clone(), 112 || var.clone(),
108 || EnumVariantData { 113 || EnumVariantData {
109 name: var.name().map_or_else(Name::missing, |it| it.as_name()), 114 name: var.name().map_or_else(Name::missing, |it| it.as_name()),
110 variant_data: Arc::new(VariantData::new(var.kind())), 115 variant_data: Arc::new(VariantData::new(db, ast.with_value(var.kind()))),
111 }, 116 },
112 ); 117 );
113 } 118 }
114} 119}
115 120
116impl VariantData { 121impl VariantData {
117 fn new(flavor: ast::StructKind) -> Self { 122 fn new(db: &impl DefDatabase, flavor: InFile<ast::StructKind>) -> Self {
118 let mut trace = Trace::new_for_arena(); 123 let mut trace = Trace::new_for_arena();
119 match lower_struct(&mut trace, &flavor) { 124 match lower_struct(db, &mut trace, &flavor) {
120 StructKind::Tuple => VariantData::Tuple(trace.into_arena()), 125 StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
121 StructKind::Record => VariantData::Record(trace.into_arena()), 126 StructKind::Record => VariantData::Record(trace.into_arena()),
122 StructKind::Unit => VariantData::Unit, 127 StructKind::Unit => VariantData::Unit,
@@ -163,7 +168,7 @@ impl HasChildSource for VariantId {
163 }), 168 }),
164 }; 169 };
165 let mut trace = Trace::new_for_map(); 170 let mut trace = Trace::new_for_map();
166 lower_struct(&mut trace, &src.value); 171 lower_struct(db, &mut trace, &src);
167 src.with_value(trace.into_map()) 172 src.with_value(trace.into_map())
168 } 173 }
169} 174}
@@ -175,14 +180,15 @@ enum StructKind {
175} 180}
176 181
177fn lower_struct( 182fn lower_struct(
183 db: &impl DefDatabase,
178 trace: &mut Trace< 184 trace: &mut Trace<
179 LocalStructFieldId, 185 LocalStructFieldId,
180 StructFieldData, 186 StructFieldData,
181 Either<ast::TupleFieldDef, ast::RecordFieldDef>, 187 Either<ast::TupleFieldDef, ast::RecordFieldDef>,
182 >, 188 >,
183 ast: &ast::StructKind, 189 ast: &InFile<ast::StructKind>,
184) -> StructKind { 190) -> StructKind {
185 match ast { 191 match &ast.value {
186 ast::StructKind::Tuple(fl) => { 192 ast::StructKind::Tuple(fl) => {
187 for (i, fd) in fl.fields().enumerate() { 193 for (i, fd) in fl.fields().enumerate() {
188 trace.alloc( 194 trace.alloc(
@@ -190,6 +196,7 @@ fn lower_struct(
190 || StructFieldData { 196 || StructFieldData {
191 name: Name::new_tuple_field(i), 197 name: Name::new_tuple_field(i),
192 type_ref: TypeRef::from_ast_opt(fd.type_ref()), 198 type_ref: TypeRef::from_ast_opt(fd.type_ref()),
199 visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
193 }, 200 },
194 ); 201 );
195 } 202 }
@@ -202,6 +209,7 @@ fn lower_struct(
202 || StructFieldData { 209 || StructFieldData {
203 name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing), 210 name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
204 type_ref: TypeRef::from_ast_opt(fd.ascribed_type()), 211 type_ref: TypeRef::from_ast_opt(fd.ascribed_type()),
212 visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
205 }, 213 },
206 ); 214 );
207 } 215 }
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index d3e4c50ae..142c52d35 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -163,6 +163,7 @@ pub struct BodySourceMap {
163 pat_map: FxHashMap<PatSource, PatId>, 163 pat_map: FxHashMap<PatSource, PatId>,
164 pat_map_back: ArenaMap<PatId, PatSource>, 164 pat_map_back: ArenaMap<PatId, PatSource>,
165 field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>, 165 field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>,
166 expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
166} 167}
167 168
168impl Body { 169impl Body {
@@ -237,6 +238,11 @@ impl BodySourceMap {
237 self.expr_map.get(&src).cloned() 238 self.expr_map.get(&src).cloned()
238 } 239 }
239 240
241 pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> {
242 let src = node.map(|it| AstPtr::new(it));
243 self.expansions.get(&src).cloned()
244 }
245
240 pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option<ExprId> { 246 pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option<ExprId> {
241 let src = node.map(|it| Either::Right(AstPtr::new(it))); 247 let src = node.map(|it| Either::Right(AstPtr::new(it)));
242 self.expr_map.get(&src).cloned() 248 self.expr_map.get(&src).cloned()
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 5323af097..e656f9a41 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -446,14 +446,20 @@ where
446 } 446 }
447 } 447 }
448 // FIXME expand to statements in statement position 448 // FIXME expand to statements in statement position
449 ast::Expr::MacroCall(e) => match self.expander.enter_expand(self.db, e) { 449 ast::Expr::MacroCall(e) => {
450 Some((mark, expansion)) => { 450 let macro_call = self.expander.to_source(AstPtr::new(&e));
451 let id = self.collect_expr(expansion); 451 match self.expander.enter_expand(self.db, e.clone()) {
452 self.expander.exit(self.db, mark); 452 Some((mark, expansion)) => {
453 id 453 self.source_map
454 .expansions
455 .insert(macro_call, self.expander.current_file_id);
456 let id = self.collect_expr(expansion);
457 self.expander.exit(self.db, mark);
458 id
459 }
460 None => self.alloc_expr(Expr::Missing, syntax_ptr),
454 } 461 }
455 None => self.alloc_expr(Expr::Missing, syntax_ptr), 462 }
456 },
457 463
458 // FIXME implement HIR for these: 464 // FIXME implement HIR for these:
459 ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), 465 ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
@@ -543,7 +549,10 @@ where
543 }; 549 };
544 self.body.item_scope.define_def(def); 550 self.body.item_scope.define_def(def);
545 if let Some(name) = name { 551 if let Some(name) = name {
546 self.body.item_scope.push_res(name.as_name(), def.into()); 552 let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
553 self.body
554 .item_scope
555 .push_res(name.as_name(), crate::per_ns::PerNs::from_def(def, vis));
547 } 556 }
548 } 557 }
549 } 558 }
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs
index b0288ee8d..fe7bb9779 100644
--- a/crates/ra_hir_def/src/item_scope.rs
+++ b/crates/ra_hir_def/src/item_scope.rs
@@ -5,7 +5,10 @@ use hir_expand::name::Name;
5use once_cell::sync::Lazy; 5use once_cell::sync::Lazy;
6use rustc_hash::FxHashMap; 6use rustc_hash::FxHashMap;
7 7
8use crate::{per_ns::PerNs, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, TraitId}; 8use crate::{
9 per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId,
10 TraitId,
11};
9 12
10#[derive(Debug, Default, PartialEq, Eq)] 13#[derive(Debug, Default, PartialEq, Eq)]
11pub struct ItemScope { 14pub struct ItemScope {
@@ -30,7 +33,7 @@ pub struct ItemScope {
30static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { 33static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
31 BuiltinType::ALL 34 BuiltinType::ALL
32 .iter() 35 .iter()
33 .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into()))) 36 .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public)))
34 .collect() 37 .collect()
35}); 38});
36 39
@@ -144,8 +147,8 @@ impl ItemScope {
144 changed 147 changed
145 } 148 }
146 149
147 pub(crate) fn collect_resolutions(&self) -> Vec<(Name, PerNs)> { 150 pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a {
148 self.visible.iter().map(|(name, res)| (name.clone(), res.clone())).collect() 151 self.visible.iter().map(|(name, res)| (name.clone(), res.clone()))
149 } 152 }
150 153
151 pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { 154 pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> {
@@ -153,20 +156,20 @@ impl ItemScope {
153 } 156 }
154} 157}
155 158
156impl From<ModuleDefId> for PerNs { 159impl PerNs {
157 fn from(def: ModuleDefId) -> PerNs { 160 pub(crate) fn from_def(def: ModuleDefId, v: Visibility) -> PerNs {
158 match def { 161 match def {
159 ModuleDefId::ModuleId(_) => PerNs::types(def), 162 ModuleDefId::ModuleId(_) => PerNs::types(def, v),
160 ModuleDefId::FunctionId(_) => PerNs::values(def), 163 ModuleDefId::FunctionId(_) => PerNs::values(def, v),
161 ModuleDefId::AdtId(adt) => match adt { 164 ModuleDefId::AdtId(adt) => match adt {
162 AdtId::StructId(_) | AdtId::UnionId(_) => PerNs::both(def, def), 165 AdtId::StructId(_) | AdtId::UnionId(_) => PerNs::both(def, def, v),
163 AdtId::EnumId(_) => PerNs::types(def), 166 AdtId::EnumId(_) => PerNs::types(def, v),
164 }, 167 },
165 ModuleDefId::EnumVariantId(_) => PerNs::both(def, def), 168 ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v),
166 ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def), 169 ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v),
167 ModuleDefId::TraitId(_) => PerNs::types(def), 170 ModuleDefId::TraitId(_) => PerNs::types(def, v),
168 ModuleDefId::TypeAliasId(_) => PerNs::types(def), 171 ModuleDefId::TypeAliasId(_) => PerNs::types(def, v),
169 ModuleDefId::BuiltinType(_) => PerNs::types(def), 172 ModuleDefId::BuiltinType(_) => PerNs::types(def, v),
170 } 173 }
171 } 174 }
172} 175}
diff --git a/crates/ra_hir_def/src/lang_item.rs b/crates/ra_hir_def/src/lang_item.rs
index cef061837..37c861a87 100644
--- a/crates/ra_hir_def/src/lang_item.rs
+++ b/crates/ra_hir_def/src/lang_item.rs
@@ -22,6 +22,50 @@ pub enum LangItemTarget {
22 TraitId(TraitId), 22 TraitId(TraitId),
23} 23}
24 24
25impl LangItemTarget {
26 pub fn as_enum(self) -> Option<EnumId> {
27 match self {
28 LangItemTarget::EnumId(id) => Some(id),
29 _ => None,
30 }
31 }
32
33 pub fn as_function(self) -> Option<FunctionId> {
34 match self {
35 LangItemTarget::FunctionId(id) => Some(id),
36 _ => None,
37 }
38 }
39
40 pub fn as_impl_block(self) -> Option<ImplId> {
41 match self {
42 LangItemTarget::ImplBlockId(id) => Some(id),
43 _ => None,
44 }
45 }
46
47 pub fn as_static(self) -> Option<StaticId> {
48 match self {
49 LangItemTarget::StaticId(id) => Some(id),
50 _ => None,
51 }
52 }
53
54 pub fn as_struct(self) -> Option<StructId> {
55 match self {
56 LangItemTarget::StructId(id) => Some(id),
57 _ => None,
58 }
59 }
60
61 pub fn as_trait(self) -> Option<TraitId> {
62 match self {
63 LangItemTarget::TraitId(id) => Some(id),
64 _ => None,
65 }
66 }
67}
68
25#[derive(Default, Debug, Clone, PartialEq, Eq)] 69#[derive(Default, Debug, Clone, PartialEq, Eq)]
26pub struct LangItems { 70pub struct LangItems {
27 items: FxHashMap<SmolStr, LangItemTarget>, 71 items: FxHashMap<SmolStr, LangItemTarget>,
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index f6c7f38d1..61f044ecf 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -36,6 +36,8 @@ pub mod nameres;
36pub mod src; 36pub mod src;
37pub mod child_by_source; 37pub mod child_by_source;
38 38
39pub mod visibility;
40
39#[cfg(test)] 41#[cfg(test)]
40mod test_db; 42mod test_db;
41#[cfg(test)] 43#[cfg(test)]
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index b9f40d3dd..8a22b0585 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -24,6 +24,7 @@ use crate::{
24 }, 24 },
25 path::{ModPath, PathKind}, 25 path::{ModPath, PathKind},
26 per_ns::PerNs, 26 per_ns::PerNs,
27 visibility::Visibility,
27 AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern, 28 AdtId, AstId, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern,
28 LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, 29 LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
29}; 30};
@@ -108,7 +109,7 @@ struct MacroDirective {
108struct DefCollector<'a, DB> { 109struct DefCollector<'a, DB> {
109 db: &'a DB, 110 db: &'a DB,
110 def_map: CrateDefMap, 111 def_map: CrateDefMap,
111 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, raw::Import)>>, 112 glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
112 unresolved_imports: Vec<ImportDirective>, 113 unresolved_imports: Vec<ImportDirective>,
113 resolved_imports: Vec<ImportDirective>, 114 resolved_imports: Vec<ImportDirective>,
114 unexpanded_macros: Vec<MacroDirective>, 115 unexpanded_macros: Vec<MacroDirective>,
@@ -214,7 +215,11 @@ where
214 // In Rust, `#[macro_export]` macros are unconditionally visible at the 215 // In Rust, `#[macro_export]` macros are unconditionally visible at the
215 // crate root, even if the parent modules is **not** visible. 216 // crate root, even if the parent modules is **not** visible.
216 if export { 217 if export {
217 self.update(self.def_map.root, &[(name, PerNs::macros(macro_))]); 218 self.update(
219 self.def_map.root,
220 &[(name, PerNs::macros(macro_, Visibility::Public))],
221 Visibility::Public,
222 );
218 } 223 }
219 } 224 }
220 225
@@ -348,9 +353,12 @@ where
348 353
349 fn record_resolved_import(&mut self, directive: &ImportDirective) { 354 fn record_resolved_import(&mut self, directive: &ImportDirective) {
350 let module_id = directive.module_id; 355 let module_id = directive.module_id;
351 let import_id = directive.import_id;
352 let import = &directive.import; 356 let import = &directive.import;
353 let def = directive.status.namespaces(); 357 let def = directive.status.namespaces();
358 let vis = self
359 .def_map
360 .resolve_visibility(self.db, module_id, &directive.import.visibility)
361 .unwrap_or(Visibility::Public);
354 362
355 if import.is_glob { 363 if import.is_glob {
356 log::debug!("glob import: {:?}", import); 364 log::debug!("glob import: {:?}", import);
@@ -366,9 +374,16 @@ where
366 let scope = &item_map[m.local_id].scope; 374 let scope = &item_map[m.local_id].scope;
367 375
368 // Module scoped macros is included 376 // Module scoped macros is included
369 let items = scope.collect_resolutions(); 377 let items = scope
370 378 .resolutions()
371 self.update(module_id, &items); 379 // only keep visible names...
380 .map(|(n, res)| {
381 (n, res.filter_visibility(|v| v.is_visible_from_other_crate()))
382 })
383 .filter(|(_, res)| !res.is_none())
384 .collect::<Vec<_>>();
385
386 self.update(module_id, &items, vis);
372 } else { 387 } else {
373 // glob import from same crate => we do an initial 388 // glob import from same crate => we do an initial
374 // import, and then need to propagate any further 389 // import, and then need to propagate any further
@@ -376,13 +391,25 @@ where
376 let scope = &self.def_map[m.local_id].scope; 391 let scope = &self.def_map[m.local_id].scope;
377 392
378 // Module scoped macros is included 393 // Module scoped macros is included
379 let items = scope.collect_resolutions(); 394 let items = scope
380 395 .resolutions()
381 self.update(module_id, &items); 396 // only keep visible names...
397 .map(|(n, res)| {
398 (
399 n,
400 res.filter_visibility(|v| {
401 v.is_visible_from_def_map(&self.def_map, module_id)
402 }),
403 )
404 })
405 .filter(|(_, res)| !res.is_none())
406 .collect::<Vec<_>>();
407
408 self.update(module_id, &items, vis);
382 // record the glob import in case we add further items 409 // record the glob import in case we add further items
383 let glob = self.glob_imports.entry(m.local_id).or_default(); 410 let glob = self.glob_imports.entry(m.local_id).or_default();
384 if !glob.iter().any(|it| *it == (module_id, import_id)) { 411 if !glob.iter().any(|(mid, _)| *mid == module_id) {
385 glob.push((module_id, import_id)); 412 glob.push((module_id, vis));
386 } 413 }
387 } 414 }
388 } 415 }
@@ -396,11 +423,11 @@ where
396 .map(|(local_id, variant_data)| { 423 .map(|(local_id, variant_data)| {
397 let name = variant_data.name.clone(); 424 let name = variant_data.name.clone();
398 let variant = EnumVariantId { parent: e, local_id }; 425 let variant = EnumVariantId { parent: e, local_id };
399 let res = PerNs::both(variant.into(), variant.into()); 426 let res = PerNs::both(variant.into(), variant.into(), vis);
400 (name, res) 427 (name, res)
401 }) 428 })
402 .collect::<Vec<_>>(); 429 .collect::<Vec<_>>();
403 self.update(module_id, &resolutions); 430 self.update(module_id, &resolutions, vis);
404 } 431 }
405 Some(d) => { 432 Some(d) => {
406 log::debug!("glob import {:?} from non-module/enum {:?}", import, d); 433 log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
@@ -422,21 +449,24 @@ where
422 } 449 }
423 } 450 }
424 451
425 self.update(module_id, &[(name, def)]); 452 self.update(module_id, &[(name, def)], vis);
426 } 453 }
427 None => tested_by!(bogus_paths), 454 None => tested_by!(bogus_paths),
428 } 455 }
429 } 456 }
430 } 457 }
431 458
432 fn update(&mut self, module_id: LocalModuleId, resolutions: &[(Name, PerNs)]) { 459 fn update(&mut self, module_id: LocalModuleId, resolutions: &[(Name, PerNs)], vis: Visibility) {
433 self.update_recursive(module_id, resolutions, 0) 460 self.update_recursive(module_id, resolutions, vis, 0)
434 } 461 }
435 462
436 fn update_recursive( 463 fn update_recursive(
437 &mut self, 464 &mut self,
438 module_id: LocalModuleId, 465 module_id: LocalModuleId,
439 resolutions: &[(Name, PerNs)], 466 resolutions: &[(Name, PerNs)],
467 // All resolutions are imported with this visibility; the visibilies in
468 // the `PerNs` values are ignored and overwritten
469 vis: Visibility,
440 depth: usize, 470 depth: usize,
441 ) { 471 ) {
442 if depth > 100 { 472 if depth > 100 {
@@ -446,7 +476,7 @@ where
446 let scope = &mut self.def_map.modules[module_id].scope; 476 let scope = &mut self.def_map.modules[module_id].scope;
447 let mut changed = false; 477 let mut changed = false;
448 for (name, res) in resolutions { 478 for (name, res) in resolutions {
449 changed |= scope.push_res(name.clone(), *res); 479 changed |= scope.push_res(name.clone(), res.with_visibility(vis));
450 } 480 }
451 481
452 if !changed { 482 if !changed {
@@ -459,9 +489,13 @@ where
459 .flat_map(|v| v.iter()) 489 .flat_map(|v| v.iter())
460 .cloned() 490 .cloned()
461 .collect::<Vec<_>>(); 491 .collect::<Vec<_>>();
462 for (glob_importing_module, _glob_import) in glob_imports { 492 for (glob_importing_module, glob_import_vis) in glob_imports {
463 // We pass the glob import so that the tracked import in those modules is that glob import 493 // we know all resolutions have the same visibility (`vis`), so we
464 self.update_recursive(glob_importing_module, resolutions, depth + 1); 494 // just need to check that once
495 if !vis.is_visible_from_def_map(&self.def_map, glob_importing_module) {
496 continue;
497 }
498 self.update_recursive(glob_importing_module, resolutions, glob_import_vis, depth + 1);
465 } 499 }
466 } 500 }
467 501
@@ -633,9 +667,13 @@ where
633 let is_macro_use = attrs.by_key("macro_use").exists(); 667 let is_macro_use = attrs.by_key("macro_use").exists();
634 match module { 668 match module {
635 // inline module, just recurse 669 // inline module, just recurse
636 raw::ModuleData::Definition { name, items, ast_id } => { 670 raw::ModuleData::Definition { name, visibility, items, ast_id } => {
637 let module_id = 671 let module_id = self.push_child_module(
638 self.push_child_module(name.clone(), AstId::new(self.file_id, *ast_id), None); 672 name.clone(),
673 AstId::new(self.file_id, *ast_id),
674 None,
675 &visibility,
676 );
639 677
640 ModCollector { 678 ModCollector {
641 def_collector: &mut *self.def_collector, 679 def_collector: &mut *self.def_collector,
@@ -650,7 +688,7 @@ where
650 } 688 }
651 } 689 }
652 // out of line module, resolve, parse and recurse 690 // out of line module, resolve, parse and recurse
653 raw::ModuleData::Declaration { name, ast_id } => { 691 raw::ModuleData::Declaration { name, visibility, ast_id } => {
654 let ast_id = AstId::new(self.file_id, *ast_id); 692 let ast_id = AstId::new(self.file_id, *ast_id);
655 match self.mod_dir.resolve_declaration( 693 match self.mod_dir.resolve_declaration(
656 self.def_collector.db, 694 self.def_collector.db,
@@ -659,7 +697,12 @@ where
659 path_attr, 697 path_attr,
660 ) { 698 ) {
661 Ok((file_id, mod_dir)) => { 699 Ok((file_id, mod_dir)) => {
662 let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); 700 let module_id = self.push_child_module(
701 name.clone(),
702 ast_id,
703 Some(file_id),
704 &visibility,
705 );
663 let raw_items = self.def_collector.db.raw_items(file_id.into()); 706 let raw_items = self.def_collector.db.raw_items(file_id.into());
664 ModCollector { 707 ModCollector {
665 def_collector: &mut *self.def_collector, 708 def_collector: &mut *self.def_collector,
@@ -690,7 +733,13 @@ where
690 name: Name, 733 name: Name,
691 declaration: AstId<ast::Module>, 734 declaration: AstId<ast::Module>,
692 definition: Option<FileId>, 735 definition: Option<FileId>,
736 visibility: &crate::visibility::RawVisibility,
693 ) -> LocalModuleId { 737 ) -> LocalModuleId {
738 let vis = self
739 .def_collector
740 .def_map
741 .resolve_visibility(self.def_collector.db, self.module_id, visibility)
742 .unwrap_or(Visibility::Public);
694 let modules = &mut self.def_collector.def_map.modules; 743 let modules = &mut self.def_collector.def_map.modules;
695 let res = modules.alloc(ModuleData::default()); 744 let res = modules.alloc(ModuleData::default());
696 modules[res].parent = Some(self.module_id); 745 modules[res].parent = Some(self.module_id);
@@ -702,7 +751,7 @@ where
702 let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res }; 751 let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res };
703 let def: ModuleDefId = module.into(); 752 let def: ModuleDefId = module.into();
704 self.def_collector.def_map.modules[self.module_id].scope.define_def(def); 753 self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
705 self.def_collector.update(self.module_id, &[(name, def.into())]); 754 self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis);
706 res 755 res
707 } 756 }
708 757
@@ -716,6 +765,7 @@ where
716 765
717 let name = def.name.clone(); 766 let name = def.name.clone();
718 let container = ContainerId::ModuleId(module); 767 let container = ContainerId::ModuleId(module);
768 let vis = &def.visibility;
719 let def: ModuleDefId = match def.kind { 769 let def: ModuleDefId = match def.kind {
720 raw::DefKind::Function(ast_id) => FunctionLoc { 770 raw::DefKind::Function(ast_id) => FunctionLoc {
721 container: container.into(), 771 container: container.into(),
@@ -761,7 +811,12 @@ where
761 .into(), 811 .into(),
762 }; 812 };
763 self.def_collector.def_map.modules[self.module_id].scope.define_def(def); 813 self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
764 self.def_collector.update(self.module_id, &[(name, def.into())]) 814 let vis = self
815 .def_collector
816 .def_map
817 .resolve_visibility(self.def_collector.db, self.module_id, vis)
818 .unwrap_or(Visibility::Public);
819 self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis))], vis)
765 } 820 }
766 821
767 fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) { 822 fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs
index 695014c7b..fd6422d60 100644
--- a/crates/ra_hir_def/src/nameres/path_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/path_resolution.rs
@@ -21,6 +21,7 @@ use crate::{
21 nameres::{BuiltinShadowMode, CrateDefMap}, 21 nameres::{BuiltinShadowMode, CrateDefMap},
22 path::{ModPath, PathKind}, 22 path::{ModPath, PathKind},
23 per_ns::PerNs, 23 per_ns::PerNs,
24 visibility::{RawVisibility, Visibility},
24 AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId, 25 AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
25}; 26};
26 27
@@ -61,7 +62,35 @@ impl ResolvePathResult {
61 62
62impl CrateDefMap { 63impl CrateDefMap {
63 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { 64 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
64 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) 65 self.extern_prelude
66 .get(name)
67 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
68 }
69
70 pub(crate) fn resolve_visibility(
71 &self,
72 db: &impl DefDatabase,
73 original_module: LocalModuleId,
74 visibility: &RawVisibility,
75 ) -> Option<Visibility> {
76 match visibility {
77 RawVisibility::Module(path) => {
78 let (result, remaining) =
79 self.resolve_path(db, original_module, &path, BuiltinShadowMode::Module);
80 if remaining.is_some() {
81 return None;
82 }
83 let types = result.take_types()?;
84 match types {
85 ModuleDefId::ModuleId(m) => Some(Visibility::Module(m)),
86 _ => {
87 // error: visibility needs to refer to module
88 None
89 }
90 }
91 }
92 RawVisibility::Public => Some(Visibility::Public),
93 }
65 } 94 }
66 95
67 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change 96 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
@@ -88,17 +117,21 @@ impl CrateDefMap {
88 PathKind::DollarCrate(krate) => { 117 PathKind::DollarCrate(krate) => {
89 if krate == self.krate { 118 if krate == self.krate {
90 tested_by!(macro_dollar_crate_self); 119 tested_by!(macro_dollar_crate_self);
91 PerNs::types(ModuleId { krate: self.krate, local_id: self.root }.into()) 120 PerNs::types(
121 ModuleId { krate: self.krate, local_id: self.root }.into(),
122 Visibility::Public,
123 )
92 } else { 124 } else {
93 let def_map = db.crate_def_map(krate); 125 let def_map = db.crate_def_map(krate);
94 let module = ModuleId { krate, local_id: def_map.root }; 126 let module = ModuleId { krate, local_id: def_map.root };
95 tested_by!(macro_dollar_crate_other); 127 tested_by!(macro_dollar_crate_other);
96 PerNs::types(module.into()) 128 PerNs::types(module.into(), Visibility::Public)
97 } 129 }
98 } 130 }
99 PathKind::Crate => { 131 PathKind::Crate => PerNs::types(
100 PerNs::types(ModuleId { krate: self.krate, local_id: self.root }.into()) 132 ModuleId { krate: self.krate, local_id: self.root }.into(),
101 } 133 Visibility::Public,
134 ),
102 // plain import or absolute path in 2015: crate-relative with 135 // plain import or absolute path in 2015: crate-relative with
103 // fallback to extern prelude (with the simplification in 136 // fallback to extern prelude (with the simplification in
104 // rust-lang/rust#57745) 137 // rust-lang/rust#57745)
@@ -126,7 +159,10 @@ impl CrateDefMap {
126 let m = successors(Some(original_module), |m| self.modules[*m].parent) 159 let m = successors(Some(original_module), |m| self.modules[*m].parent)
127 .nth(lvl as usize); 160 .nth(lvl as usize);
128 if let Some(local_id) = m { 161 if let Some(local_id) = m {
129 PerNs::types(ModuleId { krate: self.krate, local_id }.into()) 162 PerNs::types(
163 ModuleId { krate: self.krate, local_id }.into(),
164 Visibility::Public,
165 )
130 } else { 166 } else {
131 log::debug!("super path in root module"); 167 log::debug!("super path in root module");
132 return ResolvePathResult::empty(ReachedFixedPoint::Yes); 168 return ResolvePathResult::empty(ReachedFixedPoint::Yes);
@@ -140,7 +176,7 @@ impl CrateDefMap {
140 }; 176 };
141 if let Some(def) = self.extern_prelude.get(&segment) { 177 if let Some(def) = self.extern_prelude.get(&segment) {
142 log::debug!("absolute path {:?} resolved to crate {:?}", path, def); 178 log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
143 PerNs::types(*def) 179 PerNs::types(*def, Visibility::Public)
144 } else { 180 } else {
145 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude 181 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
146 } 182 }
@@ -148,7 +184,7 @@ impl CrateDefMap {
148 }; 184 };
149 185
150 for (i, segment) in segments { 186 for (i, segment) in segments {
151 let curr = match curr_per_ns.take_types() { 187 let (curr, vis) = match curr_per_ns.take_types_vis() {
152 Some(r) => r, 188 Some(r) => r,
153 None => { 189 None => {
154 // we still have path segments left, but the path so far 190 // we still have path segments left, but the path so far
@@ -189,11 +225,11 @@ impl CrateDefMap {
189 match enum_data.variant(&segment) { 225 match enum_data.variant(&segment) {
190 Some(local_id) => { 226 Some(local_id) => {
191 let variant = EnumVariantId { parent: e, local_id }; 227 let variant = EnumVariantId { parent: e, local_id };
192 PerNs::both(variant.into(), variant.into()) 228 PerNs::both(variant.into(), variant.into(), Visibility::Public)
193 } 229 }
194 None => { 230 None => {
195 return ResolvePathResult::with( 231 return ResolvePathResult::with(
196 PerNs::types(e.into()), 232 PerNs::types(e.into(), vis),
197 ReachedFixedPoint::Yes, 233 ReachedFixedPoint::Yes,
198 Some(i), 234 Some(i),
199 Some(self.krate), 235 Some(self.krate),
@@ -211,7 +247,7 @@ impl CrateDefMap {
211 ); 247 );
212 248
213 return ResolvePathResult::with( 249 return ResolvePathResult::with(
214 PerNs::types(s), 250 PerNs::types(s, vis),
215 ReachedFixedPoint::Yes, 251 ReachedFixedPoint::Yes,
216 Some(i), 252 Some(i),
217 Some(self.krate), 253 Some(self.krate),
@@ -235,11 +271,15 @@ impl CrateDefMap {
235 // - current module / scope 271 // - current module / scope
236 // - extern prelude 272 // - extern prelude
237 // - std prelude 273 // - std prelude
238 let from_legacy_macro = 274 let from_legacy_macro = self[module]
239 self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros); 275 .scope
276 .get_legacy_macro(name)
277 .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
240 let from_scope = self[module].scope.get(name, shadow); 278 let from_scope = self[module].scope.get(name, shadow);
241 let from_extern_prelude = 279 let from_extern_prelude = self
242 self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); 280 .extern_prelude
281 .get(name)
282 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public));
243 let from_prelude = self.resolve_in_prelude(db, name, shadow); 283 let from_prelude = self.resolve_in_prelude(db, name, shadow);
244 284
245 from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude) 285 from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude)
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index 73dc08745..fac1169ef 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -16,12 +16,15 @@ use hir_expand::{
16use ra_arena::{impl_arena_id, Arena, RawId}; 16use ra_arena::{impl_arena_id, Arena, RawId};
17use ra_prof::profile; 17use ra_prof::profile;
18use ra_syntax::{ 18use ra_syntax::{
19 ast::{self, AttrsOwner, NameOwner}, 19 ast::{self, AttrsOwner, NameOwner, VisibilityOwner},
20 AstNode, 20 AstNode,
21}; 21};
22use test_utils::tested_by; 22use test_utils::tested_by;
23 23
24use crate::{attr::Attrs, db::DefDatabase, path::ModPath, FileAstId, HirFileId, InFile}; 24use crate::{
25 attr::Attrs, db::DefDatabase, path::ModPath, visibility::RawVisibility, FileAstId, HirFileId,
26 InFile,
27};
25 28
26/// `RawItems` is a set of top-level items in a file (except for impls). 29/// `RawItems` is a set of top-level items in a file (except for impls).
27/// 30///
@@ -122,8 +125,17 @@ impl_arena_id!(Module);
122 125
123#[derive(Debug, PartialEq, Eq)] 126#[derive(Debug, PartialEq, Eq)]
124pub(super) enum ModuleData { 127pub(super) enum ModuleData {
125 Declaration { name: Name, ast_id: FileAstId<ast::Module> }, 128 Declaration {
126 Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> }, 129 name: Name,
130 visibility: RawVisibility,
131 ast_id: FileAstId<ast::Module>,
132 },
133 Definition {
134 name: Name,
135 visibility: RawVisibility,
136 ast_id: FileAstId<ast::Module>,
137 items: Vec<RawItem>,
138 },
127} 139}
128 140
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 141#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -138,6 +150,7 @@ pub struct ImportData {
138 pub(super) is_prelude: bool, 150 pub(super) is_prelude: bool,
139 pub(super) is_extern_crate: bool, 151 pub(super) is_extern_crate: bool,
140 pub(super) is_macro_use: bool, 152 pub(super) is_macro_use: bool,
153 pub(super) visibility: RawVisibility,
141} 154}
142 155
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 156#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -148,6 +161,7 @@ impl_arena_id!(Def);
148pub(super) struct DefData { 161pub(super) struct DefData {
149 pub(super) name: Name, 162 pub(super) name: Name,
150 pub(super) kind: DefKind, 163 pub(super) kind: DefKind,
164 pub(super) visibility: RawVisibility,
151} 165}
152 166
153#[derive(Debug, PartialEq, Eq, Clone, Copy)] 167#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -218,6 +232,7 @@ impl RawItemsCollector {
218 232
219 fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) { 233 fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) {
220 let attrs = self.parse_attrs(&item); 234 let attrs = self.parse_attrs(&item);
235 let visibility = RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
221 let (kind, name) = match item { 236 let (kind, name) = match item {
222 ast::ModuleItem::Module(module) => { 237 ast::ModuleItem::Module(module) => {
223 self.add_module(current_module, module); 238 self.add_module(current_module, module);
@@ -266,7 +281,7 @@ impl RawItemsCollector {
266 }; 281 };
267 if let Some(name) = name { 282 if let Some(name) = name {
268 let name = name.as_name(); 283 let name = name.as_name();
269 let def = self.raw_items.defs.alloc(DefData { name, kind }); 284 let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
270 self.push_item(current_module, attrs, RawItemKind::Def(def)); 285 self.push_item(current_module, attrs, RawItemKind::Def(def));
271 } 286 }
272 } 287 }
@@ -277,10 +292,12 @@ impl RawItemsCollector {
277 None => return, 292 None => return,
278 }; 293 };
279 let attrs = self.parse_attrs(&module); 294 let attrs = self.parse_attrs(&module);
295 let visibility = RawVisibility::from_ast_with_hygiene(module.visibility(), &self.hygiene);
280 296
281 let ast_id = self.source_ast_id_map.ast_id(&module); 297 let ast_id = self.source_ast_id_map.ast_id(&module);
282 if module.has_semi() { 298 if module.has_semi() {
283 let item = self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id }); 299 let item =
300 self.raw_items.modules.alloc(ModuleData::Declaration { name, visibility, ast_id });
284 self.push_item(current_module, attrs, RawItemKind::Module(item)); 301 self.push_item(current_module, attrs, RawItemKind::Module(item));
285 return; 302 return;
286 } 303 }
@@ -288,6 +305,7 @@ impl RawItemsCollector {
288 if let Some(item_list) = module.item_list() { 305 if let Some(item_list) = module.item_list() {
289 let item = self.raw_items.modules.alloc(ModuleData::Definition { 306 let item = self.raw_items.modules.alloc(ModuleData::Definition {
290 name, 307 name,
308 visibility,
291 ast_id, 309 ast_id,
292 items: Vec::new(), 310 items: Vec::new(),
293 }); 311 });
@@ -302,6 +320,7 @@ impl RawItemsCollector {
302 // FIXME: cfg_attr 320 // FIXME: cfg_attr
303 let is_prelude = use_item.has_atom_attr("prelude_import"); 321 let is_prelude = use_item.has_atom_attr("prelude_import");
304 let attrs = self.parse_attrs(&use_item); 322 let attrs = self.parse_attrs(&use_item);
323 let visibility = RawVisibility::from_ast_with_hygiene(use_item.visibility(), &self.hygiene);
305 324
306 let mut buf = Vec::new(); 325 let mut buf = Vec::new();
307 ModPath::expand_use_item( 326 ModPath::expand_use_item(
@@ -315,6 +334,7 @@ impl RawItemsCollector {
315 is_prelude, 334 is_prelude,
316 is_extern_crate: false, 335 is_extern_crate: false,
317 is_macro_use: false, 336 is_macro_use: false,
337 visibility: visibility.clone(),
318 }; 338 };
319 buf.push(import_data); 339 buf.push(import_data);
320 }, 340 },
@@ -331,6 +351,8 @@ impl RawItemsCollector {
331 ) { 351 ) {
332 if let Some(name_ref) = extern_crate.name_ref() { 352 if let Some(name_ref) = extern_crate.name_ref() {
333 let path = ModPath::from_name_ref(&name_ref); 353 let path = ModPath::from_name_ref(&name_ref);
354 let visibility =
355 RawVisibility::from_ast_with_hygiene(extern_crate.visibility(), &self.hygiene);
334 let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); 356 let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
335 let attrs = self.parse_attrs(&extern_crate); 357 let attrs = self.parse_attrs(&extern_crate);
336 // FIXME: cfg_attr 358 // FIXME: cfg_attr
@@ -342,6 +364,7 @@ impl RawItemsCollector {
342 is_prelude: false, 364 is_prelude: false,
343 is_extern_crate: true, 365 is_extern_crate: true,
344 is_macro_use, 366 is_macro_use,
367 visibility,
345 }; 368 };
346 self.push_import(current_module, attrs, import_data); 369 self.push_import(current_module, attrs, import_data);
347 } 370 }
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs
index ff474b53b..78bcdc850 100644
--- a/crates/ra_hir_def/src/nameres/tests.rs
+++ b/crates/ra_hir_def/src/nameres/tests.rs
@@ -12,8 +12,8 @@ use test_utils::covers;
12 12
13use crate::{db::DefDatabase, nameres::*, test_db::TestDB, LocalModuleId}; 13use crate::{db::DefDatabase, nameres::*, test_db::TestDB, LocalModuleId};
14 14
15fn def_map(fixtute: &str) -> String { 15fn def_map(fixture: &str) -> String {
16 let dm = compute_crate_def_map(fixtute); 16 let dm = compute_crate_def_map(fixture);
17 render_crate_def_map(&dm) 17 render_crate_def_map(&dm)
18} 18}
19 19
@@ -32,7 +32,7 @@ fn render_crate_def_map(map: &CrateDefMap) -> String {
32 *buf += path; 32 *buf += path;
33 *buf += "\n"; 33 *buf += "\n";
34 34
35 let mut entries = map.modules[module].scope.collect_resolutions(); 35 let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect();
36 entries.sort_by_key(|(name, _)| name.clone()); 36 entries.sort_by_key(|(name, _)| name.clone());
37 37
38 for (name, def) in entries { 38 for (name, def) in entries {
diff --git a/crates/ra_hir_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs
index 5e24cb94d..71fa0abe8 100644
--- a/crates/ra_hir_def/src/nameres/tests/globs.rs
+++ b/crates/ra_hir_def/src/nameres/tests/globs.rs
@@ -74,6 +74,83 @@ fn glob_2() {
74} 74}
75 75
76#[test] 76#[test]
77fn glob_privacy_1() {
78 let map = def_map(
79 "
80 //- /lib.rs
81 mod foo;
82 use foo::*;
83
84 //- /foo/mod.rs
85 pub mod bar;
86 pub use self::bar::*;
87 struct PrivateStructFoo;
88
89 //- /foo/bar.rs
90 pub struct Baz;
91 struct PrivateStructBar;
92 pub use super::*;
93 ",
94 );
95 assert_snapshot!(map, @r###"
96 crate
97 Baz: t v
98 bar: t
99 foo: t
100
101 crate::foo
102 Baz: t v
103 PrivateStructFoo: t v
104 bar: t
105
106 crate::foo::bar
107 Baz: t v
108 PrivateStructBar: t v
109 PrivateStructFoo: t v
110 bar: t
111 "###
112 );
113}
114
115#[test]
116fn glob_privacy_2() {
117 let map = def_map(
118 "
119 //- /lib.rs
120 mod foo;
121 use foo::*;
122 use foo::bar::*;
123
124 //- /foo/mod.rs
125 mod bar;
126 fn Foo() {};
127 pub struct Foo {};
128
129 //- /foo/bar.rs
130 pub(super) struct PrivateBaz;
131 struct PrivateBar;
132 pub(crate) struct PubCrateStruct;
133 ",
134 );
135 assert_snapshot!(map, @r###"
136 crate
137 Foo: t
138 PubCrateStruct: t v
139 foo: t
140
141 crate::foo
142 Foo: t v
143 bar: t
144
145 crate::foo::bar
146 PrivateBar: t v
147 PrivateBaz: t v
148 PubCrateStruct: t v
149 "###
150 );
151}
152
153#[test]
77fn glob_across_crates() { 154fn glob_across_crates() {
78 covers!(glob_across_crates); 155 covers!(glob_across_crates);
79 let map = def_map( 156 let map = def_map(
@@ -93,6 +170,26 @@ fn glob_across_crates() {
93} 170}
94 171
95#[test] 172#[test]
173fn glob_privacy_across_crates() {
174 covers!(glob_across_crates);
175 let map = def_map(
176 "
177 //- /main.rs crate:main deps:test_crate
178 use test_crate::*;
179
180 //- /lib.rs crate:test_crate
181 pub struct Baz;
182 struct Foo;
183 ",
184 );
185 assert_snapshot!(map, @r###"
186 â‹®crate
187 â‹®Baz: t v
188 "###
189 );
190}
191
192#[test]
96fn glob_enum() { 193fn glob_enum() {
97 covers!(glob_enum); 194 covers!(glob_enum);
98 let map = def_map( 195 let map = def_map(
diff --git a/crates/ra_hir_def/src/nameres/tests/incremental.rs b/crates/ra_hir_def/src/nameres/tests/incremental.rs
index ef2e9435c..faeb7aa4d 100644
--- a/crates/ra_hir_def/src/nameres/tests/incremental.rs
+++ b/crates/ra_hir_def/src/nameres/tests/incremental.rs
@@ -116,7 +116,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
116 let events = db.log_executed(|| { 116 let events = db.log_executed(|| {
117 let crate_def_map = db.crate_def_map(krate); 117 let crate_def_map = db.crate_def_map(krate);
118 let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); 118 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
119 assert_eq!(module_data.scope.collect_resolutions().len(), 1); 119 assert_eq!(module_data.scope.resolutions().collect::<Vec<_>>().len(), 1);
120 }); 120 });
121 assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) 121 assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
122 } 122 }
@@ -126,7 +126,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
126 let events = db.log_executed(|| { 126 let events = db.log_executed(|| {
127 let crate_def_map = db.crate_def_map(krate); 127 let crate_def_map = db.crate_def_map(krate);
128 let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); 128 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
129 assert_eq!(module_data.scope.collect_resolutions().len(), 1); 129 assert_eq!(module_data.scope.resolutions().collect::<Vec<_>>().len(), 1);
130 }); 130 });
131 assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) 131 assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events)
132 } 132 }
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index 107d2d799..82cfa67a9 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -260,12 +260,8 @@ macro_rules! __known_path {
260 (std::ops::RangeTo) => {}; 260 (std::ops::RangeTo) => {};
261 (std::ops::RangeToInclusive) => {}; 261 (std::ops::RangeToInclusive) => {};
262 (std::ops::RangeInclusive) => {}; 262 (std::ops::RangeInclusive) => {};
263 (std::boxed::Box) => {};
264 (std::future::Future) => {}; 263 (std::future::Future) => {};
265 (std::ops::Try) => {}; 264 (std::ops::Try) => {};
266 (std::ops::Neg) => {};
267 (std::ops::Not) => {};
268 (std::ops::Index) => {};
269 ($path:path) => { 265 ($path:path) => {
270 compile_error!("Please register your known path in the path module") 266 compile_error!("Please register your known path in the path module")
271 }; 267 };
diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs
index 3a5105028..6e435c8c1 100644
--- a/crates/ra_hir_def/src/per_ns.rs
+++ b/crates/ra_hir_def/src/per_ns.rs
@@ -5,13 +5,13 @@
5 5
6use hir_expand::MacroDefId; 6use hir_expand::MacroDefId;
7 7
8use crate::ModuleDefId; 8use crate::{visibility::Visibility, ModuleDefId};
9 9
10#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
11pub struct PerNs { 11pub struct PerNs {
12 pub types: Option<ModuleDefId>, 12 pub types: Option<(ModuleDefId, Visibility)>,
13 pub values: Option<ModuleDefId>, 13 pub values: Option<(ModuleDefId, Visibility)>,
14 pub macros: Option<MacroDefId>, 14 pub macros: Option<(MacroDefId, Visibility)>,
15} 15}
16 16
17impl Default for PerNs { 17impl Default for PerNs {
@@ -25,20 +25,20 @@ impl PerNs {
25 PerNs { types: None, values: None, macros: None } 25 PerNs { types: None, values: None, macros: None }
26 } 26 }
27 27
28 pub fn values(t: ModuleDefId) -> PerNs { 28 pub fn values(t: ModuleDefId, v: Visibility) -> PerNs {
29 PerNs { types: None, values: Some(t), macros: None } 29 PerNs { types: None, values: Some((t, v)), macros: None }
30 } 30 }
31 31
32 pub fn types(t: ModuleDefId) -> PerNs { 32 pub fn types(t: ModuleDefId, v: Visibility) -> PerNs {
33 PerNs { types: Some(t), values: None, macros: None } 33 PerNs { types: Some((t, v)), values: None, macros: None }
34 } 34 }
35 35
36 pub fn both(types: ModuleDefId, values: ModuleDefId) -> PerNs { 36 pub fn both(types: ModuleDefId, values: ModuleDefId, v: Visibility) -> PerNs {
37 PerNs { types: Some(types), values: Some(values), macros: None } 37 PerNs { types: Some((types, v)), values: Some((values, v)), macros: None }
38 } 38 }
39 39
40 pub fn macros(macro_: MacroDefId) -> PerNs { 40 pub fn macros(macro_: MacroDefId, v: Visibility) -> PerNs {
41 PerNs { types: None, values: None, macros: Some(macro_) } 41 PerNs { types: None, values: None, macros: Some((macro_, v)) }
42 } 42 }
43 43
44 pub fn is_none(&self) -> bool { 44 pub fn is_none(&self) -> bool {
@@ -46,15 +46,35 @@ impl PerNs {
46 } 46 }
47 47
48 pub fn take_types(self) -> Option<ModuleDefId> { 48 pub fn take_types(self) -> Option<ModuleDefId> {
49 self.types.map(|it| it.0)
50 }
51
52 pub fn take_types_vis(self) -> Option<(ModuleDefId, Visibility)> {
49 self.types 53 self.types
50 } 54 }
51 55
52 pub fn take_values(self) -> Option<ModuleDefId> { 56 pub fn take_values(self) -> Option<ModuleDefId> {
53 self.values 57 self.values.map(|it| it.0)
54 } 58 }
55 59
56 pub fn take_macros(self) -> Option<MacroDefId> { 60 pub fn take_macros(self) -> Option<MacroDefId> {
57 self.macros 61 self.macros.map(|it| it.0)
62 }
63
64 pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs {
65 PerNs {
66 types: self.types.filter(|(_, v)| f(*v)),
67 values: self.values.filter(|(_, v)| f(*v)),
68 macros: self.macros.filter(|(_, v)| f(*v)),
69 }
70 }
71
72 pub fn with_visibility(self, vis: Visibility) -> PerNs {
73 PerNs {
74 types: self.types.map(|(it, _)| (it, vis)),
75 values: self.values.map(|(it, _)| (it, vis)),
76 macros: self.macros.map(|(it, _)| (it, vis)),
77 }
58 } 78 }
59 79
60 pub fn or(self, other: PerNs) -> PerNs { 80 pub fn or(self, other: PerNs) -> PerNs {
diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs
index cf3c33d78..5d16dd087 100644
--- a/crates/ra_hir_def/src/resolver.rs
+++ b/crates/ra_hir_def/src/resolver.rs
@@ -19,6 +19,7 @@ use crate::{
19 nameres::CrateDefMap, 19 nameres::CrateDefMap,
20 path::{ModPath, PathKind}, 20 path::{ModPath, PathKind},
21 per_ns::PerNs, 21 per_ns::PerNs,
22 visibility::{RawVisibility, Visibility},
22 AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId, 23 AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId,
23 FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId, 24 FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId,
24 StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId, 25 StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId,
@@ -231,6 +232,23 @@ impl Resolver {
231 Some(res) 232 Some(res)
232 } 233 }
233 234
235 pub fn resolve_visibility(
236 &self,
237 db: &impl DefDatabase,
238 visibility: &RawVisibility,
239 ) -> Option<Visibility> {
240 match visibility {
241 RawVisibility::Module(_) => {
242 let (item_map, module) = match self.module() {
243 Some(it) => it,
244 None => return None,
245 };
246 item_map.resolve_visibility(db, module, visibility)
247 }
248 RawVisibility::Public => Some(Visibility::Public),
249 }
250 }
251
234 pub fn resolve_path_in_value_ns( 252 pub fn resolve_path_in_value_ns(
235 &self, 253 &self,
236 db: &impl DefDatabase, 254 db: &impl DefDatabase,
@@ -448,10 +466,10 @@ impl Scope {
448 f(name.clone(), ScopeDef::PerNs(def)); 466 f(name.clone(), ScopeDef::PerNs(def));
449 }); 467 });
450 m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { 468 m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| {
451 f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_))); 469 f(name.clone(), ScopeDef::PerNs(PerNs::macros(macro_, Visibility::Public)));
452 }); 470 });
453 m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| { 471 m.crate_def_map.extern_prelude.iter().for_each(|(name, &def)| {
454 f(name.clone(), ScopeDef::PerNs(PerNs::types(def.into()))); 472 f(name.clone(), ScopeDef::PerNs(PerNs::types(def.into(), Visibility::Public)));
455 }); 473 });
456 if let Some(prelude) = m.crate_def_map.prelude { 474 if let Some(prelude) = m.crate_def_map.prelude {
457 let prelude_def_map = db.crate_def_map(prelude.krate); 475 let prelude_def_map = db.crate_def_map(prelude.krate);
@@ -626,7 +644,7 @@ impl HasResolver for ContainerId {
626 fn resolver(self, db: &impl DefDatabase) -> Resolver { 644 fn resolver(self, db: &impl DefDatabase) -> Resolver {
627 match self { 645 match self {
628 ContainerId::ModuleId(it) => it.resolver(db), 646 ContainerId::ModuleId(it) => it.resolver(db),
629 ContainerId::DefWithBodyId(it) => it.resolver(db), 647 ContainerId::DefWithBodyId(it) => it.module(db).resolver(db),
630 } 648 }
631 } 649 }
632} 650}
diff --git a/crates/ra_hir_def/src/visibility.rs b/crates/ra_hir_def/src/visibility.rs
new file mode 100644
index 000000000..d8296da4b
--- /dev/null
+++ b/crates/ra_hir_def/src/visibility.rs
@@ -0,0 +1,120 @@
1//! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
2
3use hir_expand::{hygiene::Hygiene, InFile};
4use ra_syntax::ast;
5
6use crate::{
7 db::DefDatabase,
8 path::{ModPath, PathKind},
9 ModuleId,
10};
11
12/// Visibility of an item, not yet resolved.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum RawVisibility {
15 /// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is
16 /// equivalent to `pub(self)`.
17 Module(ModPath),
18 /// `pub`.
19 Public,
20}
21
22impl RawVisibility {
23 const fn private() -> RawVisibility {
24 let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() };
25 RawVisibility::Module(path)
26 }
27
28 pub(crate) fn from_ast(
29 db: &impl DefDatabase,
30 node: InFile<Option<ast::Visibility>>,
31 ) -> RawVisibility {
32 Self::from_ast_with_hygiene(node.value, &Hygiene::new(db, node.file_id))
33 }
34
35 pub(crate) fn from_ast_with_hygiene(
36 node: Option<ast::Visibility>,
37 hygiene: &Hygiene,
38 ) -> RawVisibility {
39 let node = match node {
40 None => return RawVisibility::private(),
41 Some(node) => node,
42 };
43 match node.kind() {
44 ast::VisibilityKind::In(path) => {
45 let path = ModPath::from_src(path, hygiene);
46 let path = match path {
47 None => return RawVisibility::private(),
48 Some(path) => path,
49 };
50 RawVisibility::Module(path)
51 }
52 ast::VisibilityKind::PubCrate => {
53 let path = ModPath { kind: PathKind::Crate, segments: Vec::new() };
54 RawVisibility::Module(path)
55 }
56 ast::VisibilityKind::PubSuper => {
57 let path = ModPath { kind: PathKind::Super(1), segments: Vec::new() };
58 RawVisibility::Module(path)
59 }
60 ast::VisibilityKind::Pub => RawVisibility::Public,
61 }
62 }
63
64 pub fn resolve(
65 &self,
66 db: &impl DefDatabase,
67 resolver: &crate::resolver::Resolver,
68 ) -> Visibility {
69 // we fall back to public visibility (i.e. fail open) if the path can't be resolved
70 resolver.resolve_visibility(db, self).unwrap_or(Visibility::Public)
71 }
72}
73
74/// Visibility of an item, with the path resolved.
75#[derive(Debug, Copy, Clone, PartialEq, Eq)]
76pub enum Visibility {
77 /// Visibility is restricted to a certain module.
78 Module(ModuleId),
79 /// Visibility is unrestricted.
80 Public,
81}
82
83impl Visibility {
84 pub fn is_visible_from(self, db: &impl DefDatabase, from_module: ModuleId) -> bool {
85 let to_module = match self {
86 Visibility::Module(m) => m,
87 Visibility::Public => return true,
88 };
89 // if they're not in the same crate, it can't be visible
90 if from_module.krate != to_module.krate {
91 return false;
92 }
93 let def_map = db.crate_def_map(from_module.krate);
94 self.is_visible_from_def_map(&def_map, from_module.local_id)
95 }
96
97 pub(crate) fn is_visible_from_other_crate(self) -> bool {
98 match self {
99 Visibility::Module(_) => false,
100 Visibility::Public => true,
101 }
102 }
103
104 pub(crate) fn is_visible_from_def_map(
105 self,
106 def_map: &crate::nameres::CrateDefMap,
107 from_module: crate::LocalModuleId,
108 ) -> bool {
109 let to_module = match self {
110 Visibility::Module(m) => m,
111 Visibility::Public => return true,
112 };
113 // from_module needs to be a descendant of to_module
114 let mut ancestors = std::iter::successors(Some(from_module), |m| {
115 let parent_id = def_map[*m].parent?;
116 Some(parent_id)
117 });
118 ancestors.any(|m| m == to_module.local_id)
119 }
120}
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index 32c0d07a5..37e69599d 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -24,6 +24,7 @@ use hir_def::{
24 body::Body, 24 body::Body,
25 data::{ConstData, FunctionData}, 25 data::{ConstData, FunctionData},
26 expr::{BindingAnnotation, ExprId, PatId}, 26 expr::{BindingAnnotation, ExprId, PatId},
27 lang_item::LangItemTarget,
27 path::{path, Path}, 28 path::{path, Path},
28 resolver::{HasResolver, Resolver, TypeNs}, 29 resolver::{HasResolver, Resolver, TypeNs},
29 type_ref::{Mutability, TypeRef}, 30 type_ref::{Mutability, TypeRef},
@@ -32,6 +33,7 @@ use hir_def::{
32use hir_expand::{diagnostics::DiagnosticSink, name::name}; 33use hir_expand::{diagnostics::DiagnosticSink, name::name};
33use ra_arena::map::ArenaMap; 34use ra_arena::map::ArenaMap;
34use ra_prof::profile; 35use ra_prof::profile;
36use ra_syntax::SmolStr;
35use test_utils::tested_by; 37use test_utils::tested_by;
36 38
37use super::{ 39use super::{
@@ -482,6 +484,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
482 self.infer_expr_coerce(self.body.body_expr, &Expectation::has_type(self.return_ty.clone())); 484 self.infer_expr_coerce(self.body.body_expr, &Expectation::has_type(self.return_ty.clone()));
483 } 485 }
484 486
487 fn resolve_lang_item(&self, name: &str) -> Option<LangItemTarget> {
488 let krate = self.resolver.krate()?;
489 let name = SmolStr::new_inline_from_ascii(name.len(), name.as_bytes());
490 self.db.lang_item(krate, name)
491 }
492
485 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> { 493 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
486 let path = path![std::iter::IntoIterator]; 494 let path = path![std::iter::IntoIterator];
487 let trait_ = self.resolver.resolve_known_trait(self.db, &path)?; 495 let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
@@ -495,26 +503,22 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
495 } 503 }
496 504
497 fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> { 505 fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
498 let path = path![std::ops::Neg]; 506 let trait_ = self.resolve_lang_item("neg")?.as_trait()?;
499 let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
500 self.db.trait_data(trait_).associated_type_by_name(&name![Output]) 507 self.db.trait_data(trait_).associated_type_by_name(&name![Output])
501 } 508 }
502 509
503 fn resolve_ops_not_output(&self) -> Option<TypeAliasId> { 510 fn resolve_ops_not_output(&self) -> Option<TypeAliasId> {
504 let path = path![std::ops::Not]; 511 let trait_ = self.resolve_lang_item("not")?.as_trait()?;
505 let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
506 self.db.trait_data(trait_).associated_type_by_name(&name![Output]) 512 self.db.trait_data(trait_).associated_type_by_name(&name![Output])
507 } 513 }
508 514
509 fn resolve_future_future_output(&self) -> Option<TypeAliasId> { 515 fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
510 let path = path![std::future::Future]; 516 let trait_ = self.resolve_lang_item("future_trait")?.as_trait()?;
511 let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
512 self.db.trait_data(trait_).associated_type_by_name(&name![Output]) 517 self.db.trait_data(trait_).associated_type_by_name(&name![Output])
513 } 518 }
514 519
515 fn resolve_boxed_box(&self) -> Option<AdtId> { 520 fn resolve_boxed_box(&self) -> Option<AdtId> {
516 let path = path![std::boxed::Box]; 521 let struct_ = self.resolve_lang_item("owned_box")?.as_struct()?;
517 let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
518 Some(struct_.into()) 522 Some(struct_.into())
519 } 523 }
520 524
@@ -555,8 +559,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
555 } 559 }
556 560
557 fn resolve_ops_index_output(&self) -> Option<TypeAliasId> { 561 fn resolve_ops_index_output(&self) -> Option<TypeAliasId> {
558 let path = path![std::ops::Index]; 562 let trait_ = self.resolve_lang_item("index")?.as_trait()?;
559 let trait_ = self.resolver.resolve_known_trait(self.db, &path)?;
560 self.db.trait_data(trait_).associated_type_by_name(&name![Output]) 563 self.db.trait_data(trait_).associated_type_by_name(&name![Output])
561 } 564 }
562} 565}
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index d447b4571..d1f10e675 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -11,8 +11,8 @@ use std::fmt::Write;
11use std::sync::Arc; 11use std::sync::Arc;
12 12
13use hir_def::{ 13use hir_def::{
14 body::BodySourceMap, child_by_source::ChildBySource, db::DefDatabase, keys, 14 body::BodySourceMap, child_by_source::ChildBySource, db::DefDatabase, item_scope::ItemScope,
15 nameres::CrateDefMap, AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, 15 keys, nameres::CrateDefMap, AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
16}; 16};
17use hir_expand::InFile; 17use hir_expand::InFile;
18use insta::assert_snapshot; 18use insta::assert_snapshot;
@@ -163,35 +163,69 @@ fn visit_module(
163 module_id: LocalModuleId, 163 module_id: LocalModuleId,
164 cb: &mut dyn FnMut(DefWithBodyId), 164 cb: &mut dyn FnMut(DefWithBodyId),
165) { 165) {
166 for decl in crate_def_map[module_id].scope.declarations() { 166 visit_scope(db, crate_def_map, &crate_def_map[module_id].scope, cb);
167 match decl {
168 ModuleDefId::FunctionId(it) => cb(it.into()),
169 ModuleDefId::ConstId(it) => cb(it.into()),
170 ModuleDefId::StaticId(it) => cb(it.into()),
171 ModuleDefId::TraitId(it) => {
172 let trait_data = db.trait_data(it);
173 for &(_, item) in trait_data.items.iter() {
174 match item {
175 AssocItemId::FunctionId(it) => cb(it.into()),
176 AssocItemId::ConstId(it) => cb(it.into()),
177 AssocItemId::TypeAliasId(_) => (),
178 }
179 }
180 }
181 ModuleDefId::ModuleId(it) => visit_module(db, crate_def_map, it.local_id, cb),
182 _ => (),
183 }
184 }
185 for impl_id in crate_def_map[module_id].scope.impls() { 167 for impl_id in crate_def_map[module_id].scope.impls() {
186 let impl_data = db.impl_data(impl_id); 168 let impl_data = db.impl_data(impl_id);
187 for &item in impl_data.items.iter() { 169 for &item in impl_data.items.iter() {
188 match item { 170 match item {
189 AssocItemId::FunctionId(it) => cb(it.into()), 171 AssocItemId::FunctionId(it) => {
190 AssocItemId::ConstId(it) => cb(it.into()), 172 let def = it.into();
173 cb(def);
174 let body = db.body(def);
175 visit_scope(db, crate_def_map, &body.item_scope, cb);
176 }
177 AssocItemId::ConstId(it) => {
178 let def = it.into();
179 cb(def);
180 let body = db.body(def);
181 visit_scope(db, crate_def_map, &body.item_scope, cb);
182 }
191 AssocItemId::TypeAliasId(_) => (), 183 AssocItemId::TypeAliasId(_) => (),
192 } 184 }
193 } 185 }
194 } 186 }
187
188 fn visit_scope(
189 db: &TestDB,
190 crate_def_map: &CrateDefMap,
191 scope: &ItemScope,
192 cb: &mut dyn FnMut(DefWithBodyId),
193 ) {
194 for decl in scope.declarations() {
195 match decl {
196 ModuleDefId::FunctionId(it) => {
197 let def = it.into();
198 cb(def);
199 let body = db.body(def);
200 visit_scope(db, crate_def_map, &body.item_scope, cb);
201 }
202 ModuleDefId::ConstId(it) => {
203 let def = it.into();
204 cb(def);
205 let body = db.body(def);
206 visit_scope(db, crate_def_map, &body.item_scope, cb);
207 }
208 ModuleDefId::StaticId(it) => {
209 let def = it.into();
210 cb(def);
211 let body = db.body(def);
212 visit_scope(db, crate_def_map, &body.item_scope, cb);
213 }
214 ModuleDefId::TraitId(it) => {
215 let trait_data = db.trait_data(it);
216 for &(_, item) in trait_data.items.iter() {
217 match item {
218 AssocItemId::FunctionId(it) => cb(it.into()),
219 AssocItemId::ConstId(it) => cb(it.into()),
220 AssocItemId::TypeAliasId(_) => (),
221 }
222 }
223 }
224 ModuleDefId::ModuleId(it) => visit_module(db, crate_def_map, it.local_id, cb),
225 _ => (),
226 }
227 }
228 }
195} 229}
196 230
197fn ellipsize(mut text: String, max_len: usize) -> String { 231fn ellipsize(mut text: String, max_len: usize) -> String {
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index 09d684ac2..8b3aa8564 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -1,7 +1,8 @@
1use super::infer;
2use insta::assert_snapshot; 1use insta::assert_snapshot;
3use test_utils::covers; 2use test_utils::covers;
4 3
4use super::infer;
5
5#[test] 6#[test]
6fn bug_484() { 7fn bug_484() {
7 assert_snapshot!( 8 assert_snapshot!(
@@ -331,3 +332,36 @@ pub fn main_loop() {
331 "### 332 "###
332 ); 333 );
333} 334}
335
336#[test]
337fn issue_2669() {
338 assert_snapshot!(
339 infer(
340 r#"trait A {}
341 trait Write {}
342 struct Response<T> {}
343
344 trait D {
345 fn foo();
346 }
347
348 impl<T:A> D for Response<T> {
349 fn foo() {
350 end();
351 fn end<W: Write>() {
352 let _x: T = loop {};
353 }
354 }
355 }"#
356 ),
357 @r###"
358 [147; 262) '{ ... }': ()
359 [161; 164) 'end': fn end<{unknown}>() -> ()
360 [161; 166) 'end()': ()
361 [199; 252) '{ ... }': ()
362 [221; 223) '_x': !
363 [230; 237) 'loop {}': !
364 [235; 237) '{}': ()
365 "###
366 )
367}
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 3e5e163e3..f7e042c12 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -20,6 +20,7 @@ fn test() {
20mod prelude {} 20mod prelude {}
21 21
22mod boxed { 22mod boxed {
23 #[lang = "owned_box"]
23 pub struct Box<T: ?Sized> { 24 pub struct Box<T: ?Sized> {
24 inner: *mut T, 25 inner: *mut T,
25 } 26 }
@@ -1518,6 +1519,7 @@ fn test() {
1518 [167; 179) 'GLOBAL_CONST': u32 1519 [167; 179) 'GLOBAL_CONST': u32
1519 [189; 191) 'id': u32 1520 [189; 191) 'id': u32
1520 [194; 210) 'Foo::A..._CONST': u32 1521 [194; 210) 'Foo::A..._CONST': u32
1522 [126; 128) '99': u32
1521 "### 1523 "###
1522 ); 1524 );
1523} 1525}
@@ -1549,6 +1551,8 @@ fn test() {
1549 [233; 246) 'GLOBAL_STATIC': u32 1551 [233; 246) 'GLOBAL_STATIC': u32
1550 [256; 257) 'w': u32 1552 [256; 257) 'w': u32
1551 [260; 277) 'GLOBAL...IC_MUT': u32 1553 [260; 277) 'GLOBAL...IC_MUT': u32
1554 [118; 120) '99': u32
1555 [161; 163) '99': u32
1552 "### 1556 "###
1553 ); 1557 );
1554} 1558}
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 0bc72644a..4b268510c 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -27,6 +27,7 @@ fn test() {
27//- /std.rs crate:std 27//- /std.rs crate:std
28#[prelude_import] use future::*; 28#[prelude_import] use future::*;
29mod future { 29mod future {
30 #[lang = "future_trait"]
30 trait Future { 31 trait Future {
31 type Output; 32 type Output;
32 } 33 }
@@ -56,6 +57,7 @@ fn test() {
56//- /std.rs crate:std 57//- /std.rs crate:std
57#[prelude_import] use future::*; 58#[prelude_import] use future::*;
58mod future { 59mod future {
60 #[lang = "future_trait"]
59 trait Future { 61 trait Future {
60 type Output; 62 type Output;
61 } 63 }
@@ -198,6 +200,7 @@ fn test() {
198 200
199#[prelude_import] use ops::*; 201#[prelude_import] use ops::*;
200mod ops { 202mod ops {
203 #[lang = "neg"]
201 pub trait Neg { 204 pub trait Neg {
202 type Output; 205 type Output;
203 } 206 }
@@ -230,6 +233,7 @@ fn test() {
230 233
231#[prelude_import] use ops::*; 234#[prelude_import] use ops::*;
232mod ops { 235mod ops {
236 #[lang = "not"]
233 pub trait Not { 237 pub trait Not {
234 type Output; 238 type Output;
235 } 239 }
@@ -506,6 +510,7 @@ fn test() {
506 510
507#[prelude_import] use ops::*; 511#[prelude_import] use ops::*;
508mod ops { 512mod ops {
513 #[lang = "index"]
509 pub trait Index<Idx> { 514 pub trait Index<Idx> {
510 type Output; 515 type Output;
511 } 516 }
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 294964887..210a685e4 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::Type; 3use hir::{HasVisibility, Type};
4 4
5use crate::completion::completion_item::CompletionKind; 5use crate::completion::completion_item::CompletionKind;
6use crate::{ 6use crate::{
@@ -38,9 +38,15 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
38fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 38fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
39 for receiver in receiver.autoderef(ctx.db) { 39 for receiver in receiver.autoderef(ctx.db) {
40 for (field, ty) in receiver.fields(ctx.db) { 40 for (field, ty) in receiver.fields(ctx.db) {
41 if ctx.module.map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
42 // Skip private field. FIXME: If the definition location of the
43 // field is editable, we should show the completion
44 continue;
45 }
41 acc.add_field(ctx, field, &ty); 46 acc.add_field(ctx, field, &ty);
42 } 47 }
43 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { 48 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
49 // FIXME: Handle visibility
44 acc.add_tuple_field(ctx, i, &ty); 50 acc.add_tuple_field(ctx, i, &ty);
45 } 51 }
46 } 52 }
@@ -187,6 +193,55 @@ mod tests {
187 } 193 }
188 194
189 #[test] 195 #[test]
196 fn test_struct_field_visibility_private() {
197 assert_debug_snapshot!(
198 do_ref_completion(
199 r"
200 mod inner {
201 struct A {
202 private_field: u32,
203 pub pub_field: u32,
204 pub(crate) crate_field: u32,
205 pub(super) super_field: u32,
206 }
207 }
208 fn foo(a: inner::A) {
209 a.<|>
210 }
211 ",
212 ),
213 @r###"
214 [
215 CompletionItem {
216 label: "crate_field",
217 source_range: [313; 313),
218 delete: [313; 313),
219 insert: "crate_field",
220 kind: Field,
221 detail: "u32",
222 },
223 CompletionItem {
224 label: "pub_field",
225 source_range: [313; 313),
226 delete: [313; 313),
227 insert: "pub_field",
228 kind: Field,
229 detail: "u32",
230 },
231 CompletionItem {
232 label: "super_field",
233 source_range: [313; 313),
234 delete: [313; 313),
235 insert: "super_field",
236 kind: Field,
237 detail: "u32",
238 },
239 ]
240 "###
241 );
242 }
243
244 #[test]
190 fn test_method_completion() { 245 fn test_method_completion() {
191 assert_debug_snapshot!( 246 assert_debug_snapshot!(
192 do_ref_completion( 247 do_ref_completion(
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index a097cf8e8..1d130544f 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -38,12 +38,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
38<span class="keyword">fn</span> <span class="function">main</span>() { 38<span class="keyword">fn</span> <span class="function">main</span>() {
39 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal.numeric">92</span>); 39 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal.numeric">92</span>);
40 40
41 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">vec</span> = <span class="text">Vec</span>::<span class="text">new</span>(); 41 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">vec</span> = Vec::new();
42 <span class="keyword.control">if</span> <span class="keyword">true</span> { 42 <span class="keyword.control">if</span> <span class="keyword">true</span> {
43 <span class="keyword">let</span> <span class="variable">x</span> = <span class="literal.numeric">92</span>; 43 <span class="keyword">let</span> <span class="variable">x</span> = <span class="literal.numeric">92</span>;
44 <span class="variable.mut">vec</span>.<span class="text">push</span>(<span class="type">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="literal.numeric">1</span> }); 44 <span class="variable.mut">vec</span>.push(<span class="type">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="literal.numeric">1</span> });
45 } 45 }
46 <span class="keyword.unsafe">unsafe</span> { <span class="variable.mut">vec</span>.<span class="text">set_len</span>(<span class="literal.numeric">0</span>); } 46 <span class="keyword.unsafe">unsafe</span> { <span class="variable.mut">vec</span>.set_len(<span class="literal.numeric">0</span>); }
47 47
48 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">x</span> = <span class="literal.numeric">42</span>; 48 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable.mut">x</span> = <span class="literal.numeric">42</span>;
49 <span class="keyword">let</span> <span class="variable.mut">y</span> = &<span class="keyword">mut</span> <span class="variable.mut">x</span>; 49 <span class="keyword">let</span> <span class="variable.mut">y</span> = &<span class="keyword">mut</span> <span class="variable.mut">x</span>;
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 110556c09..d90ee8540 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -25,11 +25,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
25</style> 25</style>
26<pre><code><span class="keyword">fn</span> <span class="function">main</span>() { 26<pre><code><span class="keyword">fn</span> <span class="function">main</span>() {
27 <span class="keyword">let</span> <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span> = <span class="string">"hello"</span>; 27 <span class="keyword">let</span> <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span> = <span class="string">"hello"</span>;
28 <span class="keyword">let</span> <span class="variable" data-binding-hash="14702933417323009544" style="color: hsl(108,90%,49%);">x</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.<span class="text">to_string</span>(); 28 <span class="keyword">let</span> <span class="variable" data-binding-hash="14702933417323009544" style="color: hsl(108,90%,49%);">x</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.to_string();
29 <span class="keyword">let</span> <span class="variable" data-binding-hash="5443150872754369068" style="color: hsl(215,43%,43%);">y</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.<span class="text">to_string</span>(); 29 <span class="keyword">let</span> <span class="variable" data-binding-hash="5443150872754369068" style="color: hsl(215,43%,43%);">y</span> = <span class="variable" data-binding-hash="8723171760279909834" style="color: hsl(307,91%,75%);">hello</span>.to_string();
30 30
31 <span class="keyword">let</span> <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span> = <span class="string">"other color please!"</span>; 31 <span class="keyword">let</span> <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span> = <span class="string">"other color please!"</span>;
32 <span class="keyword">let</span> <span class="variable" data-binding-hash="2073121142529774969" style="color: hsl(320,43%,74%);">y</span> = <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span>.<span class="text">to_string</span>(); 32 <span class="keyword">let</span> <span class="variable" data-binding-hash="2073121142529774969" style="color: hsl(320,43%,74%);">y</span> = <span class="variable" data-binding-hash="17358108296605513516" style="color: hsl(331,46%,60%);">x</span>.to_string();
33} 33}
34 34
35<span class="keyword">fn</span> <span class="function">bar</span>() { 35<span class="keyword">fn</span> <span class="function">bar</span>() {
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 0228ee7e9..56a36f587 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -20,13 +20,13 @@ pub mod tags {
20 pub(crate) const FIELD: &str = "field"; 20 pub(crate) const FIELD: &str = "field";
21 pub(crate) const FUNCTION: &str = "function"; 21 pub(crate) const FUNCTION: &str = "function";
22 pub(crate) const MODULE: &str = "module"; 22 pub(crate) const MODULE: &str = "module";
23 pub(crate) const TYPE: &str = "type";
24 pub(crate) const CONSTANT: &str = "constant"; 23 pub(crate) const CONSTANT: &str = "constant";
25 pub(crate) const MACRO: &str = "macro"; 24 pub(crate) const MACRO: &str = "macro";
25
26 pub(crate) const VARIABLE: &str = "variable"; 26 pub(crate) const VARIABLE: &str = "variable";
27 pub(crate) const VARIABLE_MUT: &str = "variable.mut"; 27 pub(crate) const VARIABLE_MUT: &str = "variable.mut";
28 pub(crate) const TEXT: &str = "text";
29 28
29 pub(crate) const TYPE: &str = "type";
30 pub(crate) const TYPE_BUILTIN: &str = "type.builtin"; 30 pub(crate) const TYPE_BUILTIN: &str = "type.builtin";
31 pub(crate) const TYPE_SELF: &str = "type.self"; 31 pub(crate) const TYPE_SELF: &str = "type.self";
32 pub(crate) const TYPE_PARAM: &str = "type.param"; 32 pub(crate) const TYPE_PARAM: &str = "type.param";
@@ -35,13 +35,14 @@ pub mod tags {
35 pub(crate) const LITERAL_BYTE: &str = "literal.byte"; 35 pub(crate) const LITERAL_BYTE: &str = "literal.byte";
36 pub(crate) const LITERAL_NUMERIC: &str = "literal.numeric"; 36 pub(crate) const LITERAL_NUMERIC: &str = "literal.numeric";
37 pub(crate) const LITERAL_CHAR: &str = "literal.char"; 37 pub(crate) const LITERAL_CHAR: &str = "literal.char";
38
38 pub(crate) const LITERAL_COMMENT: &str = "comment"; 39 pub(crate) const LITERAL_COMMENT: &str = "comment";
39 pub(crate) const LITERAL_STRING: &str = "string"; 40 pub(crate) const LITERAL_STRING: &str = "string";
40 pub(crate) const LITERAL_ATTRIBUTE: &str = "attribute"; 41 pub(crate) const LITERAL_ATTRIBUTE: &str = "attribute";
41 42
43 pub(crate) const KEYWORD: &str = "keyword";
42 pub(crate) const KEYWORD_UNSAFE: &str = "keyword.unsafe"; 44 pub(crate) const KEYWORD_UNSAFE: &str = "keyword.unsafe";
43 pub(crate) const KEYWORD_CONTROL: &str = "keyword.control"; 45 pub(crate) const KEYWORD_CONTROL: &str = "keyword.control";
44 pub(crate) const KEYWORD: &str = "keyword";
45} 46}
46 47
47#[derive(Debug)] 48#[derive(Debug)]
@@ -109,15 +110,21 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
109 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); 110 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
110 let name_kind = 111 let name_kind =
111 classify_name_ref(db, InFile::new(file_id.into(), &name_ref)).map(|d| d.kind); 112 classify_name_ref(db, InFile::new(file_id.into(), &name_ref)).map(|d| d.kind);
113 match name_kind {
114 Some(name_kind) => {
115 if let Local(local) = &name_kind {
116 if let Some(name) = local.name(db) {
117 let shadow_count =
118 bindings_shadow_count.entry(name.clone()).or_default();
119 binding_hash =
120 Some(calc_binding_hash(file_id, &name, *shadow_count))
121 }
122 };
112 123
113 if let Some(Local(local)) = &name_kind { 124 highlight_name(db, name_kind)
114 if let Some(name) = local.name(db) {
115 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
116 binding_hash = Some(calc_binding_hash(file_id, &name, *shadow_count))
117 } 125 }
118 }; 126 _ => continue,
119 127 }
120 name_kind.map_or(tags::TEXT, |it| highlight_name(db, it))
121 } 128 }
122 NAME => { 129 NAME => {
123 let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap(); 130 let name = node.as_node().cloned().and_then(ast::Name::cast).unwrap();
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index 030e9033c..9b7dcb6e9 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -27,6 +27,7 @@ ra_project_model = { path = "../ra_project_model" }
27ra_prof = { path = "../ra_prof" } 27ra_prof = { path = "../ra_prof" }
28ra_vfs_glob = { path = "../ra_vfs_glob" } 28ra_vfs_glob = { path = "../ra_vfs_glob" }
29env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } 29env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] }
30ra_cargo_watch = { path = "../ra_cargo_watch" }
30 31
31[dev-dependencies] 32[dev-dependencies]
32tempfile = "3" 33tempfile = "3"
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs
index ceb4c4259..0dee1f6fe 100644
--- a/crates/ra_lsp_server/src/caps.rs
+++ b/crates/ra_lsp_server/src/caps.rs
@@ -3,7 +3,7 @@
3use lsp_types::{ 3use lsp_types::{
4 CodeActionProviderCapability, CodeLensOptions, CompletionOptions, 4 CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
5 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, 5 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
6 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, 6 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
7 SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions, 7 SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions,
8 TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, 8 TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
9 TypeDefinitionProviderCapability, WorkDoneProgressOptions, 9 TypeDefinitionProviderCapability, WorkDoneProgressOptions,
@@ -16,7 +16,7 @@ pub fn server_capabilities() -> ServerCapabilities {
16 change: Some(TextDocumentSyncKind::Full), 16 change: Some(TextDocumentSyncKind::Full),
17 will_save: None, 17 will_save: None,
18 will_save_wait_until: None, 18 will_save_wait_until: None,
19 save: None, 19 save: Some(SaveOptions::default()),
20 })), 20 })),
21 hover_provider: Some(true), 21 hover_provider: Some(true),
22 completion_provider: Some(CompletionOptions { 22 completion_provider: Some(CompletionOptions {
diff --git a/crates/ra_lsp_server/src/config.rs b/crates/ra_lsp_server/src/config.rs
index 67942aa41..2d7948d74 100644
--- a/crates/ra_lsp_server/src/config.rs
+++ b/crates/ra_lsp_server/src/config.rs
@@ -32,6 +32,11 @@ pub struct ServerConfig {
32 32
33 pub max_inlay_hint_length: Option<usize>, 33 pub max_inlay_hint_length: Option<usize>,
34 34
35 pub cargo_watch_enable: bool,
36 pub cargo_watch_args: Vec<String>,
37 pub cargo_watch_command: String,
38 pub cargo_watch_all_targets: bool,
39
35 /// For internal usage to make integrated tests faster. 40 /// For internal usage to make integrated tests faster.
36 #[serde(deserialize_with = "nullable_bool_true")] 41 #[serde(deserialize_with = "nullable_bool_true")]
37 pub with_sysroot: bool, 42 pub with_sysroot: bool,
@@ -51,6 +56,10 @@ impl Default for ServerConfig {
51 use_client_watching: false, 56 use_client_watching: false,
52 lru_capacity: None, 57 lru_capacity: None,
53 max_inlay_hint_length: None, 58 max_inlay_hint_length: None,
59 cargo_watch_enable: true,
60 cargo_watch_args: Vec::new(),
61 cargo_watch_command: "check".to_string(),
62 cargo_watch_all_targets: true,
54 with_sysroot: true, 63 with_sysroot: true,
55 feature_flags: FxHashMap::default(), 64 feature_flags: FxHashMap::default(),
56 cargo_features: Default::default(), 65 cargo_features: Default::default(),
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index dda318e43..4336583fe 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -10,6 +10,7 @@ use std::{error::Error, fmt, panic, path::PathBuf, sync::Arc, time::Instant};
10use crossbeam_channel::{select, unbounded, RecvError, Sender}; 10use crossbeam_channel::{select, unbounded, RecvError, Sender};
11use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 11use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
12use lsp_types::{ClientCapabilities, NumberOrString}; 12use lsp_types::{ClientCapabilities, NumberOrString};
13use ra_cargo_watch::{CheckOptions, CheckTask};
13use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; 14use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId};
14use ra_prof::profile; 15use ra_prof::profile;
15use ra_vfs::{VfsTask, Watch}; 16use ra_vfs::{VfsTask, Watch};
@@ -126,6 +127,12 @@ pub fn main_loop(
126 .and_then(|it| it.line_folding_only) 127 .and_then(|it| it.line_folding_only)
127 .unwrap_or(false), 128 .unwrap_or(false),
128 max_inlay_hint_length: config.max_inlay_hint_length, 129 max_inlay_hint_length: config.max_inlay_hint_length,
130 cargo_watch: CheckOptions {
131 enable: config.cargo_watch_enable,
132 args: config.cargo_watch_args,
133 command: config.cargo_watch_command,
134 all_targets: config.cargo_watch_all_targets,
135 },
129 } 136 }
130 }; 137 };
131 138
@@ -176,7 +183,11 @@ pub fn main_loop(
176 Ok(task) => Event::Vfs(task), 183 Ok(task) => Event::Vfs(task),
177 Err(RecvError) => Err("vfs died")?, 184 Err(RecvError) => Err("vfs died")?,
178 }, 185 },
179 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()) 186 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()),
187 recv(world_state.check_watcher.task_recv) -> task => match task {
188 Ok(task) => Event::CheckWatcher(task),
189 Err(RecvError) => Err("check watcher died")?,
190 }
180 }; 191 };
181 if let Event::Msg(Message::Request(req)) = &event { 192 if let Event::Msg(Message::Request(req)) = &event {
182 if connection.handle_shutdown(&req)? { 193 if connection.handle_shutdown(&req)? {
@@ -222,6 +233,7 @@ enum Event {
222 Task(Task), 233 Task(Task),
223 Vfs(VfsTask), 234 Vfs(VfsTask),
224 Lib(LibraryData), 235 Lib(LibraryData),
236 CheckWatcher(CheckTask),
225} 237}
226 238
227impl fmt::Debug for Event { 239impl fmt::Debug for Event {
@@ -259,6 +271,7 @@ impl fmt::Debug for Event {
259 Event::Task(it) => fmt::Debug::fmt(it, f), 271 Event::Task(it) => fmt::Debug::fmt(it, f),
260 Event::Vfs(it) => fmt::Debug::fmt(it, f), 272 Event::Vfs(it) => fmt::Debug::fmt(it, f),
261 Event::Lib(it) => fmt::Debug::fmt(it, f), 273 Event::Lib(it) => fmt::Debug::fmt(it, f),
274 Event::CheckWatcher(it) => fmt::Debug::fmt(it, f),
262 } 275 }
263 } 276 }
264} 277}
@@ -318,6 +331,28 @@ fn loop_turn(
318 world_state.maybe_collect_garbage(); 331 world_state.maybe_collect_garbage();
319 loop_state.in_flight_libraries -= 1; 332 loop_state.in_flight_libraries -= 1;
320 } 333 }
334 Event::CheckWatcher(task) => match task {
335 CheckTask::Update(uri) => {
336 // We manually send a diagnostic update when the watcher asks
337 // us to, to avoid the issue of having to change the file to
338 // receive updated diagnostics.
339 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
340 if let Some(file_id) = world_state.vfs.read().path2file(&path) {
341 let params =
342 handlers::publish_diagnostics(&world_state.snapshot(), FileId(file_id.0))?;
343 let not = notification_new::<req::PublishDiagnostics>(params);
344 task_sender.send(Task::Notify(not)).unwrap();
345 }
346 }
347 CheckTask::Status(progress) => {
348 let params = req::ProgressParams {
349 token: req::ProgressToken::String("rustAnalyzer/cargoWatcher".to_string()),
350 value: req::ProgressParamsValue::WorkDone(progress),
351 };
352 let not = notification_new::<req::Progress>(params);
353 task_sender.send(Task::Notify(not)).unwrap();
354 }
355 },
321 Event::Msg(msg) => match msg { 356 Event::Msg(msg) => match msg {
322 Message::Request(req) => on_request( 357 Message::Request(req) => on_request(
323 world_state, 358 world_state,
@@ -517,6 +552,13 @@ fn on_notification(
517 } 552 }
518 Err(not) => not, 553 Err(not) => not,
519 }; 554 };
555 let not = match notification_cast::<req::DidSaveTextDocument>(not) {
556 Ok(_params) => {
557 state.check_watcher.update();
558 return Ok(());
559 }
560 Err(not) => not,
561 };
520 let not = match notification_cast::<req::DidCloseTextDocument>(not) { 562 let not = match notification_cast::<req::DidCloseTextDocument>(not) {
521 Ok(params) => { 563 Ok(params) => {
522 let uri = params.text_document.uri; 564 let uri = params.text_document.uri;
@@ -667,16 +709,11 @@ where
667 Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message), 709 Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message),
668 Err(e) => { 710 Err(e) => {
669 if is_canceled(&e) { 711 if is_canceled(&e) {
670 // FIXME: When https://github.com/Microsoft/vscode-languageserver-node/issues/457 712 Response::new_err(
671 // gets fixed, we can return the proper response. 713 id,
672 // This works around the issue where "content modified" error would continuously 714 ErrorCode::ContentModified as i32,
673 // show an message pop-up in VsCode 715 "content modified".to_string(),
674 // Response::err( 716 )
675 // id,
676 // ErrorCode::ContentModified as i32,
677 // "content modified".to_string(),
678 // )
679 Response::new_ok(id, ())
680 } else { 717 } else {
681 Response::new_err(id, ErrorCode::InternalError as i32, e.to_string()) 718 Response::new_err(id, ErrorCode::InternalError as i32, e.to_string())
682 } 719 }
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 39eb3df3e..331beab13 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -654,6 +654,29 @@ pub fn handle_code_action(
654 res.push(action.into()); 654 res.push(action.into());
655 } 655 }
656 656
657 for fix in world.check_watcher.read().fixes_for(&params.text_document.uri).into_iter().flatten()
658 {
659 let fix_range = fix.location.range.conv_with(&line_index);
660 if fix_range.intersection(&range).is_none() {
661 continue;
662 }
663
664 let edits = vec![TextEdit::new(fix.location.range, fix.replacement.clone())];
665 let mut edit_map = std::collections::HashMap::new();
666 edit_map.insert(fix.location.uri.clone(), edits);
667 let edit = WorkspaceEdit::new(edit_map);
668
669 let action = CodeAction {
670 title: fix.title.clone(),
671 kind: Some("quickfix".to_string()),
672 diagnostics: Some(fix.diagnostics.clone()),
673 edit: Some(edit),
674 command: None,
675 is_preferred: None,
676 };
677 res.push(action.into());
678 }
679
657 for assist in assists { 680 for assist in assists {
658 let title = assist.change.label.clone(); 681 let title = assist.change.label.clone();
659 let edit = assist.change.try_conv_with(&world)?; 682 let edit = assist.change.try_conv_with(&world)?;
@@ -820,7 +843,7 @@ pub fn publish_diagnostics(
820 let _p = profile("publish_diagnostics"); 843 let _p = profile("publish_diagnostics");
821 let uri = world.file_id_to_uri(file_id)?; 844 let uri = world.file_id_to_uri(file_id)?;
822 let line_index = world.analysis().file_line_index(file_id)?; 845 let line_index = world.analysis().file_line_index(file_id)?;
823 let diagnostics = world 846 let mut diagnostics: Vec<Diagnostic> = world
824 .analysis() 847 .analysis()
825 .diagnostics(file_id)? 848 .diagnostics(file_id)?
826 .into_iter() 849 .into_iter()
@@ -834,6 +857,9 @@ pub fn publish_diagnostics(
834 tags: None, 857 tags: None,
835 }) 858 })
836 .collect(); 859 .collect();
860 if let Some(check_diags) = world.check_watcher.read().diagnostics_for(&uri) {
861 diagnostics.extend(check_diags.iter().cloned());
862 }
837 Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None }) 863 Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None })
838} 864}
839 865
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs
index b34e6f9b8..40edaf677 100644
--- a/crates/ra_lsp_server/src/req.rs
+++ b/crates/ra_lsp_server/src/req.rs
@@ -9,10 +9,10 @@ pub use lsp_types::{
9 CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams, 9 CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams,
10 DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions, 10 DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions,
11 DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, 11 DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse,
12 FileSystemWatcher, Hover, InitializeResult, MessageType, PublishDiagnosticsParams, 12 FileSystemWatcher, Hover, InitializeResult, MessageType, ProgressParams, ProgressParamsValue,
13 ReferenceParams, Registration, RegistrationParams, SelectionRange, SelectionRangeParams, 13 ProgressToken, PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams,
14 ShowMessageParams, SignatureHelp, TextDocumentEdit, TextDocumentPositionParams, TextEdit, 14 SelectionRange, SelectionRangeParams, ShowMessageParams, SignatureHelp, TextDocumentEdit,
15 WorkspaceEdit, WorkspaceSymbolParams, 15 TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams,
16}; 16};
17 17
18pub enum AnalyzerStatus {} 18pub enum AnalyzerStatus {}
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs
index 79431e7e6..121ddfd1f 100644
--- a/crates/ra_lsp_server/src/world.rs
+++ b/crates/ra_lsp_server/src/world.rs
@@ -12,6 +12,9 @@ use crossbeam_channel::{unbounded, Receiver};
12use lsp_server::ErrorCode; 12use lsp_server::ErrorCode;
13use lsp_types::Url; 13use lsp_types::Url;
14use parking_lot::RwLock; 14use parking_lot::RwLock;
15use ra_cargo_watch::{
16 url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher, CheckWatcherSharedState,
17};
15use ra_ide::{ 18use ra_ide::{
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, 19 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData,
17 SourceRootId, 20 SourceRootId,
@@ -20,13 +23,11 @@ use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace};
20use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; 23use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
21use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; 24use ra_vfs_glob::{Glob, RustPackageFilterBuilder};
22use relative_path::RelativePathBuf; 25use relative_path::RelativePathBuf;
23use std::path::{Component, Prefix};
24 26
25use crate::{ 27use crate::{
26 main_loop::pending_requests::{CompletedRequest, LatestRequests}, 28 main_loop::pending_requests::{CompletedRequest, LatestRequests},
27 LspError, Result, 29 LspError, Result,
28}; 30};
29use std::str::FromStr;
30 31
31#[derive(Debug, Clone)] 32#[derive(Debug, Clone)]
32pub struct Options { 33pub struct Options {
@@ -34,6 +35,7 @@ pub struct Options {
34 pub supports_location_link: bool, 35 pub supports_location_link: bool,
35 pub line_folding_only: bool, 36 pub line_folding_only: bool,
36 pub max_inlay_hint_length: Option<usize>, 37 pub max_inlay_hint_length: Option<usize>,
38 pub cargo_watch: CheckOptions,
37} 39}
38 40
39/// `WorldState` is the primary mutable state of the language server 41/// `WorldState` is the primary mutable state of the language server
@@ -52,6 +54,7 @@ pub struct WorldState {
52 pub vfs: Arc<RwLock<Vfs>>, 54 pub vfs: Arc<RwLock<Vfs>>,
53 pub task_receiver: Receiver<VfsTask>, 55 pub task_receiver: Receiver<VfsTask>,
54 pub latest_requests: Arc<RwLock<LatestRequests>>, 56 pub latest_requests: Arc<RwLock<LatestRequests>>,
57 pub check_watcher: CheckWatcher,
55} 58}
56 59
57/// An immutable snapshot of the world's state at a point in time. 60/// An immutable snapshot of the world's state at a point in time.
@@ -61,6 +64,7 @@ pub struct WorldSnapshot {
61 pub analysis: Analysis, 64 pub analysis: Analysis,
62 pub vfs: Arc<RwLock<Vfs>>, 65 pub vfs: Arc<RwLock<Vfs>>,
63 pub latest_requests: Arc<RwLock<LatestRequests>>, 66 pub latest_requests: Arc<RwLock<LatestRequests>>,
67 pub check_watcher: Arc<RwLock<CheckWatcherSharedState>>,
64} 68}
65 69
66impl WorldState { 70impl WorldState {
@@ -127,6 +131,10 @@ impl WorldState {
127 } 131 }
128 change.set_crate_graph(crate_graph); 132 change.set_crate_graph(crate_graph);
129 133
134 // FIXME: Figure out the multi-workspace situation
135 let check_watcher =
136 CheckWatcher::new(&options.cargo_watch, folder_roots.first().cloned().unwrap());
137
130 let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags); 138 let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags);
131 analysis_host.apply_change(change); 139 analysis_host.apply_change(change);
132 WorldState { 140 WorldState {
@@ -138,6 +146,7 @@ impl WorldState {
138 vfs: Arc::new(RwLock::new(vfs)), 146 vfs: Arc::new(RwLock::new(vfs)),
139 task_receiver, 147 task_receiver,
140 latest_requests: Default::default(), 148 latest_requests: Default::default(),
149 check_watcher,
141 } 150 }
142 } 151 }
143 152
@@ -199,6 +208,7 @@ impl WorldState {
199 analysis: self.analysis_host.analysis(), 208 analysis: self.analysis_host.analysis(),
200 vfs: Arc::clone(&self.vfs), 209 vfs: Arc::clone(&self.vfs),
201 latest_requests: Arc::clone(&self.latest_requests), 210 latest_requests: Arc::clone(&self.latest_requests),
211 check_watcher: self.check_watcher.shared.clone(),
202 } 212 }
203 } 213 }
204 214
@@ -284,61 +294,3 @@ impl WorldSnapshot {
284 self.analysis.feature_flags() 294 self.analysis.feature_flags()
285 } 295 }
286} 296}
287
288/// Returns a `Url` object from a given path, will lowercase drive letters if present.
289/// This will only happen when processing windows paths.
290///
291/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
292fn url_from_path_with_drive_lowercasing(path: impl AsRef<Path>) -> Result<Url> {
293 let component_has_windows_drive = path.as_ref().components().any(|comp| {
294 if let Component::Prefix(c) = comp {
295 match c.kind() {
296 Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true,
297 _ => return false,
298 }
299 }
300 false
301 });
302
303 // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
304 if component_has_windows_drive {
305 let url_original = Url::from_file_path(&path)
306 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;
307
308 let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect();
309
310 // There is a drive partition, but we never found a colon.
311 // This should not happen, but in this case we just pass it through.
312 if drive_partition.len() == 1 {
313 return Ok(url_original);
314 }
315
316 let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
317 let url = Url::from_str(&joined).expect("This came from a valid `Url`");
318
319 Ok(url)
320 } else {
321 Ok(Url::from_file_path(&path)
322 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?)
323 }
324}
325
326// `Url` is not able to parse windows paths on unix machines.
327#[cfg(target_os = "windows")]
328#[cfg(test)]
329mod path_conversion_windows_tests {
330 use super::url_from_path_with_drive_lowercasing;
331 #[test]
332 fn test_lowercase_drive_letter_with_drive() {
333 let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap();
334
335 assert_eq!(url.to_string(), "file:///c:/Test");
336 }
337
338 #[test]
339 fn test_drive_without_colon_passthrough() {
340 let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap();
341
342 assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
343 }
344}
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 277532a8c..89cb9a9f3 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -17,7 +17,9 @@ use crate::{
17 17
18pub use self::{ 18pub use self::{
19 expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, 19 expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp},
20 extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind}, 20 extensions::{
21 FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind, VisibilityKind,
22 },
21 generated::*, 23 generated::*,
22 tokens::*, 24 tokens::*,
23 traits::*, 25 traits::*,
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs
index baaef3023..d9666cdca 100644
--- a/crates/ra_syntax/src/ast/extensions.rs
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -413,3 +413,32 @@ impl ast::TraitDef {
413 self.syntax().children_with_tokens().any(|t| t.kind() == T![auto]) 413 self.syntax().children_with_tokens().any(|t| t.kind() == T![auto])
414 } 414 }
415} 415}
416
417pub enum VisibilityKind {
418 In(ast::Path),
419 PubCrate,
420 PubSuper,
421 Pub,
422}
423
424impl ast::Visibility {
425 pub fn kind(&self) -> VisibilityKind {
426 if let Some(path) = children(self).next() {
427 VisibilityKind::In(path)
428 } else if self.is_pub_crate() {
429 VisibilityKind::PubCrate
430 } else if self.is_pub_super() {
431 VisibilityKind::PubSuper
432 } else {
433 VisibilityKind::Pub
434 }
435 }
436
437 fn is_pub_crate(&self) -> bool {
438 self.syntax().children_with_tokens().any(|it| it.kind() == T![crate])
439 }
440
441 fn is_pub_super(&self) -> bool {
442 self.syntax().children_with_tokens().any(|it| it.kind() == T![super])
443 }
444}
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 9f9d6e63c..e64c83d33 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -1064,6 +1064,7 @@ impl AstNode for ExternCrateItem {
1064 } 1064 }
1065} 1065}
1066impl ast::AttrsOwner for ExternCrateItem {} 1066impl ast::AttrsOwner for ExternCrateItem {}
1067impl ast::VisibilityOwner for ExternCrateItem {}
1067impl ExternCrateItem { 1068impl ExternCrateItem {
1068 pub fn name_ref(&self) -> Option<NameRef> { 1069 pub fn name_ref(&self) -> Option<NameRef> {
1069 AstChildren::new(&self.syntax).next() 1070 AstChildren::new(&self.syntax).next()
@@ -2006,6 +2007,7 @@ impl AstNode for ModuleItem {
2006 } 2007 }
2007} 2008}
2008impl ast::AttrsOwner for ModuleItem {} 2009impl ast::AttrsOwner for ModuleItem {}
2010impl ast::VisibilityOwner for ModuleItem {}
2009impl ModuleItem {} 2011impl ModuleItem {}
2010#[derive(Debug, Clone, PartialEq, Eq, Hash)] 2012#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2011pub struct Name { 2013pub struct Name {
@@ -3893,6 +3895,7 @@ impl AstNode for UseItem {
3893 } 3895 }
3894} 3896}
3895impl ast::AttrsOwner for UseItem {} 3897impl ast::AttrsOwner for UseItem {}
3898impl ast::VisibilityOwner for UseItem {}
3896impl UseItem { 3899impl UseItem {
3897 pub fn use_tree(&self) -> Option<UseTree> { 3900 pub fn use_tree(&self) -> Option<UseTree> {
3898 AstChildren::new(&self.syntax).next() 3901 AstChildren::new(&self.syntax).next()
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index 08aafb610..e43a724f0 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -412,7 +412,7 @@ Grammar(
412 "ModuleItem": ( 412 "ModuleItem": (
413 enum: ["StructDef", "UnionDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock", 413 enum: ["StructDef", "UnionDef", "EnumDef", "FnDef", "TraitDef", "TypeAliasDef", "ImplBlock",
414 "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ], 414 "UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ],
415 traits: ["AttrsOwner"], 415 traits: ["AttrsOwner", "VisibilityOwner"],
416 ), 416 ),
417 "ImplItem": ( 417 "ImplItem": (
418 enum: ["FnDef", "TypeAliasDef", "ConstDef"], 418 enum: ["FnDef", "TypeAliasDef", "ConstDef"],
@@ -683,7 +683,7 @@ Grammar(
683 ] 683 ]
684 ), 684 ),
685 "UseItem": ( 685 "UseItem": (
686 traits: ["AttrsOwner"], 686 traits: ["AttrsOwner", "VisibilityOwner"],
687 options: [ "UseTree" ], 687 options: [ "UseTree" ],
688 ), 688 ),
689 "UseTree": ( 689 "UseTree": (
@@ -696,7 +696,7 @@ Grammar(
696 collections: [("use_trees", "UseTree")] 696 collections: [("use_trees", "UseTree")]
697 ), 697 ),
698 "ExternCrateItem": ( 698 "ExternCrateItem": (
699 traits: ["AttrsOwner"], 699 traits: ["AttrsOwner", "VisibilityOwner"],
700 options: ["NameRef", "Alias"], 700 options: ["NameRef", "Alias"],
701 ), 701 ),
702 "ArgList": ( 702 "ArgList": (