diff options
Diffstat (limited to 'crates')
29 files changed, 914 insertions, 223 deletions
diff --git a/crates/assists/src/handlers/merge_imports.rs b/crates/assists/src/handlers/merge_imports.rs index 0bd679260..fe33cee53 100644 --- a/crates/assists/src/handlers/merge_imports.rs +++ b/crates/assists/src/handlers/merge_imports.rs | |||
@@ -95,7 +95,7 @@ use std::fmt::Debug; | |||
95 | use std::fmt<|>::Display; | 95 | use std::fmt<|>::Display; |
96 | ", | 96 | ", |
97 | r" | 97 | r" |
98 | use std::fmt::{Display, Debug}; | 98 | use std::fmt::{Debug, Display}; |
99 | ", | 99 | ", |
100 | ); | 100 | ); |
101 | } | 101 | } |
@@ -122,7 +122,7 @@ use std::fmt::{self, Display}; | |||
122 | use std::{fmt, <|>fmt::Display}; | 122 | use std::{fmt, <|>fmt::Display}; |
123 | ", | 123 | ", |
124 | r" | 124 | r" |
125 | use std::{fmt::{Display, self}}; | 125 | use std::{fmt::{self, Display}}; |
126 | ", | 126 | ", |
127 | ); | 127 | ); |
128 | } | 128 | } |
@@ -210,13 +210,17 @@ use std::{fmt<|>::Debug, fmt::Display}; | |||
210 | use std::{fmt::{Debug, Display}}; | 210 | use std::{fmt::{Debug, Display}}; |
211 | ", | 211 | ", |
212 | ); | 212 | ); |
213 | } | ||
214 | |||
215 | #[test] | ||
216 | fn test_merge_nested2() { | ||
213 | check_assist( | 217 | check_assist( |
214 | merge_imports, | 218 | merge_imports, |
215 | r" | 219 | r" |
216 | use std::{fmt::Debug, fmt<|>::Display}; | 220 | use std::{fmt::Debug, fmt<|>::Display}; |
217 | ", | 221 | ", |
218 | r" | 222 | r" |
219 | use std::{fmt::{Display, Debug}}; | 223 | use std::{fmt::{Debug, Display}}; |
220 | ", | 224 | ", |
221 | ); | 225 | ); |
222 | } | 226 | } |
@@ -310,9 +314,7 @@ use foo::<|>{ | |||
310 | }; | 314 | }; |
311 | ", | 315 | ", |
312 | r" | 316 | r" |
313 | use foo::{ | 317 | use foo::{FooBar, bar::baz}; |
314 | FooBar, | ||
315 | bar::baz}; | ||
316 | ", | 318 | ", |
317 | ) | 319 | ) |
318 | } | 320 | } |
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs index e48407fcc..8ac907707 100644 --- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -312,7 +312,7 @@ impl std::fmt<|> for Foo { | |||
312 | } | 312 | } |
313 | ", | 313 | ", |
314 | r" | 314 | r" |
315 | use std::fmt::{Debug, self}; | 315 | use std::fmt::{self, Debug}; |
316 | 316 | ||
317 | impl fmt for Foo { | 317 | impl fmt for Foo { |
318 | } | 318 | } |
@@ -330,9 +330,8 @@ use std::fmt::{Debug, nested::{Display}}; | |||
330 | impl std::fmt::nested<|> for Foo { | 330 | impl std::fmt::nested<|> for Foo { |
331 | } | 331 | } |
332 | ", | 332 | ", |
333 | // FIXME(veykril): should be nested::{self, Display} here | ||
334 | r" | 333 | r" |
335 | use std::fmt::{Debug, nested::{Display}, nested}; | 334 | use std::fmt::{Debug, nested::{self, Display}}; |
336 | 335 | ||
337 | impl nested for Foo { | 336 | impl nested for Foo { |
338 | } | 337 | } |
@@ -350,9 +349,8 @@ use std::fmt::{Debug, nested::{self, Display}}; | |||
350 | impl std::fmt::nested<|> for Foo { | 349 | impl std::fmt::nested<|> for Foo { |
351 | } | 350 | } |
352 | ", | 351 | ", |
353 | // FIXME(veykril): nested is duplicated now | ||
354 | r" | 352 | r" |
355 | use std::fmt::{Debug, nested::{self, Display}, nested}; | 353 | use std::fmt::{Debug, nested::{self, Display}}; |
356 | 354 | ||
357 | impl nested for Foo { | 355 | impl nested for Foo { |
358 | } | 356 | } |
@@ -371,7 +369,7 @@ impl std::fmt::nested::Debug<|> for Foo { | |||
371 | } | 369 | } |
372 | ", | 370 | ", |
373 | r" | 371 | r" |
374 | use std::fmt::{Debug, nested::{Display}, nested::Debug}; | 372 | use std::fmt::{Debug, nested::{Debug, Display}}; |
375 | 373 | ||
376 | impl Debug for Foo { | 374 | impl Debug for Foo { |
377 | } | 375 | } |
@@ -409,7 +407,7 @@ impl std::fmt::Display<|> for Foo { | |||
409 | } | 407 | } |
410 | ", | 408 | ", |
411 | r" | 409 | r" |
412 | use std::fmt::{nested::Debug, Display}; | 410 | use std::fmt::{Display, nested::Debug}; |
413 | 411 | ||
414 | impl Display for Foo { | 412 | impl Display for Foo { |
415 | } | 413 | } |
@@ -429,12 +427,8 @@ use crate::{ | |||
429 | 427 | ||
430 | fn foo() { crate::ty::lower<|>::trait_env() } | 428 | fn foo() { crate::ty::lower<|>::trait_env() } |
431 | ", | 429 | ", |
432 | // FIXME(veykril): formatting broke here | ||
433 | r" | 430 | r" |
434 | use crate::{ | 431 | use crate::{AssocItem, ty::{Substs, Ty, lower}}; |
435 | ty::{Substs, Ty}, | ||
436 | AssocItem, | ||
437 | ty::lower}; | ||
438 | 432 | ||
439 | fn foo() { lower::trait_env() } | 433 | fn foo() { lower::trait_env() } |
440 | ", | 434 | ", |
@@ -633,7 +627,7 @@ fn main() { | |||
633 | } | 627 | } |
634 | ", | 628 | ", |
635 | r" | 629 | r" |
636 | use std::fmt::{Display, self}; | 630 | use std::fmt::{self, Display}; |
637 | 631 | ||
638 | fn main() { | 632 | fn main() { |
639 | fmt; | 633 | fmt; |
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs index 6d110aaaf..09f4a2224 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/assists/src/utils/insert_use.rs | |||
@@ -1,7 +1,9 @@ | |||
1 | //! Handle syntactic aspects of inserting a new `use`. | 1 | //! Handle syntactic aspects of inserting a new `use`. |
2 | use std::iter::{self, successors}; | 2 | use std::{ |
3 | cmp::Ordering, | ||
4 | iter::{self, successors}, | ||
5 | }; | ||
3 | 6 | ||
4 | use algo::skip_trivia_token; | ||
5 | use ast::{ | 7 | use ast::{ |
6 | edit::{AstNodeEdit, IndentLevel}, | 8 | edit::{AstNodeEdit, IndentLevel}, |
7 | PathSegmentKind, VisibilityOwner, | 9 | PathSegmentKind, VisibilityOwner, |
@@ -9,9 +11,8 @@ use ast::{ | |||
9 | use syntax::{ | 11 | use syntax::{ |
10 | algo, | 12 | algo, |
11 | ast::{self, make, AstNode}, | 13 | ast::{self, make, AstNode}, |
12 | Direction, InsertPosition, SyntaxElement, SyntaxNode, T, | 14 | InsertPosition, SyntaxElement, SyntaxNode, |
13 | }; | 15 | }; |
14 | use test_utils::mark; | ||
15 | 16 | ||
16 | #[derive(Debug)] | 17 | #[derive(Debug)] |
17 | pub enum ImportScope { | 18 | pub enum ImportScope { |
@@ -119,7 +120,6 @@ pub(crate) fn insert_use( | |||
119 | } | 120 | } |
120 | 121 | ||
121 | if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { | 122 | if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { |
122 | // FIXME: this alone doesnt properly re-align all cases | ||
123 | buf.push(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()); | 123 | buf.push(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()); |
124 | } | 124 | } |
125 | buf.push(use_item.syntax().clone().into()); | 125 | buf.push(use_item.syntax().clone().into()); |
@@ -149,66 +149,123 @@ fn eq_visibility(vis0: Option<ast::Visibility>, vis1: Option<ast::Visibility>) - | |||
149 | } | 149 | } |
150 | 150 | ||
151 | pub(crate) fn try_merge_imports( | 151 | pub(crate) fn try_merge_imports( |
152 | old: &ast::Use, | 152 | lhs: &ast::Use, |
153 | new: &ast::Use, | 153 | rhs: &ast::Use, |
154 | merge_behaviour: MergeBehaviour, | 154 | merge_behaviour: MergeBehaviour, |
155 | ) -> Option<ast::Use> { | 155 | ) -> Option<ast::Use> { |
156 | // don't merge imports with different visibilities | 156 | // don't merge imports with different visibilities |
157 | if !eq_visibility(old.visibility(), new.visibility()) { | 157 | if !eq_visibility(lhs.visibility(), rhs.visibility()) { |
158 | return None; | 158 | return None; |
159 | } | 159 | } |
160 | let old_tree = old.use_tree()?; | 160 | let lhs_tree = lhs.use_tree()?; |
161 | let new_tree = new.use_tree()?; | 161 | let rhs_tree = rhs.use_tree()?; |
162 | let merged = try_merge_trees(&old_tree, &new_tree, merge_behaviour)?; | 162 | let merged = try_merge_trees(&lhs_tree, &rhs_tree, merge_behaviour)?; |
163 | Some(old.with_use_tree(merged)) | 163 | Some(lhs.with_use_tree(merged)) |
164 | } | ||
165 | |||
166 | /// Simple function that checks if a UseTreeList is deeper than one level | ||
167 | fn use_tree_list_is_nested(tl: &ast::UseTreeList) -> bool { | ||
168 | tl.use_trees().any(|use_tree| { | ||
169 | use_tree.use_tree_list().is_some() || use_tree.path().and_then(|p| p.qualifier()).is_some() | ||
170 | }) | ||
171 | } | 164 | } |
172 | 165 | ||
173 | // FIXME: currently this merely prepends the new tree into old, ideally it would insert the items in a sorted fashion | ||
174 | pub(crate) fn try_merge_trees( | 166 | pub(crate) fn try_merge_trees( |
175 | old: &ast::UseTree, | 167 | lhs: &ast::UseTree, |
176 | new: &ast::UseTree, | 168 | rhs: &ast::UseTree, |
177 | merge_behaviour: MergeBehaviour, | 169 | merge: MergeBehaviour, |
178 | ) -> Option<ast::UseTree> { | 170 | ) -> Option<ast::UseTree> { |
179 | let lhs_path = old.path()?; | 171 | let lhs_path = lhs.path()?; |
180 | let rhs_path = new.path()?; | 172 | let rhs_path = rhs.path()?; |
181 | 173 | ||
182 | let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; | 174 | let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; |
183 | let lhs = old.split_prefix(&lhs_prefix); | 175 | let lhs = lhs.split_prefix(&lhs_prefix); |
184 | let rhs = new.split_prefix(&rhs_prefix); | 176 | let rhs = rhs.split_prefix(&rhs_prefix); |
185 | let lhs_tl = lhs.use_tree_list()?; | 177 | recursive_merge(&lhs, &rhs, merge).map(|(merged, _)| merged) |
186 | let rhs_tl = rhs.use_tree_list()?; | 178 | } |
187 | |||
188 | // if we are only allowed to merge the last level check if the split off paths are only one level deep | ||
189 | if merge_behaviour == MergeBehaviour::Last | ||
190 | && (use_tree_list_is_nested(&lhs_tl) || use_tree_list_is_nested(&rhs_tl)) | ||
191 | { | ||
192 | mark::hit!(test_last_merge_too_long); | ||
193 | return None; | ||
194 | } | ||
195 | 179 | ||
196 | let should_insert_comma = lhs_tl | 180 | /// Recursively "zips" together lhs and rhs. |
197 | .r_curly_token() | 181 | fn recursive_merge( |
198 | .and_then(|it| skip_trivia_token(it.prev_token()?, Direction::Prev)) | 182 | lhs: &ast::UseTree, |
199 | .map(|it| it.kind()) | 183 | rhs: &ast::UseTree, |
200 | != Some(T![,]); | 184 | merge: MergeBehaviour, |
201 | let mut to_insert: Vec<SyntaxElement> = Vec::new(); | 185 | ) -> Option<(ast::UseTree, bool)> { |
202 | if should_insert_comma { | 186 | let mut use_trees = lhs |
203 | to_insert.push(make::token(T![,]).into()); | 187 | .use_tree_list() |
204 | to_insert.push(make::tokens::single_space().into()); | 188 | .into_iter() |
205 | } | 189 | .flat_map(|list| list.use_trees()) |
206 | to_insert.extend( | 190 | // check if any of the use trees are nested, if they are and the behaviour is `last` we are not allowed to merge this |
207 | rhs_tl.syntax().children_with_tokens().filter(|it| !matches!(it.kind(), T!['{'] | T!['}'])), | 191 | // so early exit the iterator by using Option's Intoiterator impl |
208 | ); | 192 | .map(|tree| match merge == MergeBehaviour::Last && tree.use_tree_list().is_some() { |
209 | let pos = InsertPosition::Before(lhs_tl.r_curly_token()?.into()); | 193 | true => None, |
210 | let use_tree_list = lhs_tl.insert_children(pos, to_insert); | 194 | false => Some(tree), |
211 | Some(lhs.with_use_tree_list(use_tree_list)) | 195 | }) |
196 | .collect::<Option<Vec<_>>>()?; | ||
197 | use_trees.sort_unstable_by(|a, b| path_cmp_opt(a.path(), b.path())); | ||
198 | for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) { | ||
199 | let rhs_path = rhs_t.path(); | ||
200 | match use_trees.binary_search_by(|p| path_cmp_opt(p.path(), rhs_path.clone())) { | ||
201 | Ok(idx) => { | ||
202 | let lhs_t = &mut use_trees[idx]; | ||
203 | let lhs_path = lhs_t.path()?; | ||
204 | let rhs_path = rhs_path?; | ||
205 | let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; | ||
206 | if lhs_prefix == lhs_path && rhs_prefix == rhs_path { | ||
207 | let tree_is_self = |tree: ast::UseTree| { | ||
208 | tree.path().as_ref().map(path_is_self).unwrap_or(false) | ||
209 | }; | ||
210 | // check if only one of the two trees has a tree list, and whether that then contains `self` or not. | ||
211 | // If this is the case we can skip this iteration since the path without the list is already included in the other one via `self` | ||
212 | let tree_contains_self = |tree: &ast::UseTree| { | ||
213 | tree.use_tree_list() | ||
214 | .map(|tree_list| tree_list.use_trees().any(tree_is_self)) | ||
215 | .unwrap_or(false) | ||
216 | }; | ||
217 | match (tree_contains_self(&lhs_t), tree_contains_self(&rhs_t)) { | ||
218 | (true, false) => continue, | ||
219 | (false, true) => { | ||
220 | *lhs_t = rhs_t; | ||
221 | continue; | ||
222 | } | ||
223 | _ => (), | ||
224 | } | ||
225 | |||
226 | // glob imports arent part of the use-tree lists so we need to special handle them here as well | ||
227 | // this special handling is only required for when we merge a module import into a glob import of said module | ||
228 | // see the `merge_self_glob` or `merge_mod_into_glob` tests | ||
229 | if lhs_t.star_token().is_some() || rhs_t.star_token().is_some() { | ||
230 | *lhs_t = make::use_tree( | ||
231 | make::path_unqualified(make::path_segment_self()), | ||
232 | None, | ||
233 | None, | ||
234 | false, | ||
235 | ); | ||
236 | use_trees.insert(idx, make::glob_use_tree()); | ||
237 | continue; | ||
238 | } | ||
239 | } | ||
240 | let lhs = lhs_t.split_prefix(&lhs_prefix); | ||
241 | let rhs = rhs_t.split_prefix(&rhs_prefix); | ||
242 | let this_has_children = use_trees.len() > 0; | ||
243 | match recursive_merge(&lhs, &rhs, merge) { | ||
244 | Some((_, has_multiple_children)) | ||
245 | if merge == MergeBehaviour::Last | ||
246 | && this_has_children | ||
247 | && has_multiple_children => | ||
248 | { | ||
249 | return None | ||
250 | } | ||
251 | Some((use_tree, _)) => use_trees[idx] = use_tree, | ||
252 | None => use_trees.insert(idx, rhs_t), | ||
253 | } | ||
254 | } | ||
255 | Err(_) | ||
256 | if merge == MergeBehaviour::Last | ||
257 | && use_trees.len() > 0 | ||
258 | && rhs_t.use_tree_list().is_some() => | ||
259 | { | ||
260 | return None | ||
261 | } | ||
262 | Err(idx) => { | ||
263 | use_trees.insert(idx, rhs_t); | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | let has_multiple_children = use_trees.len() > 1; | ||
268 | Some((lhs.with_use_tree_list(make::use_tree_list(use_trees)), has_multiple_children)) | ||
212 | } | 269 | } |
213 | 270 | ||
214 | /// Traverses both paths until they differ, returning the common prefix of both. | 271 | /// Traverses both paths until they differ, returning the common prefix of both. |
@@ -219,7 +276,7 @@ fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Pa | |||
219 | loop { | 276 | loop { |
220 | match (lhs_curr.segment(), rhs_curr.segment()) { | 277 | match (lhs_curr.segment(), rhs_curr.segment()) { |
221 | (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (), | 278 | (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (), |
222 | _ => break, | 279 | _ => break res, |
223 | } | 280 | } |
224 | res = Some((lhs_curr.clone(), rhs_curr.clone())); | 281 | res = Some((lhs_curr.clone(), rhs_curr.clone())); |
225 | 282 | ||
@@ -228,11 +285,62 @@ fn common_prefix(lhs: &ast::Path, rhs: &ast::Path) -> Option<(ast::Path, ast::Pa | |||
228 | lhs_curr = lhs; | 285 | lhs_curr = lhs; |
229 | rhs_curr = rhs; | 286 | rhs_curr = rhs; |
230 | } | 287 | } |
231 | _ => break, | 288 | _ => break res, |
232 | } | 289 | } |
233 | } | 290 | } |
291 | } | ||
234 | 292 | ||
235 | res | 293 | fn path_is_self(path: &ast::Path) -> bool { |
294 | path.segment().and_then(|seg| seg.self_token()).is_some() && path.qualifier().is_none() | ||
295 | } | ||
296 | |||
297 | #[inline] | ||
298 | fn first_segment(path: &ast::Path) -> Option<ast::PathSegment> { | ||
299 | first_path(path).segment() | ||
300 | } | ||
301 | |||
302 | fn first_path(path: &ast::Path) -> ast::Path { | ||
303 | successors(Some(path.clone()), ast::Path::qualifier).last().unwrap() | ||
304 | } | ||
305 | |||
306 | fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Clone { | ||
307 | // cant make use of SyntaxNode::siblings, because the returned Iterator is not clone | ||
308 | successors(first_segment(path), |p| p.parent_path().parent_path().and_then(|p| p.segment())) | ||
309 | } | ||
310 | |||
311 | /// Orders paths in the following way: | ||
312 | /// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers | ||
313 | // FIXME: rustfmt sort lowercase idents before uppercase, in general we want to have the same ordering rustfmt has | ||
314 | // which is `self` and `super` first, then identifier imports with lowercase ones first, then glob imports and at last list imports. | ||
315 | // Example foo::{self, foo, baz, Baz, Qux, *, {Bar}} | ||
316 | fn path_cmp(a: &ast::Path, b: &ast::Path) -> Ordering { | ||
317 | match (path_is_self(a), path_is_self(b)) { | ||
318 | (true, true) => Ordering::Equal, | ||
319 | (true, false) => Ordering::Less, | ||
320 | (false, true) => Ordering::Greater, | ||
321 | (false, false) => { | ||
322 | let a = segment_iter(a); | ||
323 | let b = segment_iter(b); | ||
324 | // cmp_by would be useful for us here but that is currently unstable | ||
325 | // cmp doesnt work due the lifetimes on text's return type | ||
326 | a.zip(b) | ||
327 | .flat_map(|(seg, seg2)| seg.name_ref().zip(seg2.name_ref())) | ||
328 | .find_map(|(a, b)| match a.text().cmp(b.text()) { | ||
329 | ord @ Ordering::Greater | ord @ Ordering::Less => Some(ord), | ||
330 | Ordering::Equal => None, | ||
331 | }) | ||
332 | .unwrap_or(Ordering::Equal) | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | |||
337 | fn path_cmp_opt(a: Option<ast::Path>, b: Option<ast::Path>) -> Ordering { | ||
338 | match (a, b) { | ||
339 | (None, None) => Ordering::Equal, | ||
340 | (None, Some(_)) => Ordering::Less, | ||
341 | (Some(_), None) => Ordering::Greater, | ||
342 | (Some(a), Some(b)) => path_cmp(&a, &b), | ||
343 | } | ||
236 | } | 344 | } |
237 | 345 | ||
238 | /// What type of merges are allowed. | 346 | /// What type of merges are allowed. |
@@ -279,19 +387,6 @@ impl ImportGroup { | |||
279 | } | 387 | } |
280 | } | 388 | } |
281 | 389 | ||
282 | fn first_segment(path: &ast::Path) -> Option<ast::PathSegment> { | ||
283 | first_path(path).segment() | ||
284 | } | ||
285 | |||
286 | fn first_path(path: &ast::Path) -> ast::Path { | ||
287 | successors(Some(path.clone()), ast::Path::qualifier).last().unwrap() | ||
288 | } | ||
289 | |||
290 | fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Clone { | ||
291 | // cant make use of SyntaxNode::siblings, because the returned Iterator is not clone | ||
292 | successors(first_segment(path), |p| p.parent_path().parent_path().and_then(|p| p.segment())) | ||
293 | } | ||
294 | |||
295 | #[derive(PartialEq, Eq)] | 390 | #[derive(PartialEq, Eq)] |
296 | enum AddBlankLine { | 391 | enum AddBlankLine { |
297 | Before, | 392 | Before, |
@@ -594,7 +689,7 @@ use std::io;", | |||
594 | check_full( | 689 | check_full( |
595 | "std::foo::bar::Baz", | 690 | "std::foo::bar::Baz", |
596 | r"use std::foo::bar::Qux;", | 691 | r"use std::foo::bar::Qux;", |
597 | r"use std::foo::bar::{Qux, Baz};", | 692 | r"use std::foo::bar::{Baz, Qux};", |
598 | ) | 693 | ) |
599 | } | 694 | } |
600 | 695 | ||
@@ -603,7 +698,7 @@ use std::io;", | |||
603 | check_last( | 698 | check_last( |
604 | "std::foo::bar::Baz", | 699 | "std::foo::bar::Baz", |
605 | r"use std::foo::bar::Qux;", | 700 | r"use std::foo::bar::Qux;", |
606 | r"use std::foo::bar::{Qux, Baz};", | 701 | r"use std::foo::bar::{Baz, Qux};", |
607 | ) | 702 | ) |
608 | } | 703 | } |
609 | 704 | ||
@@ -612,7 +707,7 @@ use std::io;", | |||
612 | check_full( | 707 | check_full( |
613 | "std::foo::bar::Baz", | 708 | "std::foo::bar::Baz", |
614 | r"use std::foo::bar::{Qux, Quux};", | 709 | r"use std::foo::bar::{Qux, Quux};", |
615 | r"use std::foo::bar::{Qux, Quux, Baz};", | 710 | r"use std::foo::bar::{Baz, Quux, Qux};", |
616 | ) | 711 | ) |
617 | } | 712 | } |
618 | 713 | ||
@@ -621,7 +716,7 @@ use std::io;", | |||
621 | check_last( | 716 | check_last( |
622 | "std::foo::bar::Baz", | 717 | "std::foo::bar::Baz", |
623 | r"use std::foo::bar::{Qux, Quux};", | 718 | r"use std::foo::bar::{Qux, Quux};", |
624 | r"use std::foo::bar::{Qux, Quux, Baz};", | 719 | r"use std::foo::bar::{Baz, Quux, Qux};", |
625 | ) | 720 | ) |
626 | } | 721 | } |
627 | 722 | ||
@@ -630,7 +725,7 @@ use std::io;", | |||
630 | check_full( | 725 | check_full( |
631 | "std::foo::bar::Baz", | 726 | "std::foo::bar::Baz", |
632 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", | 727 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", |
633 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}, Baz};", | 728 | r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};", |
634 | ) | 729 | ) |
635 | } | 730 | } |
636 | 731 | ||
@@ -645,6 +740,15 @@ use std::foo::bar::{Qux, quux::{Fez, Fizz}};", | |||
645 | } | 740 | } |
646 | 741 | ||
647 | #[test] | 742 | #[test] |
743 | fn merge_groups_full_nested_deep() { | ||
744 | check_full( | ||
745 | "std::foo::bar::quux::Baz", | ||
746 | r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", | ||
747 | r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};", | ||
748 | ) | ||
749 | } | ||
750 | |||
751 | #[test] | ||
648 | fn merge_groups_skip_pub() { | 752 | fn merge_groups_skip_pub() { |
649 | check_full( | 753 | check_full( |
650 | "std::io", | 754 | "std::io", |
@@ -670,34 +774,63 @@ use std::io;", | |||
670 | check_last( | 774 | check_last( |
671 | "std::fmt::Result", | 775 | "std::fmt::Result", |
672 | r"use std::{fmt, io};", | 776 | r"use std::{fmt, io};", |
673 | r"use std::{self, fmt::Result}; | 777 | r"use std::fmt::{self, Result}; |
674 | use std::io;", | 778 | use std::io;", |
675 | ) | 779 | ) |
676 | } | 780 | } |
677 | 781 | ||
678 | #[test] | 782 | #[test] |
783 | fn merge_into_module_import() { | ||
784 | check_full( | ||
785 | "std::fmt::Result", | ||
786 | r"use std::{fmt, io};", | ||
787 | r"use std::{fmt::{self, Result}, io};", | ||
788 | ) | ||
789 | } | ||
790 | |||
791 | #[test] | ||
679 | fn merge_groups_self() { | 792 | fn merge_groups_self() { |
680 | check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};") | 793 | check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};") |
681 | } | 794 | } |
682 | 795 | ||
683 | #[test] | 796 | #[test] |
684 | fn merge_self_glob() { | 797 | fn merge_mod_into_glob() { |
685 | check_full( | 798 | check_full( |
686 | "token::TokenKind", | 799 | "token::TokenKind", |
687 | r"use token::TokenKind::*;", | 800 | r"use token::TokenKind::*;", |
688 | r"use token::TokenKind::{self::*, self};", | 801 | r"use token::TokenKind::{*, self};", |
802 | ) | ||
803 | // FIXME: have it emit `use token::TokenKind::{self, *}`? | ||
804 | } | ||
805 | |||
806 | #[test] | ||
807 | fn merge_self_glob() { | ||
808 | check_full("self", r"use self::*;", r"use self::{*, self};") | ||
809 | // FIXME: have it emit `use {self, *}`? | ||
810 | } | ||
811 | |||
812 | #[test] | ||
813 | #[ignore] // FIXME: Support this | ||
814 | fn merge_partial_path() { | ||
815 | check_full( | ||
816 | "ast::Foo", | ||
817 | r"use syntax::{ast, algo};", | ||
818 | r"use syntax::{ast::{self, Foo}, algo};", | ||
819 | ) | ||
820 | } | ||
821 | |||
822 | #[test] | ||
823 | fn merge_glob_nested() { | ||
824 | check_full( | ||
825 | "foo::bar::quux::Fez", | ||
826 | r"use foo::bar::{Baz, quux::*};", | ||
827 | r"use foo::bar::{Baz, quux::{self::*, Fez}};", | ||
689 | ) | 828 | ) |
690 | } | 829 | } |
691 | 830 | ||
692 | #[test] | 831 | #[test] |
693 | fn merge_last_too_long() { | 832 | fn merge_last_too_long() { |
694 | mark::check!(test_last_merge_too_long); | 833 | check_last("foo::bar", r"use foo::bar::baz::Qux;", r"use foo::bar::{self, baz::Qux};"); |
695 | check_last( | ||
696 | "foo::bar", | ||
697 | r"use foo::bar::baz::Qux;", | ||
698 | r"use foo::bar; | ||
699 | use foo::bar::baz::Qux;", | ||
700 | ); | ||
701 | } | 834 | } |
702 | 835 | ||
703 | #[test] | 836 | #[test] |
@@ -710,6 +843,42 @@ use foo::bar::baz::Qux;", | |||
710 | ); | 843 | ); |
711 | } | 844 | } |
712 | 845 | ||
846 | #[test] | ||
847 | fn merge_last_fail() { | ||
848 | check_merge_only_fail( | ||
849 | r"use foo::bar::{baz::{Qux, Fez}};", | ||
850 | r"use foo::bar::{baaz::{Quux, Feez}};", | ||
851 | MergeBehaviour::Last, | ||
852 | ); | ||
853 | } | ||
854 | |||
855 | #[test] | ||
856 | fn merge_last_fail1() { | ||
857 | check_merge_only_fail( | ||
858 | r"use foo::bar::{baz::{Qux, Fez}};", | ||
859 | r"use foo::bar::baaz::{Quux, Feez};", | ||
860 | MergeBehaviour::Last, | ||
861 | ); | ||
862 | } | ||
863 | |||
864 | #[test] | ||
865 | fn merge_last_fail2() { | ||
866 | check_merge_only_fail( | ||
867 | r"use foo::bar::baz::{Qux, Fez};", | ||
868 | r"use foo::bar::{baaz::{Quux, Feez}};", | ||
869 | MergeBehaviour::Last, | ||
870 | ); | ||
871 | } | ||
872 | |||
873 | #[test] | ||
874 | fn merge_last_fail3() { | ||
875 | check_merge_only_fail( | ||
876 | r"use foo::bar::baz::{Qux, Fez};", | ||
877 | r"use foo::bar::baaz::{Quux, Feez};", | ||
878 | MergeBehaviour::Last, | ||
879 | ); | ||
880 | } | ||
881 | |||
713 | fn check( | 882 | fn check( |
714 | path: &str, | 883 | path: &str, |
715 | ra_fixture_before: &str, | 884 | ra_fixture_before: &str, |
@@ -742,4 +911,23 @@ use foo::bar::baz::Qux;", | |||
742 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 911 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
743 | check(path, ra_fixture_before, ra_fixture_after, None) | 912 | check(path, ra_fixture_before, ra_fixture_after, None) |
744 | } | 913 | } |
914 | |||
915 | fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehaviour) { | ||
916 | let use0 = ast::SourceFile::parse(ra_fixture0) | ||
917 | .tree() | ||
918 | .syntax() | ||
919 | .descendants() | ||
920 | .find_map(ast::Use::cast) | ||
921 | .unwrap(); | ||
922 | |||
923 | let use1 = ast::SourceFile::parse(ra_fixture1) | ||
924 | .tree() | ||
925 | .syntax() | ||
926 | .descendants() | ||
927 | .find_map(ast::Use::cast) | ||
928 | .unwrap(); | ||
929 | |||
930 | let result = try_merge_imports(&use0, &use1, mb); | ||
931 | assert_eq!(result.map(|u| u.to_string()), None); | ||
932 | } | ||
745 | } | 933 | } |
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index 9a8eb4ede..6190906da 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs | |||
@@ -54,6 +54,7 @@ pub struct TypeAliasData { | |||
54 | pub name: Name, | 54 | pub name: Name, |
55 | pub type_ref: Option<TypeRef>, | 55 | pub type_ref: Option<TypeRef>, |
56 | pub visibility: RawVisibility, | 56 | pub visibility: RawVisibility, |
57 | pub is_extern: bool, | ||
57 | /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl). | 58 | /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl). |
58 | pub bounds: Vec<TypeBound>, | 59 | pub bounds: Vec<TypeBound>, |
59 | } | 60 | } |
@@ -71,6 +72,7 @@ impl TypeAliasData { | |||
71 | name: typ.name.clone(), | 72 | name: typ.name.clone(), |
72 | type_ref: typ.type_ref.clone(), | 73 | type_ref: typ.type_ref.clone(), |
73 | visibility: item_tree[typ.visibility].clone(), | 74 | visibility: item_tree[typ.visibility].clone(), |
75 | is_extern: typ.is_extern, | ||
74 | bounds: typ.bounds.to_vec(), | 76 | bounds: typ.bounds.to_vec(), |
75 | }) | 77 | }) |
76 | } | 78 | } |
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs index 3e19d9117..2ec0fd3fb 100644 --- a/crates/hir_def/src/diagnostics.rs +++ b/crates/hir_def/src/diagnostics.rs | |||
@@ -28,3 +28,45 @@ impl Diagnostic for UnresolvedModule { | |||
28 | self | 28 | self |
29 | } | 29 | } |
30 | } | 30 | } |
31 | |||
32 | #[derive(Debug)] | ||
33 | pub struct UnresolvedExternCrate { | ||
34 | pub file: HirFileId, | ||
35 | pub item: AstPtr<ast::ExternCrate>, | ||
36 | } | ||
37 | |||
38 | impl Diagnostic for UnresolvedExternCrate { | ||
39 | fn code(&self) -> DiagnosticCode { | ||
40 | DiagnosticCode("unresolved-extern-crate") | ||
41 | } | ||
42 | fn message(&self) -> String { | ||
43 | "unresolved extern crate".to_string() | ||
44 | } | ||
45 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
46 | InFile::new(self.file, self.item.clone().into()) | ||
47 | } | ||
48 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
49 | self | ||
50 | } | ||
51 | } | ||
52 | |||
53 | #[derive(Debug)] | ||
54 | pub struct UnresolvedImport { | ||
55 | pub file: HirFileId, | ||
56 | pub node: AstPtr<ast::UseTree>, | ||
57 | } | ||
58 | |||
59 | impl Diagnostic for UnresolvedImport { | ||
60 | fn code(&self) -> DiagnosticCode { | ||
61 | DiagnosticCode("unresolved-import") | ||
62 | } | ||
63 | fn message(&self) -> String { | ||
64 | "unresolved import".to_string() | ||
65 | } | ||
66 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
67 | InFile::new(self.file, self.node.clone().into()) | ||
68 | } | ||
69 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
70 | self | ||
71 | } | ||
72 | } | ||
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index e14722cae..db3b4a985 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -291,7 +291,6 @@ pub enum AttrOwner { | |||
291 | 291 | ||
292 | Variant(Idx<Variant>), | 292 | Variant(Idx<Variant>), |
293 | Field(Idx<Field>), | 293 | Field(Idx<Field>), |
294 | // FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`. | ||
295 | } | 294 | } |
296 | 295 | ||
297 | macro_rules! from_attrs { | 296 | macro_rules! from_attrs { |
@@ -483,6 +482,11 @@ pub struct Import { | |||
483 | /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many | 482 | /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many |
484 | /// `Import`s can map to the same `use` item. | 483 | /// `Import`s can map to the same `use` item. |
485 | pub ast_id: FileAstId<ast::Use>, | 484 | pub ast_id: FileAstId<ast::Use>, |
485 | /// Index of this `Import` when the containing `Use` is visited via `ModPath::expand_use_item`. | ||
486 | /// | ||
487 | /// This can be used to get the `UseTree` this `Import` corresponds to and allows emitting | ||
488 | /// precise diagnostics. | ||
489 | pub index: usize, | ||
486 | } | 490 | } |
487 | 491 | ||
488 | #[derive(Debug, Clone, Eq, PartialEq)] | 492 | #[derive(Debug, Clone, Eq, PartialEq)] |
@@ -592,6 +596,7 @@ pub struct TypeAlias { | |||
592 | pub bounds: Box<[TypeBound]>, | 596 | pub bounds: Box<[TypeBound]>, |
593 | pub generic_params: GenericParamsId, | 597 | pub generic_params: GenericParamsId, |
594 | pub type_ref: Option<TypeRef>, | 598 | pub type_ref: Option<TypeRef>, |
599 | pub is_extern: bool, | ||
595 | pub ast_id: FileAstId<ast::TypeAlias>, | 600 | pub ast_id: FileAstId<ast::TypeAlias>, |
596 | } | 601 | } |
597 | 602 | ||
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 6a503d785..da62e1297 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -364,6 +364,7 @@ impl Ctx { | |||
364 | generic_params, | 364 | generic_params, |
365 | type_ref, | 365 | type_ref, |
366 | ast_id, | 366 | ast_id, |
367 | is_extern: false, | ||
367 | }; | 368 | }; |
368 | Some(id(self.data().type_aliases.alloc(res))) | 369 | Some(id(self.data().type_aliases.alloc(res))) |
369 | } | 370 | } |
@@ -482,7 +483,7 @@ impl Ctx { | |||
482 | ModPath::expand_use_item( | 483 | ModPath::expand_use_item( |
483 | InFile::new(self.file, use_item.clone()), | 484 | InFile::new(self.file, use_item.clone()), |
484 | &self.hygiene, | 485 | &self.hygiene, |
485 | |path, _tree, is_glob, alias| { | 486 | |path, _use_tree, is_glob, alias| { |
486 | imports.push(id(tree.imports.alloc(Import { | 487 | imports.push(id(tree.imports.alloc(Import { |
487 | path, | 488 | path, |
488 | alias, | 489 | alias, |
@@ -490,6 +491,7 @@ impl Ctx { | |||
490 | is_glob, | 491 | is_glob, |
491 | is_prelude, | 492 | is_prelude, |
492 | ast_id, | 493 | ast_id, |
494 | index: imports.len(), | ||
493 | }))); | 495 | }))); |
494 | }, | 496 | }, |
495 | ); | 497 | ); |
@@ -558,8 +560,9 @@ impl Ctx { | |||
558 | statik.into() | 560 | statik.into() |
559 | } | 561 | } |
560 | ast::ExternItem::TypeAlias(ty) => { | 562 | ast::ExternItem::TypeAlias(ty) => { |
561 | let id = self.lower_type_alias(&ty)?; | 563 | let foreign_ty = self.lower_type_alias(&ty)?; |
562 | id.into() | 564 | self.data().type_aliases[foreign_ty.index].is_extern = true; |
565 | foreign_ty.into() | ||
563 | } | 566 | } |
564 | ast::ExternItem::MacroCall(_) => return None, | 567 | ast::ExternItem::MacroCall(_) => return None, |
565 | }; | 568 | }; |
diff --git a/crates/hir_def/src/item_tree/tests.rs b/crates/hir_def/src/item_tree/tests.rs index 620e697d4..4d0a3b7e6 100644 --- a/crates/hir_def/src/item_tree/tests.rs +++ b/crates/hir_def/src/item_tree/tests.rs | |||
@@ -228,15 +228,15 @@ fn smoke() { | |||
228 | 228 | ||
229 | top-level items: | 229 | top-level items: |
230 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] | 230 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] |
231 | Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0) } | 231 | Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0), index: 0 } |
232 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] | 232 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }] |
233 | Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0) } | 233 | Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<syntax::ast::generated::nodes::Use>(0), index: 1 } |
234 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }] | 234 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }] |
235 | ExternCrate { path: ModPath { kind: Plain, segments: [Name(Text("krate"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<syntax::ast::generated::nodes::ExternCrate>(1) } | 235 | ExternCrate { path: ModPath { kind: Plain, segments: [Name(Text("krate"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<syntax::ast::generated::nodes::ExternCrate>(1) } |
236 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }] | 236 | #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }] |
237 | Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Trait>(2) } | 237 | Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<syntax::ast::generated::nodes::Trait>(2) } |
238 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }] | 238 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }] |
239 | > TypeAlias { name: Name(Text("AssocTy")), visibility: RawVisibilityId("pub(self)"), bounds: [Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Tr"))] }, generic_args: [Some(GenericArgs { args: [Type(Tuple([]))], has_self_type: false, bindings: [] })] })], generic_params: GenericParamsId(4294967295), type_ref: None, ast_id: FileAstId::<syntax::ast::generated::nodes::TypeAlias>(8) } | 239 | > TypeAlias { name: Name(Text("AssocTy")), visibility: RawVisibilityId("pub(self)"), bounds: [Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Tr"))] }, generic_args: [Some(GenericArgs { args: [Type(Tuple([]))], has_self_type: false, bindings: [] })] })], generic_params: GenericParamsId(4294967295), type_ref: None, is_extern: false, ast_id: FileAstId::<syntax::ast::generated::nodes::TypeAlias>(8) } |
240 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_const"))] }, input: None }]) }] | 240 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_const"))] }, input: None }]) }] |
241 | > Const { name: Some(Name(Text("CONST"))), visibility: RawVisibilityId("pub(self)"), type_ref: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("u8"))] }, generic_args: [None] }), ast_id: FileAstId::<syntax::ast::generated::nodes::Const>(9) } | 241 | > Const { name: Some(Name(Text("CONST"))), visibility: RawVisibilityId("pub(self)"), type_ref: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("u8"))] }, generic_args: [None] }), ast_id: FileAstId::<syntax::ast::generated::nodes::Const>(9) } |
242 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_method"))] }, input: None }]) }] | 242 | > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_method"))] }, input: None }]) }] |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index bf302172d..5e4d73c1f 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -288,31 +288,70 @@ pub enum ModuleSource { | |||
288 | 288 | ||
289 | mod diagnostics { | 289 | mod diagnostics { |
290 | use hir_expand::diagnostics::DiagnosticSink; | 290 | use hir_expand::diagnostics::DiagnosticSink; |
291 | use hir_expand::hygiene::Hygiene; | ||
292 | use hir_expand::InFile; | ||
291 | use syntax::{ast, AstPtr}; | 293 | use syntax::{ast, AstPtr}; |
292 | 294 | ||
293 | use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; | 295 | use crate::path::ModPath; |
296 | use crate::{db::DefDatabase, diagnostics::*, nameres::LocalModuleId, AstId}; | ||
294 | 297 | ||
295 | #[derive(Debug, PartialEq, Eq)] | 298 | #[derive(Debug, PartialEq, Eq)] |
296 | pub(super) enum DefDiagnostic { | 299 | enum DiagnosticKind { |
297 | UnresolvedModule { | 300 | UnresolvedModule { declaration: AstId<ast::Module>, candidate: String }, |
298 | module: LocalModuleId, | 301 | |
299 | declaration: AstId<ast::Module>, | 302 | UnresolvedExternCrate { ast: AstId<ast::ExternCrate> }, |
300 | candidate: String, | 303 | |
301 | }, | 304 | UnresolvedImport { ast: AstId<ast::Use>, index: usize }, |
305 | } | ||
306 | |||
307 | #[derive(Debug, PartialEq, Eq)] | ||
308 | pub(super) struct DefDiagnostic { | ||
309 | in_module: LocalModuleId, | ||
310 | kind: DiagnosticKind, | ||
302 | } | 311 | } |
303 | 312 | ||
304 | impl DefDiagnostic { | 313 | impl DefDiagnostic { |
314 | pub(super) fn unresolved_module( | ||
315 | container: LocalModuleId, | ||
316 | declaration: AstId<ast::Module>, | ||
317 | candidate: String, | ||
318 | ) -> Self { | ||
319 | Self { | ||
320 | in_module: container, | ||
321 | kind: DiagnosticKind::UnresolvedModule { declaration, candidate }, | ||
322 | } | ||
323 | } | ||
324 | |||
325 | pub(super) fn unresolved_extern_crate( | ||
326 | container: LocalModuleId, | ||
327 | declaration: AstId<ast::ExternCrate>, | ||
328 | ) -> Self { | ||
329 | Self { | ||
330 | in_module: container, | ||
331 | kind: DiagnosticKind::UnresolvedExternCrate { ast: declaration }, | ||
332 | } | ||
333 | } | ||
334 | |||
335 | pub(super) fn unresolved_import( | ||
336 | container: LocalModuleId, | ||
337 | ast: AstId<ast::Use>, | ||
338 | index: usize, | ||
339 | ) -> Self { | ||
340 | Self { in_module: container, kind: DiagnosticKind::UnresolvedImport { ast, index } } | ||
341 | } | ||
342 | |||
305 | pub(super) fn add_to( | 343 | pub(super) fn add_to( |
306 | &self, | 344 | &self, |
307 | db: &dyn DefDatabase, | 345 | db: &dyn DefDatabase, |
308 | target_module: LocalModuleId, | 346 | target_module: LocalModuleId, |
309 | sink: &mut DiagnosticSink, | 347 | sink: &mut DiagnosticSink, |
310 | ) { | 348 | ) { |
311 | match self { | 349 | if self.in_module != target_module { |
312 | DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { | 350 | return; |
313 | if *module != target_module { | 351 | } |
314 | return; | 352 | |
315 | } | 353 | match &self.kind { |
354 | DiagnosticKind::UnresolvedModule { declaration, candidate } => { | ||
316 | let decl = declaration.to_node(db.upcast()); | 355 | let decl = declaration.to_node(db.upcast()); |
317 | sink.push(UnresolvedModule { | 356 | sink.push(UnresolvedModule { |
318 | file: declaration.file_id, | 357 | file: declaration.file_id, |
@@ -320,6 +359,36 @@ mod diagnostics { | |||
320 | candidate: candidate.clone(), | 359 | candidate: candidate.clone(), |
321 | }) | 360 | }) |
322 | } | 361 | } |
362 | |||
363 | DiagnosticKind::UnresolvedExternCrate { ast } => { | ||
364 | let item = ast.to_node(db.upcast()); | ||
365 | sink.push(UnresolvedExternCrate { | ||
366 | file: ast.file_id, | ||
367 | item: AstPtr::new(&item), | ||
368 | }); | ||
369 | } | ||
370 | |||
371 | DiagnosticKind::UnresolvedImport { ast, index } => { | ||
372 | let use_item = ast.to_node(db.upcast()); | ||
373 | let hygiene = Hygiene::new(db.upcast(), ast.file_id); | ||
374 | let mut cur = 0; | ||
375 | let mut tree = None; | ||
376 | ModPath::expand_use_item( | ||
377 | InFile::new(ast.file_id, use_item), | ||
378 | &hygiene, | ||
379 | |_mod_path, use_tree, _is_glob, _alias| { | ||
380 | if cur == *index { | ||
381 | tree = Some(use_tree.clone()); | ||
382 | } | ||
383 | |||
384 | cur += 1; | ||
385 | }, | ||
386 | ); | ||
387 | |||
388 | if let Some(tree) = tree { | ||
389 | sink.push(UnresolvedImport { file: ast.file_id, node: AstPtr::new(&tree) }); | ||
390 | } | ||
391 | } | ||
323 | } | 392 | } |
324 | } | 393 | } |
325 | } | 394 | } |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 3e99c8773..818008169 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -5,6 +5,7 @@ | |||
5 | 5 | ||
6 | use base_db::{CrateId, FileId, ProcMacroId}; | 6 | use base_db::{CrateId, FileId, ProcMacroId}; |
7 | use cfg::CfgOptions; | 7 | use cfg::CfgOptions; |
8 | use hir_expand::InFile; | ||
8 | use hir_expand::{ | 9 | use hir_expand::{ |
9 | ast_id_map::FileAstId, | 10 | ast_id_map::FileAstId, |
10 | builtin_derive::find_builtin_derive, | 11 | builtin_derive::find_builtin_derive, |
@@ -14,6 +15,7 @@ use hir_expand::{ | |||
14 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, | 15 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, |
15 | }; | 16 | }; |
16 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::FxHashMap; |
18 | use rustc_hash::FxHashSet; | ||
17 | use syntax::ast; | 19 | use syntax::ast; |
18 | use test_utils::mark; | 20 | use test_utils::mark; |
19 | 21 | ||
@@ -21,9 +23,7 @@ use crate::{ | |||
21 | attr::Attrs, | 23 | attr::Attrs, |
22 | db::DefDatabase, | 24 | db::DefDatabase, |
23 | item_scope::{ImportType, PerNsGlobImports}, | 25 | item_scope::{ImportType, PerNsGlobImports}, |
24 | item_tree::{ | 26 | item_tree::{self, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind}, |
25 | self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind, | ||
26 | }, | ||
27 | nameres::{ | 27 | nameres::{ |
28 | diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, | 28 | diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, |
29 | BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, | 29 | BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, |
@@ -112,6 +112,12 @@ impl PartialResolvedImport { | |||
112 | } | 112 | } |
113 | 113 | ||
114 | #[derive(Clone, Debug, Eq, PartialEq)] | 114 | #[derive(Clone, Debug, Eq, PartialEq)] |
115 | enum ImportSource { | ||
116 | Import(ItemTreeId<item_tree::Import>), | ||
117 | ExternCrate(ItemTreeId<item_tree::ExternCrate>), | ||
118 | } | ||
119 | |||
120 | #[derive(Clone, Debug, Eq, PartialEq)] | ||
115 | struct Import { | 121 | struct Import { |
116 | pub path: ModPath, | 122 | pub path: ModPath, |
117 | pub alias: Option<ImportAlias>, | 123 | pub alias: Option<ImportAlias>, |
@@ -120,11 +126,12 @@ struct Import { | |||
120 | pub is_prelude: bool, | 126 | pub is_prelude: bool, |
121 | pub is_extern_crate: bool, | 127 | pub is_extern_crate: bool, |
122 | pub is_macro_use: bool, | 128 | pub is_macro_use: bool, |
129 | source: ImportSource, | ||
123 | } | 130 | } |
124 | 131 | ||
125 | impl Import { | 132 | impl Import { |
126 | fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self { | 133 | fn from_use(tree: &ItemTree, id: ItemTreeId<item_tree::Import>) -> Self { |
127 | let it = &tree[id]; | 134 | let it = &tree[id.value]; |
128 | let visibility = &tree[it.visibility]; | 135 | let visibility = &tree[it.visibility]; |
129 | Self { | 136 | Self { |
130 | path: it.path.clone(), | 137 | path: it.path.clone(), |
@@ -134,11 +141,12 @@ impl Import { | |||
134 | is_prelude: it.is_prelude, | 141 | is_prelude: it.is_prelude, |
135 | is_extern_crate: false, | 142 | is_extern_crate: false, |
136 | is_macro_use: false, | 143 | is_macro_use: false, |
144 | source: ImportSource::Import(id), | ||
137 | } | 145 | } |
138 | } | 146 | } |
139 | 147 | ||
140 | fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self { | 148 | fn from_extern_crate(tree: &ItemTree, id: ItemTreeId<item_tree::ExternCrate>) -> Self { |
141 | let it = &tree[id]; | 149 | let it = &tree[id.value]; |
142 | let visibility = &tree[it.visibility]; | 150 | let visibility = &tree[it.visibility]; |
143 | Self { | 151 | Self { |
144 | path: it.path.clone(), | 152 | path: it.path.clone(), |
@@ -148,6 +156,7 @@ impl Import { | |||
148 | is_prelude: false, | 156 | is_prelude: false, |
149 | is_extern_crate: true, | 157 | is_extern_crate: true, |
150 | is_macro_use: it.is_macro_use, | 158 | is_macro_use: it.is_macro_use, |
159 | source: ImportSource::ExternCrate(id), | ||
151 | } | 160 | } |
152 | } | 161 | } |
153 | } | 162 | } |
@@ -245,9 +254,10 @@ impl DefCollector<'_> { | |||
245 | 254 | ||
246 | let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | 255 | let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); |
247 | // show unresolved imports in completion, etc | 256 | // show unresolved imports in completion, etc |
248 | for directive in unresolved_imports { | 257 | for directive in &unresolved_imports { |
249 | self.record_resolved_import(&directive) | 258 | self.record_resolved_import(directive) |
250 | } | 259 | } |
260 | self.unresolved_imports = unresolved_imports; | ||
251 | 261 | ||
252 | // Record proc-macros | 262 | // Record proc-macros |
253 | self.collect_proc_macro(); | 263 | self.collect_proc_macro(); |
@@ -420,7 +430,11 @@ impl DefCollector<'_> { | |||
420 | .as_ident() | 430 | .as_ident() |
421 | .expect("extern crate should have been desugared to one-element path"), | 431 | .expect("extern crate should have been desugared to one-element path"), |
422 | ); | 432 | ); |
423 | PartialResolvedImport::Resolved(res) | 433 | if res.is_none() { |
434 | PartialResolvedImport::Unresolved | ||
435 | } else { | ||
436 | PartialResolvedImport::Resolved(res) | ||
437 | } | ||
424 | } else { | 438 | } else { |
425 | let res = self.def_map.resolve_path_fp_with_macro( | 439 | let res = self.def_map.resolve_path_fp_with_macro( |
426 | self.db, | 440 | self.db, |
@@ -774,7 +788,51 @@ impl DefCollector<'_> { | |||
774 | .collect(item_tree.top_level_items()); | 788 | .collect(item_tree.top_level_items()); |
775 | } | 789 | } |
776 | 790 | ||
777 | fn finish(self) -> CrateDefMap { | 791 | fn finish(mut self) -> CrateDefMap { |
792 | // Emit diagnostics for all remaining unresolved imports. | ||
793 | |||
794 | // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't | ||
795 | // resolve. We first emit diagnostics for unresolved extern crates and collect the missing | ||
796 | // crate names. Then we emit diagnostics for unresolved imports, but only if the import | ||
797 | // doesn't start with an unresolved crate's name. Due to renaming and reexports, this is a | ||
798 | // heuristic, but it works in practice. | ||
799 | let mut diagnosed_extern_crates = FxHashSet::default(); | ||
800 | for directive in &self.unresolved_imports { | ||
801 | if let ImportSource::ExternCrate(krate) = directive.import.source { | ||
802 | let item_tree = self.db.item_tree(krate.file_id); | ||
803 | let extern_crate = &item_tree[krate.value]; | ||
804 | |||
805 | diagnosed_extern_crates.insert(extern_crate.path.segments[0].clone()); | ||
806 | |||
807 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( | ||
808 | directive.module_id, | ||
809 | InFile::new(krate.file_id, extern_crate.ast_id), | ||
810 | )); | ||
811 | } | ||
812 | } | ||
813 | |||
814 | for directive in &self.unresolved_imports { | ||
815 | if let ImportSource::Import(import) = &directive.import.source { | ||
816 | let item_tree = self.db.item_tree(import.file_id); | ||
817 | let import_data = &item_tree[import.value]; | ||
818 | |||
819 | match (import_data.path.segments.first(), &import_data.path.kind) { | ||
820 | (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => { | ||
821 | if diagnosed_extern_crates.contains(krate) { | ||
822 | continue; | ||
823 | } | ||
824 | } | ||
825 | _ => {} | ||
826 | } | ||
827 | |||
828 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( | ||
829 | directive.module_id, | ||
830 | InFile::new(import.file_id, import_data.ast_id), | ||
831 | import_data.index, | ||
832 | )); | ||
833 | } | ||
834 | } | ||
835 | |||
778 | self.def_map | 836 | self.def_map |
779 | } | 837 | } |
780 | } | 838 | } |
@@ -830,14 +888,20 @@ impl ModCollector<'_, '_> { | |||
830 | ModItem::Import(import_id) => { | 888 | ModItem::Import(import_id) => { |
831 | self.def_collector.unresolved_imports.push(ImportDirective { | 889 | self.def_collector.unresolved_imports.push(ImportDirective { |
832 | module_id: self.module_id, | 890 | module_id: self.module_id, |
833 | import: Import::from_use(&self.item_tree, import_id), | 891 | import: Import::from_use( |
892 | &self.item_tree, | ||
893 | InFile::new(self.file_id, import_id), | ||
894 | ), | ||
834 | status: PartialResolvedImport::Unresolved, | 895 | status: PartialResolvedImport::Unresolved, |
835 | }) | 896 | }) |
836 | } | 897 | } |
837 | ModItem::ExternCrate(import_id) => { | 898 | ModItem::ExternCrate(import_id) => { |
838 | self.def_collector.unresolved_imports.push(ImportDirective { | 899 | self.def_collector.unresolved_imports.push(ImportDirective { |
839 | module_id: self.module_id, | 900 | module_id: self.module_id, |
840 | import: Import::from_extern_crate(&self.item_tree, import_id), | 901 | import: Import::from_extern_crate( |
902 | &self.item_tree, | ||
903 | InFile::new(self.file_id, import_id), | ||
904 | ), | ||
841 | status: PartialResolvedImport::Unresolved, | 905 | status: PartialResolvedImport::Unresolved, |
842 | }) | 906 | }) |
843 | } | 907 | } |
@@ -1051,13 +1115,11 @@ impl ModCollector<'_, '_> { | |||
1051 | self.import_all_legacy_macros(module_id); | 1115 | self.import_all_legacy_macros(module_id); |
1052 | } | 1116 | } |
1053 | } | 1117 | } |
1054 | Err(candidate) => self.def_collector.def_map.diagnostics.push( | 1118 | Err(candidate) => { |
1055 | DefDiagnostic::UnresolvedModule { | 1119 | self.def_collector.def_map.diagnostics.push( |
1056 | module: self.module_id, | 1120 | DefDiagnostic::unresolved_module(self.module_id, ast_id, candidate), |
1057 | declaration: ast_id, | 1121 | ); |
1058 | candidate, | 1122 | } |
1059 | }, | ||
1060 | ), | ||
1061 | }; | 1123 | }; |
1062 | } | 1124 | } |
1063 | } | 1125 | } |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index 5ca30dac9..11d84f808 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -2,6 +2,7 @@ mod globs; | |||
2 | mod incremental; | 2 | mod incremental; |
3 | mod macros; | 3 | mod macros; |
4 | mod mod_resolution; | 4 | mod mod_resolution; |
5 | mod diagnostics; | ||
5 | mod primitives; | 6 | mod primitives; |
6 | 7 | ||
7 | use std::sync::Arc; | 8 | use std::sync::Arc; |
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs new file mode 100644 index 000000000..576b813d2 --- /dev/null +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -0,0 +1,131 @@ | |||
1 | use base_db::fixture::WithFixture; | ||
2 | use base_db::FileId; | ||
3 | use base_db::SourceDatabaseExt; | ||
4 | use hir_expand::db::AstDatabase; | ||
5 | use rustc_hash::FxHashMap; | ||
6 | use syntax::TextRange; | ||
7 | use syntax::TextSize; | ||
8 | |||
9 | use crate::test_db::TestDB; | ||
10 | |||
11 | fn check_diagnostics(ra_fixture: &str) { | ||
12 | let db: TestDB = TestDB::with_files(ra_fixture); | ||
13 | let annotations = db.extract_annotations(); | ||
14 | assert!(!annotations.is_empty()); | ||
15 | |||
16 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | ||
17 | db.diagnostics(|d| { | ||
18 | let src = d.display_source(); | ||
19 | let root = db.parse_or_expand(src.file_id).unwrap(); | ||
20 | // FIXME: macros... | ||
21 | let file_id = src.file_id.original_file(&db); | ||
22 | let range = src.value.to_node(&root).text_range(); | ||
23 | let message = d.message().to_owned(); | ||
24 | actual.entry(file_id).or_default().push((range, message)); | ||
25 | }); | ||
26 | |||
27 | for (file_id, diags) in actual.iter_mut() { | ||
28 | diags.sort_by_key(|it| it.0.start()); | ||
29 | let text = db.file_text(*file_id); | ||
30 | // For multiline spans, place them on line start | ||
31 | for (range, content) in diags { | ||
32 | if text[*range].contains('\n') { | ||
33 | *range = TextRange::new(range.start(), range.start() + TextSize::from(1)); | ||
34 | *content = format!("... {}", content); | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | |||
39 | assert_eq!(annotations, actual); | ||
40 | } | ||
41 | |||
42 | #[test] | ||
43 | fn unresolved_import() { | ||
44 | check_diagnostics( | ||
45 | r" | ||
46 | use does_exist; | ||
47 | use does_not_exist; | ||
48 | //^^^^^^^^^^^^^^ unresolved import | ||
49 | |||
50 | mod does_exist {} | ||
51 | ", | ||
52 | ); | ||
53 | } | ||
54 | |||
55 | #[test] | ||
56 | fn unresolved_import_in_use_tree() { | ||
57 | // Only the relevant part of a nested `use` item should be highlighted. | ||
58 | check_diagnostics( | ||
59 | r" | ||
60 | use does_exist::{Exists, DoesntExist}; | ||
61 | //^^^^^^^^^^^ unresolved import | ||
62 | |||
63 | use {does_not_exist::*, does_exist}; | ||
64 | //^^^^^^^^^^^^^^^^^ unresolved import | ||
65 | |||
66 | use does_not_exist::{ | ||
67 | a, | ||
68 | //^ unresolved import | ||
69 | b, | ||
70 | //^ unresolved import | ||
71 | c, | ||
72 | //^ unresolved import | ||
73 | }; | ||
74 | |||
75 | mod does_exist { | ||
76 | pub struct Exists; | ||
77 | } | ||
78 | ", | ||
79 | ); | ||
80 | } | ||
81 | |||
82 | #[test] | ||
83 | fn unresolved_extern_crate() { | ||
84 | check_diagnostics( | ||
85 | r" | ||
86 | //- /main.rs crate:main deps:core | ||
87 | extern crate core; | ||
88 | extern crate doesnotexist; | ||
89 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | ||
90 | //- /lib.rs crate:core | ||
91 | ", | ||
92 | ); | ||
93 | } | ||
94 | |||
95 | #[test] | ||
96 | fn dedup_unresolved_import_from_unresolved_crate() { | ||
97 | check_diagnostics( | ||
98 | r" | ||
99 | //- /main.rs crate:main | ||
100 | mod a { | ||
101 | extern crate doesnotexist; | ||
102 | //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate | ||
103 | |||
104 | // Should not error, since we already errored for the missing crate. | ||
105 | use doesnotexist::{self, bla, *}; | ||
106 | |||
107 | use crate::doesnotexist; | ||
108 | //^^^^^^^^^^^^^^^^^^^ unresolved import | ||
109 | } | ||
110 | |||
111 | mod m { | ||
112 | use super::doesnotexist; | ||
113 | //^^^^^^^^^^^^^^^^^^^ unresolved import | ||
114 | } | ||
115 | ", | ||
116 | ); | ||
117 | } | ||
118 | |||
119 | #[test] | ||
120 | fn unresolved_module() { | ||
121 | check_diagnostics( | ||
122 | r" | ||
123 | //- /lib.rs | ||
124 | mod foo; | ||
125 | mod bar; | ||
126 | //^^^^^^^^ unresolved module | ||
127 | mod baz {} | ||
128 | //- /foo.rs | ||
129 | ", | ||
130 | ); | ||
131 | } | ||
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs index 1f619787e..f93337a6e 100644 --- a/crates/hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs | |||
@@ -672,42 +672,6 @@ pub struct Baz; | |||
672 | } | 672 | } |
673 | 673 | ||
674 | #[test] | 674 | #[test] |
675 | fn unresolved_module_diagnostics() { | ||
676 | let db = TestDB::with_files( | ||
677 | r" | ||
678 | //- /lib.rs | ||
679 | mod foo; | ||
680 | mod bar; | ||
681 | mod baz {} | ||
682 | //- /foo.rs | ||
683 | ", | ||
684 | ); | ||
685 | let krate = db.test_crate(); | ||
686 | |||
687 | let crate_def_map = db.crate_def_map(krate); | ||
688 | |||
689 | expect![[r#" | ||
690 | [ | ||
691 | UnresolvedModule { | ||
692 | module: Idx::<ModuleData>(0), | ||
693 | declaration: InFile { | ||
694 | file_id: HirFileId( | ||
695 | FileId( | ||
696 | FileId( | ||
697 | 0, | ||
698 | ), | ||
699 | ), | ||
700 | ), | ||
701 | value: FileAstId::<syntax::ast::generated::nodes::Module>(1), | ||
702 | }, | ||
703 | candidate: "bar.rs", | ||
704 | }, | ||
705 | ] | ||
706 | "#]] | ||
707 | .assert_debug_eq(&crate_def_map.diagnostics); | ||
708 | } | ||
709 | |||
710 | #[test] | ||
711 | fn module_resolution_decl_inside_module_in_non_crate_root_2() { | 675 | fn module_resolution_decl_inside_module_in_non_crate_root_2() { |
712 | check( | 676 | check( |
713 | r#" | 677 | r#" |
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index 42a762936..fb1d3c974 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs | |||
@@ -5,9 +5,15 @@ use std::{ | |||
5 | sync::{Arc, Mutex}, | 5 | sync::{Arc, Mutex}, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use base_db::SourceDatabase; | ||
8 | use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; | 9 | use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; |
9 | use hir_expand::db::AstDatabase; | 10 | use hir_expand::db::AstDatabase; |
11 | use hir_expand::diagnostics::Diagnostic; | ||
12 | use hir_expand::diagnostics::DiagnosticSinkBuilder; | ||
13 | use rustc_hash::FxHashMap; | ||
10 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
15 | use syntax::TextRange; | ||
16 | use test_utils::extract_annotations; | ||
11 | 17 | ||
12 | use crate::db::DefDatabase; | 18 | use crate::db::DefDatabase; |
13 | 19 | ||
@@ -98,4 +104,40 @@ impl TestDB { | |||
98 | }) | 104 | }) |
99 | .collect() | 105 | .collect() |
100 | } | 106 | } |
107 | |||
108 | pub fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> { | ||
109 | let mut files = Vec::new(); | ||
110 | let crate_graph = self.crate_graph(); | ||
111 | for krate in crate_graph.iter() { | ||
112 | let crate_def_map = self.crate_def_map(krate); | ||
113 | for (module_id, _) in crate_def_map.modules.iter() { | ||
114 | let file_id = crate_def_map[module_id].origin.file_id(); | ||
115 | files.extend(file_id) | ||
116 | } | ||
117 | } | ||
118 | assert!(!files.is_empty()); | ||
119 | files | ||
120 | .into_iter() | ||
121 | .filter_map(|file_id| { | ||
122 | let text = self.file_text(file_id); | ||
123 | let annotations = extract_annotations(&text); | ||
124 | if annotations.is_empty() { | ||
125 | return None; | ||
126 | } | ||
127 | Some((file_id, annotations)) | ||
128 | }) | ||
129 | .collect() | ||
130 | } | ||
131 | |||
132 | pub fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { | ||
133 | let crate_graph = self.crate_graph(); | ||
134 | for krate in crate_graph.iter() { | ||
135 | let crate_def_map = self.crate_def_map(krate); | ||
136 | |||
137 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); | ||
138 | for (module_id, _) in crate_def_map.modules.iter() { | ||
139 | crate_def_map.add_diagnostics(self, module_id, &mut sink); | ||
140 | } | ||
141 | } | ||
142 | } | ||
101 | } | 143 | } |
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index 7d02aaf95..bc86df2b1 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -17,9 +17,9 @@ ena = "0.14.0" | |||
17 | log = "0.4.8" | 17 | log = "0.4.8" |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | scoped-tls = "1" | 19 | scoped-tls = "1" |
20 | chalk-solve = { version = "0.25.0" } | 20 | chalk-solve = { version = "0.27.0" } |
21 | chalk-ir = { version = "0.25.0" } | 21 | chalk-ir = { version = "0.27.0" } |
22 | chalk-recursive = { version = "0.25.0" } | 22 | chalk-recursive = { version = "0.27.0" } |
23 | 23 | ||
24 | stdx = { path = "../stdx", version = "0.0.0" } | 24 | stdx = { path = "../stdx", version = "0.0.0" } |
25 | hir_def = { path = "../hir_def", version = "0.0.0" } | 25 | hir_def = { path = "../hir_def", version = "0.0.0" } |
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index efb48c7ee..f389c5a4b 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs | |||
@@ -380,6 +380,15 @@ impl HirDisplay for ApplicationTy { | |||
380 | write!(f, ">")?; | 380 | write!(f, ">")?; |
381 | } | 381 | } |
382 | } | 382 | } |
383 | TypeCtor::ForeignType(type_alias) => { | ||
384 | let type_alias = f.db.type_alias_data(type_alias); | ||
385 | write!(f, "{}", type_alias.name)?; | ||
386 | if self.parameters.len() > 0 { | ||
387 | write!(f, "<")?; | ||
388 | f.write_joined(&*self.parameters.0, ", ")?; | ||
389 | write!(f, ">")?; | ||
390 | } | ||
391 | } | ||
383 | TypeCtor::OpaqueType(opaque_ty_id) => { | 392 | TypeCtor::OpaqueType(opaque_ty_id) => { |
384 | match opaque_ty_id { | 393 | match opaque_ty_id { |
385 | OpaqueTyId::ReturnTypeImplTrait(func, idx) => { | 394 | OpaqueTyId::ReturnTypeImplTrait(func, idx) => { |
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index f16d1fc97..768d95eff 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -134,6 +134,9 @@ pub enum TypeCtor { | |||
134 | /// representing the Future::Output type. | 134 | /// representing the Future::Output type. |
135 | OpaqueType(OpaqueTyId), | 135 | OpaqueType(OpaqueTyId), |
136 | 136 | ||
137 | /// Represents a foreign type declared in external blocks. | ||
138 | ForeignType(TypeAliasId), | ||
139 | |||
137 | /// The type of a specific closure. | 140 | /// The type of a specific closure. |
138 | /// | 141 | /// |
139 | /// The closure signature is stored in a `FnPtr` type in the first type | 142 | /// The closure signature is stored in a `FnPtr` type in the first type |
@@ -168,6 +171,10 @@ impl TypeCtor { | |||
168 | let generic_params = generics(db.upcast(), type_alias.into()); | 171 | let generic_params = generics(db.upcast(), type_alias.into()); |
169 | generic_params.len() | 172 | generic_params.len() |
170 | } | 173 | } |
174 | TypeCtor::ForeignType(type_alias) => { | ||
175 | let generic_params = generics(db.upcast(), type_alias.into()); | ||
176 | generic_params.len() | ||
177 | } | ||
171 | TypeCtor::OpaqueType(opaque_ty_id) => { | 178 | TypeCtor::OpaqueType(opaque_ty_id) => { |
172 | match opaque_ty_id { | 179 | match opaque_ty_id { |
173 | OpaqueTyId::ReturnTypeImplTrait(func, _) => { | 180 | OpaqueTyId::ReturnTypeImplTrait(func, _) => { |
@@ -204,6 +211,9 @@ impl TypeCtor { | |||
204 | TypeCtor::AssociatedType(type_alias) => { | 211 | TypeCtor::AssociatedType(type_alias) => { |
205 | Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate) | 212 | Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate) |
206 | } | 213 | } |
214 | TypeCtor::ForeignType(type_alias) => { | ||
215 | Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate) | ||
216 | } | ||
207 | TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id { | 217 | TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id { |
208 | OpaqueTyId::ReturnTypeImplTrait(func, _) => { | 218 | OpaqueTyId::ReturnTypeImplTrait(func, _) => { |
209 | Some(func.lookup(db.upcast()).module(db.upcast()).krate) | 219 | Some(func.lookup(db.upcast()).module(db.upcast()).krate) |
@@ -231,6 +241,7 @@ impl TypeCtor { | |||
231 | TypeCtor::Adt(adt) => Some(adt.into()), | 241 | TypeCtor::Adt(adt) => Some(adt.into()), |
232 | TypeCtor::FnDef(callable) => Some(callable.into()), | 242 | TypeCtor::FnDef(callable) => Some(callable.into()), |
233 | TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()), | 243 | TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()), |
244 | TypeCtor::ForeignType(type_alias) => Some(type_alias.into()), | ||
234 | TypeCtor::OpaqueType(_impl_trait_id) => None, | 245 | TypeCtor::OpaqueType(_impl_trait_id) => None, |
235 | } | 246 | } |
236 | } | 247 | } |
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index cd574e983..708e2af0f 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -1101,10 +1101,14 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { | |||
1101 | let resolver = t.resolver(db.upcast()); | 1101 | let resolver = t.resolver(db.upcast()); |
1102 | let ctx = | 1102 | let ctx = |
1103 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); | 1103 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); |
1104 | let type_ref = &db.type_alias_data(t).type_ref; | ||
1105 | let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); | 1104 | let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); |
1106 | let inner = Ty::from_hir(&ctx, type_ref.as_ref().unwrap_or(&TypeRef::Error)); | 1105 | if db.type_alias_data(t).is_extern { |
1107 | Binders::new(substs.len(), inner) | 1106 | Binders::new(substs.len(), Ty::apply(TypeCtor::ForeignType(t), substs)) |
1107 | } else { | ||
1108 | let type_ref = &db.type_alias_data(t).type_ref; | ||
1109 | let inner = Ty::from_hir(&ctx, type_ref.as_ref().unwrap_or(&TypeRef::Error)); | ||
1110 | Binders::new(substs.len(), inner) | ||
1111 | } | ||
1108 | } | 1112 | } |
1109 | 1113 | ||
1110 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 1114 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index ec59145c7..8961df404 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -250,6 +250,14 @@ impl Ty { | |||
250 | TypeCtor::Adt(def_id) => { | 250 | TypeCtor::Adt(def_id) => { |
251 | return Some(std::iter::once(def_id.module(db.upcast()).krate).collect()) | 251 | return Some(std::iter::once(def_id.module(db.upcast()).krate).collect()) |
252 | } | 252 | } |
253 | TypeCtor::ForeignType(type_alias_id) => { | ||
254 | return Some( | ||
255 | std::iter::once( | ||
256 | type_alias_id.lookup(db.upcast()).module(db.upcast()).krate, | ||
257 | ) | ||
258 | .collect(), | ||
259 | ) | ||
260 | } | ||
253 | TypeCtor::Bool => lang_item_crate!("bool"), | 261 | TypeCtor::Bool => lang_item_crate!("bool"), |
254 | TypeCtor::Char => lang_item_crate!("char"), | 262 | TypeCtor::Char => lang_item_crate!("char"), |
255 | TypeCtor::Float(f) => match f.bitness { | 263 | TypeCtor::Float(f) => match f.bitness { |
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 23b2601e6..0f17ff151 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs | |||
@@ -1051,3 +1051,39 @@ fn dyn_trait_super_trait_not_in_scope() { | |||
1051 | "#]], | 1051 | "#]], |
1052 | ); | 1052 | ); |
1053 | } | 1053 | } |
1054 | |||
1055 | #[test] | ||
1056 | fn method_resolution_foreign_opaque_type() { | ||
1057 | check_infer( | ||
1058 | r#" | ||
1059 | extern "C" { | ||
1060 | type S; | ||
1061 | fn f() -> &'static S; | ||
1062 | } | ||
1063 | |||
1064 | impl S { | ||
1065 | fn foo(&self) -> bool { | ||
1066 | true | ||
1067 | } | ||
1068 | } | ||
1069 | |||
1070 | fn test() { | ||
1071 | let s = unsafe { f() }; | ||
1072 | s.foo(); | ||
1073 | } | ||
1074 | "#, | ||
1075 | expect![[r#" | ||
1076 | 75..79 'self': &S | ||
1077 | 89..109 '{ ... }': bool | ||
1078 | 99..103 'true': bool | ||
1079 | 123..167 '{ ...o(); }': () | ||
1080 | 133..134 's': &S | ||
1081 | 137..151 'unsafe { f() }': &S | ||
1082 | 144..151 '{ f() }': &S | ||
1083 | 146..147 'f': fn f() -> &S | ||
1084 | 146..149 'f()': &S | ||
1085 | 157..158 's': &S | ||
1086 | 157..164 's.foo()': bool | ||
1087 | "#]], | ||
1088 | ); | ||
1089 | } | ||
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs index 57d0a32df..27f0ed628 100644 --- a/crates/hir_ty/src/traits/chalk.rs +++ b/crates/hir_ty/src/traits/chalk.rs | |||
@@ -23,7 +23,8 @@ use crate::{ | |||
23 | ProjectionTy, Substs, TraitRef, Ty, TypeCtor, | 23 | ProjectionTy, Substs, TraitRef, Ty, TypeCtor, |
24 | }; | 24 | }; |
25 | use mapping::{ | 25 | use mapping::{ |
26 | convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsValue, | 26 | convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsAssocType, |
27 | TypeAliasAsValue, | ||
27 | }; | 28 | }; |
28 | 29 | ||
29 | pub use self::interner::*; | 30 | pub use self::interner::*; |
@@ -340,7 +341,7 @@ pub(crate) fn associated_ty_data_query( | |||
340 | id: AssocTypeId, | 341 | id: AssocTypeId, |
341 | ) -> Arc<AssociatedTyDatum> { | 342 | ) -> Arc<AssociatedTyDatum> { |
342 | debug!("associated_ty_data {:?}", id); | 343 | debug!("associated_ty_data {:?}", id); |
343 | let type_alias: TypeAliasId = from_chalk(db, id); | 344 | let type_alias: TypeAliasId = from_chalk::<TypeAliasAsAssocType, _>(db, id).0; |
344 | let trait_ = match type_alias.lookup(db.upcast()).container { | 345 | let trait_ = match type_alias.lookup(db.upcast()).container { |
345 | AssocContainerId::TraitId(t) => t, | 346 | AssocContainerId::TraitId(t) => t, |
346 | _ => panic!("associated type not in trait"), | 347 | _ => panic!("associated type not in trait"), |
@@ -394,8 +395,10 @@ pub(crate) fn trait_datum_query( | |||
394 | fundamental: false, | 395 | fundamental: false, |
395 | }; | 396 | }; |
396 | let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); | 397 | let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); |
397 | let associated_ty_ids = | 398 | let associated_ty_ids = trait_data |
398 | trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect(); | 399 | .associated_types() |
400 | .map(|type_alias| TypeAliasAsAssocType(type_alias).to_chalk(db)) | ||
401 | .collect(); | ||
399 | let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; | 402 | let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; |
400 | let well_known = | 403 | let well_known = |
401 | lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); | 404 | lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); |
@@ -433,6 +436,7 @@ fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str { | |||
433 | WellKnownTrait::FnMut => "fn_mut", | 436 | WellKnownTrait::FnMut => "fn_mut", |
434 | WellKnownTrait::Fn => "fn", | 437 | WellKnownTrait::Fn => "fn", |
435 | WellKnownTrait::Unsize => "unsize", | 438 | WellKnownTrait::Unsize => "unsize", |
439 | WellKnownTrait::Unpin => "unpin", | ||
436 | } | 440 | } |
437 | } | 441 | } |
438 | 442 | ||
@@ -576,7 +580,7 @@ fn type_alias_associated_ty_value( | |||
576 | let value_bound = rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) }; | 580 | let value_bound = rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) }; |
577 | let value = rust_ir::AssociatedTyValue { | 581 | let value = rust_ir::AssociatedTyValue { |
578 | impl_id: impl_id.to_chalk(db), | 582 | impl_id: impl_id.to_chalk(db), |
579 | associated_ty_id: assoc_ty.to_chalk(db), | 583 | associated_ty_id: TypeAliasAsAssocType(assoc_ty).to_chalk(db), |
580 | value: make_binders(value_bound, ty.num_binders), | 584 | value: make_binders(value_bound, ty.num_binders), |
581 | }; | 585 | }; |
582 | Arc::new(value) | 586 | Arc::new(value) |
@@ -611,9 +615,11 @@ pub(crate) fn fn_def_datum_query( | |||
611 | }; | 615 | }; |
612 | let datum = FnDefDatum { | 616 | let datum = FnDefDatum { |
613 | id: fn_def_id, | 617 | id: fn_def_id, |
614 | abi: (), | 618 | sig: chalk_ir::FnSig { |
615 | safety: chalk_ir::Safety::Safe, | 619 | abi: (), |
616 | variadic: sig.value.is_varargs, | 620 | safety: chalk_ir::Safety::Safe, |
621 | variadic: sig.value.is_varargs, | ||
622 | }, | ||
617 | binders: make_binders(bound, sig.num_binders), | 623 | binders: make_binders(bound, sig.num_binders), |
618 | }; | 624 | }; |
619 | Arc::new(datum) | 625 | Arc::new(datum) |
diff --git a/crates/hir_ty/src/traits/chalk/interner.rs b/crates/hir_ty/src/traits/chalk/interner.rs index eb35db3ff..f9304b7d0 100644 --- a/crates/hir_ty/src/traits/chalk/interner.rs +++ b/crates/hir_ty/src/traits/chalk/interner.rs | |||
@@ -12,6 +12,7 @@ pub struct Interner; | |||
12 | 12 | ||
13 | pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>; | 13 | pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>; |
14 | pub type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>; | 14 | pub type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>; |
15 | pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>; | ||
15 | pub type TraitId = chalk_ir::TraitId<Interner>; | 16 | pub type TraitId = chalk_ir::TraitId<Interner>; |
16 | pub type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>; | 17 | pub type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>; |
17 | pub type AdtId = chalk_ir::AdtId<Interner>; | 18 | pub type AdtId = chalk_ir::AdtId<Interner>; |
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs index d6bacba1d..d42f4bba9 100644 --- a/crates/hir_ty/src/traits/chalk/mapping.rs +++ b/crates/hir_ty/src/traits/chalk/mapping.rs | |||
@@ -34,9 +34,11 @@ impl ToChalk for Ty { | |||
34 | let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner); | 34 | let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner); |
35 | chalk_ir::TyData::Function(chalk_ir::FnPointer { | 35 | chalk_ir::TyData::Function(chalk_ir::FnPointer { |
36 | num_binders: 0, | 36 | num_binders: 0, |
37 | abi: (), | 37 | sig: chalk_ir::FnSig { |
38 | safety: chalk_ir::Safety::Safe, | 38 | abi: (), |
39 | variadic: is_varargs, | 39 | safety: chalk_ir::Safety::Safe, |
40 | variadic: is_varargs, | ||
41 | }, | ||
40 | substitution, | 42 | substitution, |
41 | }) | 43 | }) |
42 | .intern(&Interner) | 44 | .intern(&Interner) |
@@ -48,7 +50,7 @@ impl ToChalk for Ty { | |||
48 | } | 50 | } |
49 | }, | 51 | }, |
50 | Ty::Projection(proj_ty) => { | 52 | Ty::Projection(proj_ty) => { |
51 | let associated_ty_id = proj_ty.associated_ty.to_chalk(db); | 53 | let associated_ty_id = TypeAliasAsAssocType(proj_ty.associated_ty).to_chalk(db); |
52 | let substitution = proj_ty.parameters.to_chalk(db); | 54 | let substitution = proj_ty.parameters.to_chalk(db); |
53 | chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { | 55 | chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { |
54 | associated_ty_id, | 56 | associated_ty_id, |
@@ -114,7 +116,8 @@ impl ToChalk for Ty { | |||
114 | Ty::Placeholder(db.lookup_intern_type_param_id(interned_id)) | 116 | Ty::Placeholder(db.lookup_intern_type_param_id(interned_id)) |
115 | } | 117 | } |
116 | chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => { | 118 | chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => { |
117 | let associated_ty = from_chalk(db, proj.associated_ty_id); | 119 | let associated_ty = |
120 | from_chalk::<TypeAliasAsAssocType, _>(db, proj.associated_ty_id).0; | ||
118 | let parameters = from_chalk(db, proj.substitution); | 121 | let parameters = from_chalk(db, proj.substitution); |
119 | Ty::Projection(ProjectionTy { associated_ty, parameters }) | 122 | Ty::Projection(ProjectionTy { associated_ty, parameters }) |
120 | } | 123 | } |
@@ -125,7 +128,7 @@ impl ToChalk for Ty { | |||
125 | } | 128 | } |
126 | chalk_ir::TyData::Function(chalk_ir::FnPointer { | 129 | chalk_ir::TyData::Function(chalk_ir::FnPointer { |
127 | num_binders, | 130 | num_binders, |
128 | variadic, | 131 | sig: chalk_ir::FnSig { variadic, .. }, |
129 | substitution, | 132 | substitution, |
130 | .. | 133 | .. |
131 | }) => { | 134 | }) => { |
@@ -290,8 +293,9 @@ impl ToChalk for TypeCtor { | |||
290 | fn to_chalk(self, db: &dyn HirDatabase) -> TypeName<Interner> { | 293 | fn to_chalk(self, db: &dyn HirDatabase) -> TypeName<Interner> { |
291 | match self { | 294 | match self { |
292 | TypeCtor::AssociatedType(type_alias) => { | 295 | TypeCtor::AssociatedType(type_alias) => { |
293 | let type_id = type_alias.to_chalk(db); | 296 | let assoc_type = TypeAliasAsAssocType(type_alias); |
294 | TypeName::AssociatedType(type_id) | 297 | let assoc_type_id = assoc_type.to_chalk(db); |
298 | TypeName::AssociatedType(assoc_type_id) | ||
295 | } | 299 | } |
296 | 300 | ||
297 | TypeCtor::OpaqueType(impl_trait_id) => { | 301 | TypeCtor::OpaqueType(impl_trait_id) => { |
@@ -299,6 +303,12 @@ impl ToChalk for TypeCtor { | |||
299 | TypeName::OpaqueType(id) | 303 | TypeName::OpaqueType(id) |
300 | } | 304 | } |
301 | 305 | ||
306 | TypeCtor::ForeignType(type_alias) => { | ||
307 | let foreign_type = TypeAliasAsForeignType(type_alias); | ||
308 | let foreign_type_id = foreign_type.to_chalk(db); | ||
309 | TypeName::Foreign(foreign_type_id) | ||
310 | } | ||
311 | |||
302 | TypeCtor::Bool => TypeName::Scalar(Scalar::Bool), | 312 | TypeCtor::Bool => TypeName::Scalar(Scalar::Bool), |
303 | TypeCtor::Char => TypeName::Scalar(Scalar::Char), | 313 | TypeCtor::Char => TypeName::Scalar(Scalar::Char), |
304 | TypeCtor::Int(int_ty) => TypeName::Scalar(int_ty_to_chalk(int_ty)), | 314 | TypeCtor::Int(int_ty) => TypeName::Scalar(int_ty_to_chalk(int_ty)), |
@@ -339,7 +349,9 @@ impl ToChalk for TypeCtor { | |||
339 | fn from_chalk(db: &dyn HirDatabase, type_name: TypeName<Interner>) -> TypeCtor { | 349 | fn from_chalk(db: &dyn HirDatabase, type_name: TypeName<Interner>) -> TypeCtor { |
340 | match type_name { | 350 | match type_name { |
341 | TypeName::Adt(struct_id) => TypeCtor::Adt(struct_id.0), | 351 | TypeName::Adt(struct_id) => TypeCtor::Adt(struct_id.0), |
342 | TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), | 352 | TypeName::AssociatedType(type_id) => { |
353 | TypeCtor::AssociatedType(from_chalk::<TypeAliasAsAssocType, _>(db, type_id).0) | ||
354 | } | ||
343 | TypeName::OpaqueType(opaque_type_id) => { | 355 | TypeName::OpaqueType(opaque_type_id) => { |
344 | TypeCtor::OpaqueType(from_chalk(db, opaque_type_id)) | 356 | TypeCtor::OpaqueType(from_chalk(db, opaque_type_id)) |
345 | } | 357 | } |
@@ -379,6 +391,10 @@ impl ToChalk for TypeCtor { | |||
379 | TypeCtor::Closure { def, expr } | 391 | TypeCtor::Closure { def, expr } |
380 | } | 392 | } |
381 | 393 | ||
394 | TypeName::Foreign(foreign_def_id) => { | ||
395 | TypeCtor::ForeignType(from_chalk::<TypeAliasAsForeignType, _>(db, foreign_def_id).0) | ||
396 | } | ||
397 | |||
382 | TypeName::Error => { | 398 | TypeName::Error => { |
383 | // this should not be reached, since we don't represent TypeName::Error with TypeCtor | 399 | // this should not be reached, since we don't represent TypeName::Error with TypeCtor |
384 | unreachable!() | 400 | unreachable!() |
@@ -488,15 +504,31 @@ impl ToChalk for CallableDefId { | |||
488 | } | 504 | } |
489 | } | 505 | } |
490 | 506 | ||
491 | impl ToChalk for TypeAliasId { | 507 | pub struct TypeAliasAsAssocType(pub TypeAliasId); |
508 | |||
509 | impl ToChalk for TypeAliasAsAssocType { | ||
492 | type Chalk = AssocTypeId; | 510 | type Chalk = AssocTypeId; |
493 | 511 | ||
494 | fn to_chalk(self, _db: &dyn HirDatabase) -> AssocTypeId { | 512 | fn to_chalk(self, _db: &dyn HirDatabase) -> AssocTypeId { |
495 | chalk_ir::AssocTypeId(self.as_intern_id()) | 513 | chalk_ir::AssocTypeId(self.0.as_intern_id()) |
514 | } | ||
515 | |||
516 | fn from_chalk(_db: &dyn HirDatabase, assoc_type_id: AssocTypeId) -> TypeAliasAsAssocType { | ||
517 | TypeAliasAsAssocType(InternKey::from_intern_id(assoc_type_id.0)) | ||
518 | } | ||
519 | } | ||
520 | |||
521 | pub struct TypeAliasAsForeignType(pub TypeAliasId); | ||
522 | |||
523 | impl ToChalk for TypeAliasAsForeignType { | ||
524 | type Chalk = ForeignDefId; | ||
525 | |||
526 | fn to_chalk(self, _db: &dyn HirDatabase) -> ForeignDefId { | ||
527 | chalk_ir::ForeignDefId(self.0.as_intern_id()) | ||
496 | } | 528 | } |
497 | 529 | ||
498 | fn from_chalk(_db: &dyn HirDatabase, type_alias_id: AssocTypeId) -> TypeAliasId { | 530 | fn from_chalk(_db: &dyn HirDatabase, foreign_def_id: ForeignDefId) -> TypeAliasAsForeignType { |
499 | InternKey::from_intern_id(type_alias_id.0) | 531 | TypeAliasAsForeignType(InternKey::from_intern_id(foreign_def_id.0)) |
500 | } | 532 | } |
501 | } | 533 | } |
502 | 534 | ||
@@ -580,7 +612,7 @@ impl ToChalk for ProjectionTy { | |||
580 | 612 | ||
581 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> { | 613 | fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> { |
582 | chalk_ir::ProjectionTy { | 614 | chalk_ir::ProjectionTy { |
583 | associated_ty_id: self.associated_ty.to_chalk(db), | 615 | associated_ty_id: TypeAliasAsAssocType(self.associated_ty).to_chalk(db), |
584 | substitution: self.parameters.to_chalk(db), | 616 | substitution: self.parameters.to_chalk(db), |
585 | } | 617 | } |
586 | } | 618 | } |
@@ -590,7 +622,11 @@ impl ToChalk for ProjectionTy { | |||
590 | projection_ty: chalk_ir::ProjectionTy<Interner>, | 622 | projection_ty: chalk_ir::ProjectionTy<Interner>, |
591 | ) -> ProjectionTy { | 623 | ) -> ProjectionTy { |
592 | ProjectionTy { | 624 | ProjectionTy { |
593 | associated_ty: from_chalk(db, projection_ty.associated_ty_id), | 625 | associated_ty: from_chalk::<TypeAliasAsAssocType, _>( |
626 | db, | ||
627 | projection_ty.associated_ty_id, | ||
628 | ) | ||
629 | .0, | ||
594 | parameters: from_chalk(db, projection_ty.substitution), | 630 | parameters: from_chalk(db, projection_ty.substitution), |
595 | } | 631 | } |
596 | } | 632 | } |
@@ -789,7 +825,8 @@ pub(super) fn generic_predicate_to_inline_bound( | |||
789 | let alias_eq_bound = rust_ir::AliasEqBound { | 825 | let alias_eq_bound = rust_ir::AliasEqBound { |
790 | value: proj.ty.clone().to_chalk(db), | 826 | value: proj.ty.clone().to_chalk(db), |
791 | trait_bound: rust_ir::TraitBound { trait_id: trait_.to_chalk(db), args_no_self }, | 827 | trait_bound: rust_ir::TraitBound { trait_id: trait_.to_chalk(db), args_no_self }, |
792 | associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db), | 828 | associated_ty_id: TypeAliasAsAssocType(proj.projection_ty.associated_ty) |
829 | .to_chalk(db), | ||
793 | parameters: Vec::new(), // FIXME we don't support generic associated types yet | 830 | parameters: Vec::new(), // FIXME we don't support generic associated types yet |
794 | }; | 831 | }; |
795 | Some(rust_ir::InlineBound::AliasEqBound(alias_eq_bound)) | 832 | Some(rust_ir::InlineBound::AliasEqBound(alias_eq_bound)) |
diff --git a/crates/hir_ty/src/traits/chalk/tls.rs b/crates/hir_ty/src/traits/chalk/tls.rs index cb6b0fe81..b4568cff6 100644 --- a/crates/hir_ty/src/traits/chalk/tls.rs +++ b/crates/hir_ty/src/traits/chalk/tls.rs | |||
@@ -4,7 +4,7 @@ use std::fmt; | |||
4 | use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication, TypeName}; | 4 | use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication, TypeName}; |
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | 6 | ||
7 | use super::{from_chalk, Interner}; | 7 | use super::{from_chalk, Interner, TypeAliasAsAssocType}; |
8 | use crate::{db::HirDatabase, CallableDefId, TypeCtor}; | 8 | use crate::{db::HirDatabase, CallableDefId, TypeCtor}; |
9 | use hir_def::{AdtId, AssocContainerId, DefWithBodyId, Lookup, TypeAliasId}; | 9 | use hir_def::{AdtId, AssocContainerId, DefWithBodyId, Lookup, TypeAliasId}; |
10 | 10 | ||
@@ -77,6 +77,10 @@ impl DebugContext<'_> { | |||
77 | write!(f, "{{impl trait of async block {} of {:?}}}", idx.into_raw(), def)?; | 77 | write!(f, "{{impl trait of async block {} of {:?}}}", idx.into_raw(), def)?; |
78 | } | 78 | } |
79 | }, | 79 | }, |
80 | TypeCtor::ForeignType(type_alias) => { | ||
81 | let name = self.0.type_alias_data(type_alias).name.clone(); | ||
82 | write!(f, "{}", name)?; | ||
83 | } | ||
80 | TypeCtor::Closure { def, expr } => { | 84 | TypeCtor::Closure { def, expr } => { |
81 | write!(f, "{{closure {:?} in ", expr.into_raw())?; | 85 | write!(f, "{{closure {:?} in ", expr.into_raw())?; |
82 | match def { | 86 | match def { |
@@ -119,7 +123,7 @@ impl DebugContext<'_> { | |||
119 | id: super::AssocTypeId, | 123 | id: super::AssocTypeId, |
120 | fmt: &mut fmt::Formatter<'_>, | 124 | fmt: &mut fmt::Formatter<'_>, |
121 | ) -> Result<(), fmt::Error> { | 125 | ) -> Result<(), fmt::Error> { |
122 | let type_alias: TypeAliasId = from_chalk(self.0, id); | 126 | let type_alias: TypeAliasId = from_chalk::<TypeAliasAsAssocType, _>(self.0, id).0; |
123 | let type_alias_data = self.0.type_alias_data(type_alias); | 127 | let type_alias_data = self.0.type_alias_data(type_alias); |
124 | let trait_ = match type_alias.lookup(self.0.upcast()).container { | 128 | let trait_ = match type_alias.lookup(self.0.upcast()).container { |
125 | AssocContainerId::TraitId(t) => t, | 129 | AssocContainerId::TraitId(t) => t, |
@@ -153,7 +157,8 @@ impl DebugContext<'_> { | |||
153 | projection_ty: &chalk_ir::ProjectionTy<Interner>, | 157 | projection_ty: &chalk_ir::ProjectionTy<Interner>, |
154 | fmt: &mut fmt::Formatter<'_>, | 158 | fmt: &mut fmt::Formatter<'_>, |
155 | ) -> Result<(), fmt::Error> { | 159 | ) -> Result<(), fmt::Error> { |
156 | let type_alias: TypeAliasId = from_chalk(self.0, projection_ty.associated_ty_id); | 160 | let type_alias: TypeAliasId = |
161 | from_chalk::<TypeAliasAsAssocType, _>(self.0, projection_ty.associated_ty_id).0; | ||
157 | let type_alias_data = self.0.type_alias_data(type_alias); | 162 | let type_alias_data = self.0.type_alias_data(type_alias); |
158 | let trait_ = match type_alias.lookup(self.0.upcast()).container { | 163 | let trait_ = match type_alias.lookup(self.0.upcast()).container { |
159 | AssocContainerId::TraitId(t) => t, | 164 | AssocContainerId::TraitId(t) => t, |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index b2b972b02..dc815a483 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -622,13 +622,65 @@ pub struct Foo { pub a: i32, pub b: i32 } | |||
622 | r#" | 622 | r#" |
623 | use a; | 623 | use a; |
624 | use a::{c, d::e}; | 624 | use a::{c, d::e}; |
625 | |||
626 | mod a { | ||
627 | mod c {} | ||
628 | mod d { | ||
629 | mod e {} | ||
630 | } | ||
631 | } | ||
625 | "#, | 632 | "#, |
626 | ); | 633 | ); |
627 | check_fix(r#"use {<|>b};"#, r#"use b;"#); | 634 | check_fix( |
628 | check_fix(r#"use {b<|>};"#, r#"use b;"#); | 635 | r" |
629 | check_fix(r#"use a::{c<|>};"#, r#"use a::c;"#); | 636 | mod b {} |
630 | check_fix(r#"use a::{self<|>};"#, r#"use a;"#); | 637 | use {<|>b}; |
631 | check_fix(r#"use a::{c, d::{e<|>}};"#, r#"use a::{c, d::e};"#); | 638 | ", |
639 | r" | ||
640 | mod b {} | ||
641 | use b; | ||
642 | ", | ||
643 | ); | ||
644 | check_fix( | ||
645 | r" | ||
646 | mod b {} | ||
647 | use {b<|>}; | ||
648 | ", | ||
649 | r" | ||
650 | mod b {} | ||
651 | use b; | ||
652 | ", | ||
653 | ); | ||
654 | check_fix( | ||
655 | r" | ||
656 | mod a { mod c {} } | ||
657 | use a::{c<|>}; | ||
658 | ", | ||
659 | r" | ||
660 | mod a { mod c {} } | ||
661 | use a::c; | ||
662 | ", | ||
663 | ); | ||
664 | check_fix( | ||
665 | r" | ||
666 | mod a {} | ||
667 | use a::{self<|>}; | ||
668 | ", | ||
669 | r" | ||
670 | mod a {} | ||
671 | use a; | ||
672 | ", | ||
673 | ); | ||
674 | check_fix( | ||
675 | r" | ||
676 | mod a { mod c {} mod d { mod e {} } } | ||
677 | use a::{c, d::{e<|>}}; | ||
678 | ", | ||
679 | r" | ||
680 | mod a { mod c {} mod d { mod e {} } } | ||
681 | use a::{c, d::e}; | ||
682 | ", | ||
683 | ); | ||
632 | } | 684 | } |
633 | 685 | ||
634 | #[test] | 686 | #[test] |
diff --git a/crates/project_model/src/project_json.rs b/crates/project_model/src/project_json.rs index 979e90058..545f254aa 100644 --- a/crates/project_model/src/project_json.rs +++ b/crates/project_model/src/project_json.rs | |||
@@ -13,6 +13,7 @@ use crate::cfg_flag::CfgFlag; | |||
13 | #[derive(Clone, Debug, Eq, PartialEq)] | 13 | #[derive(Clone, Debug, Eq, PartialEq)] |
14 | pub struct ProjectJson { | 14 | pub struct ProjectJson { |
15 | pub(crate) sysroot_src: Option<AbsPathBuf>, | 15 | pub(crate) sysroot_src: Option<AbsPathBuf>, |
16 | project_root: Option<AbsPathBuf>, | ||
16 | crates: Vec<Crate>, | 17 | crates: Vec<Crate>, |
17 | } | 18 | } |
18 | 19 | ||
@@ -36,6 +37,7 @@ impl ProjectJson { | |||
36 | pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { | 37 | pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { |
37 | ProjectJson { | 38 | ProjectJson { |
38 | sysroot_src: data.sysroot_src.map(|it| base.join(it)), | 39 | sysroot_src: data.sysroot_src.map(|it| base.join(it)), |
40 | project_root: base.parent().map(AbsPath::to_path_buf), | ||
39 | crates: data | 41 | crates: data |
40 | .crates | 42 | .crates |
41 | .into_iter() | 43 | .into_iter() |
@@ -89,6 +91,12 @@ impl ProjectJson { | |||
89 | pub fn crates(&self) -> impl Iterator<Item = (CrateId, &Crate)> + '_ { | 91 | pub fn crates(&self) -> impl Iterator<Item = (CrateId, &Crate)> + '_ { |
90 | self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate)) | 92 | self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate)) |
91 | } | 93 | } |
94 | pub fn path(&self) -> Option<&AbsPath> { | ||
95 | match &self.project_root { | ||
96 | Some(p) => Some(p.as_path()), | ||
97 | None => None, | ||
98 | } | ||
99 | } | ||
92 | } | 100 | } |
93 | 101 | ||
94 | #[derive(Deserialize)] | 102 | #[derive(Deserialize)] |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index bab6f8a71..b819618cb 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | use std::{mem, sync::Arc}; | 2 | use std::{mem, sync::Arc}; |
3 | 3 | ||
4 | use base_db::{CrateGraph, SourceRoot, VfsPath}; | 4 | use base_db::{CrateGraph, SourceRoot, VfsPath}; |
5 | use flycheck::FlycheckHandle; | 5 | use flycheck::{FlycheckConfig, FlycheckHandle}; |
6 | use ide::AnalysisChange; | 6 | use ide::AnalysisChange; |
7 | use project_model::{ProcMacroClient, ProjectWorkspace}; | 7 | use project_model::{ProcMacroClient, ProjectWorkspace}; |
8 | use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; | 8 | use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; |
@@ -244,13 +244,17 @@ impl GlobalState { | |||
244 | .iter() | 244 | .iter() |
245 | // FIXME: Figure out the multi-workspace situation | 245 | // FIXME: Figure out the multi-workspace situation |
246 | .find_map(|w| match w { | 246 | .find_map(|w| match w { |
247 | ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some(cargo), | 247 | ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some(cargo.workspace_root()), |
248 | ProjectWorkspace::Json { .. } => None, | 248 | ProjectWorkspace::Json { project, .. } => { |
249 | }) | 249 | // Enable flychecks for json projects if a custom flycheck command was supplied |
250 | .map(move |cargo| { | 250 | // in the workspace configuration. |
251 | let cargo_project_root = cargo.workspace_root().to_path_buf(); | 251 | match config { |
252 | FlycheckHandle::spawn(sender, config, cargo_project_root.into()) | 252 | FlycheckConfig::CustomCommand { .. } => project.path(), |
253 | _ => None, | ||
254 | } | ||
255 | } | ||
253 | }) | 256 | }) |
257 | .map(move |root| FlycheckHandle::spawn(sender, config, root.to_path_buf().into())) | ||
254 | } | 258 | } |
255 | } | 259 | } |
256 | 260 | ||
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 8b1c65dd6..45cf31f13 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -347,6 +347,7 @@ impl ast::UseTree { | |||
347 | self.clone() | 347 | self.clone() |
348 | } | 348 | } |
349 | 349 | ||
350 | /// Splits off the given prefix, making it the path component of the use tree, appending the rest of the path to all UseTreeList items. | ||
350 | #[must_use] | 351 | #[must_use] |
351 | pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { | 352 | pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { |
352 | let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() { | 353 | let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() { |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 25e8a359d..6868feed9 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -38,6 +38,10 @@ pub fn path_from_text(text: &str) -> ast::Path { | |||
38 | ast_from_text(text) | 38 | ast_from_text(text) |
39 | } | 39 | } |
40 | 40 | ||
41 | pub fn glob_use_tree() -> ast::UseTree { | ||
42 | ast_from_text("use *;") | ||
43 | } | ||
44 | |||
41 | pub fn use_tree( | 45 | pub fn use_tree( |
42 | path: ast::Path, | 46 | path: ast::Path, |
43 | use_tree_list: Option<ast::UseTreeList>, | 47 | use_tree_list: Option<ast::UseTreeList>, |