aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock16
-rw-r--r--crates/assists/src/handlers/merge_imports.rs14
-rw-r--r--crates/assists/src/handlers/replace_qualified_name_with_use.rs20
-rw-r--r--crates/assists/src/utils/insert_use.rs356
-rw-r--r--crates/hir_def/src/data.rs2
-rw-r--r--crates/hir_def/src/diagnostics.rs42
-rw-r--r--crates/hir_def/src/item_tree.rs7
-rw-r--r--crates/hir_def/src/item_tree/lower.rs9
-rw-r--r--crates/hir_def/src/item_tree/tests.rs6
-rw-r--r--crates/hir_def/src/nameres.rs93
-rw-r--r--crates/hir_def/src/nameres/collector.rs102
-rw-r--r--crates/hir_def/src/nameres/tests.rs1
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs131
-rw-r--r--crates/hir_def/src/nameres/tests/mod_resolution.rs36
-rw-r--r--crates/hir_def/src/test_db.rs42
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/hir_ty/src/display.rs9
-rw-r--r--crates/hir_ty/src/lib.rs11
-rw-r--r--crates/hir_ty/src/lower.rs10
-rw-r--r--crates/hir_ty/src/method_resolution.rs8
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs36
-rw-r--r--crates/hir_ty/src/traits/chalk.rs22
-rw-r--r--crates/hir_ty/src/traits/chalk/interner.rs1
-rw-r--r--crates/hir_ty/src/traits/chalk/mapping.rs69
-rw-r--r--crates/hir_ty/src/traits/chalk/tls.rs11
-rw-r--r--crates/ide/src/diagnostics.rs62
-rw-r--r--crates/project_model/src/project_json.rs8
-rw-r--r--crates/rust-analyzer/src/reload.rs18
-rw-r--r--crates/syntax/src/ast/edit.rs1
-rw-r--r--crates/syntax/src/ast/make.rs4
-rw-r--r--editors/code/src/commands.ts6
-rw-r--r--editors/code/src/lsp_ext.ts6
32 files changed, 928 insertions, 237 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f35d63df2..72ec68624 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -162,9 +162,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
162 162
163[[package]] 163[[package]]
164name = "chalk-derive" 164name = "chalk-derive"
165version = "0.25.0" 165version = "0.27.0"
166source = "registry+https://github.com/rust-lang/crates.io-index" 166source = "registry+https://github.com/rust-lang/crates.io-index"
167checksum = "624e14d3f029186e6ffd97081ffa082f98ddd5df20655b6f0e8efb83dd8ac8b4" 167checksum = "d5444ff2a211fe2a863e44d16a368c3d8a314d489de21b8eeb6879f14dd5d4a8"
168dependencies = [ 168dependencies = [
169 "proc-macro2", 169 "proc-macro2",
170 "quote", 170 "quote",
@@ -174,9 +174,9 @@ dependencies = [
174 174
175[[package]] 175[[package]]
176name = "chalk-ir" 176name = "chalk-ir"
177version = "0.25.0" 177version = "0.27.0"
178source = "registry+https://github.com/rust-lang/crates.io-index" 178source = "registry+https://github.com/rust-lang/crates.io-index"
179checksum = "118c68eccdda5604af50bbef84c94550f3854f76989cb03c36ffd36cc2ffe958" 179checksum = "e39c3db1dd4abfaa7658faaa62e5fe998a982a592b710bd971fad5b6adfcfdef"
180dependencies = [ 180dependencies = [
181 "chalk-derive", 181 "chalk-derive",
182 "lazy_static", 182 "lazy_static",
@@ -184,9 +184,9 @@ dependencies = [
184 184
185[[package]] 185[[package]]
186name = "chalk-recursive" 186name = "chalk-recursive"
187version = "0.25.0" 187version = "0.27.0"
188source = "registry+https://github.com/rust-lang/crates.io-index" 188source = "registry+https://github.com/rust-lang/crates.io-index"
189checksum = "5130de3065e3cdfd2ab6d7d70b02b917bafbc096f270c9a643c23da249053606" 189checksum = "3bfae328eff80ca54dcd0d731725bbb56136ac21c59261b68f1e5498e056b306"
190dependencies = [ 190dependencies = [
191 "chalk-derive", 191 "chalk-derive",
192 "chalk-ir", 192 "chalk-ir",
@@ -197,9 +197,9 @@ dependencies = [
197 197
198[[package]] 198[[package]]
199name = "chalk-solve" 199name = "chalk-solve"
200version = "0.25.0" 200version = "0.27.0"
201source = "registry+https://github.com/rust-lang/crates.io-index" 201source = "registry+https://github.com/rust-lang/crates.io-index"
202checksum = "45b235a1f568b28707f117b2d30eabbee9cbcfccaa0d6e9697300400c8ca0996" 202checksum = "a673abe3077adc25f8ee0894198aed494a5bb0ce50ee993900d0ee1a44e1948a"
203dependencies = [ 203dependencies = [
204 "chalk-derive", 204 "chalk-derive",
205 "chalk-ir", 205 "chalk-ir",
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;
95use std::fmt<|>::Display; 95use std::fmt<|>::Display;
96", 96",
97 r" 97 r"
98use std::fmt::{Display, Debug}; 98use std::fmt::{Debug, Display};
99", 99",
100 ); 100 );
101 } 101 }
@@ -122,7 +122,7 @@ use std::fmt::{self, Display};
122use std::{fmt, <|>fmt::Display}; 122use std::{fmt, <|>fmt::Display};
123", 123",
124 r" 124 r"
125use std::{fmt::{Display, self}}; 125use std::{fmt::{self, Display}};
126", 126",
127 ); 127 );
128 } 128 }
@@ -210,13 +210,17 @@ use std::{fmt<|>::Debug, fmt::Display};
210use std::{fmt::{Debug, Display}}; 210use 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"
216use std::{fmt::Debug, fmt<|>::Display}; 220use std::{fmt::Debug, fmt<|>::Display};
217", 221",
218 r" 222 r"
219use std::{fmt::{Display, Debug}}; 223use std::{fmt::{Debug, Display}};
220", 224",
221 ); 225 );
222 } 226 }
@@ -310,9 +314,7 @@ use foo::<|>{
310}; 314};
311", 315",
312 r" 316 r"
313use foo::{ 317use foo::{FooBar, bar::baz};
314 FooBar,
315bar::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"
315use std::fmt::{Debug, self}; 315use std::fmt::{self, Debug};
316 316
317impl fmt for Foo { 317impl fmt for Foo {
318} 318}
@@ -330,9 +330,8 @@ use std::fmt::{Debug, nested::{Display}};
330impl std::fmt::nested<|> for Foo { 330impl std::fmt::nested<|> for Foo {
331} 331}
332", 332",
333 // FIXME(veykril): should be nested::{self, Display} here
334 r" 333 r"
335use std::fmt::{Debug, nested::{Display}, nested}; 334use std::fmt::{Debug, nested::{self, Display}};
336 335
337impl nested for Foo { 336impl nested for Foo {
338} 337}
@@ -350,9 +349,8 @@ use std::fmt::{Debug, nested::{self, Display}};
350impl std::fmt::nested<|> for Foo { 349impl std::fmt::nested<|> for Foo {
351} 350}
352", 351",
353 // FIXME(veykril): nested is duplicated now
354 r" 352 r"
355use std::fmt::{Debug, nested::{self, Display}, nested}; 353use std::fmt::{Debug, nested::{self, Display}};
356 354
357impl nested for Foo { 355impl nested for Foo {
358} 356}
@@ -371,7 +369,7 @@ impl std::fmt::nested::Debug<|> for Foo {
371} 369}
372", 370",
373 r" 371 r"
374use std::fmt::{Debug, nested::{Display}, nested::Debug}; 372use std::fmt::{Debug, nested::{Debug, Display}};
375 373
376impl Debug for Foo { 374impl Debug for Foo {
377} 375}
@@ -409,7 +407,7 @@ impl std::fmt::Display<|> for Foo {
409} 407}
410", 408",
411 r" 409 r"
412use std::fmt::{nested::Debug, Display}; 410use std::fmt::{Display, nested::Debug};
413 411
414impl Display for Foo { 412impl Display for Foo {
415} 413}
@@ -429,12 +427,8 @@ use crate::{
429 427
430fn foo() { crate::ty::lower<|>::trait_env() } 428fn foo() { crate::ty::lower<|>::trait_env() }
431", 429",
432 // FIXME(veykril): formatting broke here
433 r" 430 r"
434use crate::{ 431use crate::{AssocItem, ty::{Substs, Ty, lower}};
435 ty::{Substs, Ty},
436 AssocItem,
437ty::lower};
438 432
439fn foo() { lower::trait_env() } 433fn foo() { lower::trait_env() }
440", 434",
@@ -633,7 +627,7 @@ fn main() {
633} 627}
634 ", 628 ",
635 r" 629 r"
636use std::fmt::{Display, self}; 630use std::fmt::{self, Display};
637 631
638fn main() { 632fn 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`.
2use std::iter::{self, successors}; 2use std::{
3 cmp::Ordering,
4 iter::{self, successors},
5};
3 6
4use algo::skip_trivia_token;
5use ast::{ 7use ast::{
6 edit::{AstNodeEdit, IndentLevel}, 8 edit::{AstNodeEdit, IndentLevel},
7 PathSegmentKind, VisibilityOwner, 9 PathSegmentKind, VisibilityOwner,
@@ -9,9 +11,8 @@ use ast::{
9use syntax::{ 11use 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};
14use test_utils::mark;
15 16
16#[derive(Debug)] 17#[derive(Debug)]
17pub enum ImportScope { 18pub 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
151pub(crate) fn try_merge_imports( 151pub(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
167fn 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
174pub(crate) fn try_merge_trees( 166pub(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() 181fn 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 293fn 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]
298fn first_segment(path: &ast::Path) -> Option<ast::PathSegment> {
299 first_path(path).segment()
300}
301
302fn first_path(path: &ast::Path) -> ast::Path {
303 successors(Some(path.clone()), ast::Path::qualifier).last().unwrap()
304}
305
306fn 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}}
316fn 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
337fn 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
282fn first_segment(path: &ast::Path) -> Option<ast::PathSegment> {
283 first_path(path).segment()
284}
285
286fn first_path(path: &ast::Path) -> ast::Path {
287 successors(Some(path.clone()), ast::Path::qualifier).last().unwrap()
288}
289
290fn 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)]
296enum AddBlankLine { 391enum 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};
674use std::io;", 778use 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;
699use 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)]
33pub struct UnresolvedExternCrate {
34 pub file: HirFileId,
35 pub item: AstPtr<ast::ExternCrate>,
36}
37
38impl 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)]
54pub struct UnresolvedImport {
55 pub file: HirFileId,
56 pub node: AstPtr<ast::UseTree>,
57}
58
59impl 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
297macro_rules! from_attrs { 296macro_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
289mod diagnostics { 289mod 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
6use base_db::{CrateId, FileId, ProcMacroId}; 6use base_db::{CrateId, FileId, ProcMacroId};
7use cfg::CfgOptions; 7use cfg::CfgOptions;
8use hir_expand::InFile;
8use hir_expand::{ 9use 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};
16use rustc_hash::FxHashMap; 17use rustc_hash::FxHashMap;
18use rustc_hash::FxHashSet;
17use syntax::ast; 19use syntax::ast;
18use test_utils::mark; 20use 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)]
115enum ImportSource {
116 Import(ItemTreeId<item_tree::Import>),
117 ExternCrate(ItemTreeId<item_tree::ExternCrate>),
118}
119
120#[derive(Clone, Debug, Eq, PartialEq)]
115struct Import { 121struct 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
125impl Import { 132impl 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;
2mod incremental; 2mod incremental;
3mod macros; 3mod macros;
4mod mod_resolution; 4mod mod_resolution;
5mod diagnostics;
5mod primitives; 6mod primitives;
6 7
7use std::sync::Arc; 8use 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 @@
1use base_db::fixture::WithFixture;
2use base_db::FileId;
3use base_db::SourceDatabaseExt;
4use hir_expand::db::AstDatabase;
5use rustc_hash::FxHashMap;
6use syntax::TextRange;
7use syntax::TextSize;
8
9use crate::test_db::TestDB;
10
11fn 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]
43fn 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]
56fn 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]
83fn 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]
96fn 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]
120fn 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]
675fn 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]
711fn module_resolution_decl_inside_module_in_non_crate_root_2() { 675fn 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
8use base_db::SourceDatabase;
8use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; 9use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
9use hir_expand::db::AstDatabase; 10use hir_expand::db::AstDatabase;
11use hir_expand::diagnostics::Diagnostic;
12use hir_expand::diagnostics::DiagnosticSinkBuilder;
13use rustc_hash::FxHashMap;
10use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15use syntax::TextRange;
16use test_utils::extract_annotations;
11 17
12use crate::db::DefDatabase; 18use 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"
17log = "0.4.8" 17log = "0.4.8"
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19scoped-tls = "1" 19scoped-tls = "1"
20chalk-solve = { version = "0.25.0" } 20chalk-solve = { version = "0.27.0" }
21chalk-ir = { version = "0.25.0" } 21chalk-ir = { version = "0.27.0" }
22chalk-recursive = { version = "0.25.0" } 22chalk-recursive = { version = "0.27.0" }
23 23
24stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
25hir_def = { path = "../hir_def", version = "0.0.0" } 25hir_def = { path = "../hir_def", version = "0.0.0" }
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index 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]
1056fn 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};
25use mapping::{ 25use 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
29pub use self::interner::*; 30pub 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
13pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>; 13pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
14pub type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>; 14pub type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>;
15pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>;
15pub type TraitId = chalk_ir::TraitId<Interner>; 16pub type TraitId = chalk_ir::TraitId<Interner>;
16pub type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>; 17pub type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>;
17pub type AdtId = chalk_ir::AdtId<Interner>; 18pub 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
491impl ToChalk for TypeAliasId { 507pub struct TypeAliasAsAssocType(pub TypeAliasId);
508
509impl 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
521pub struct TypeAliasAsForeignType(pub TypeAliasId);
522
523impl 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;
4use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication, TypeName}; 4use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication, TypeName};
5use itertools::Itertools; 5use itertools::Itertools;
6 6
7use super::{from_chalk, Interner}; 7use super::{from_chalk, Interner, TypeAliasAsAssocType};
8use crate::{db::HirDatabase, CallableDefId, TypeCtor}; 8use crate::{db::HirDatabase, CallableDefId, TypeCtor};
9use hir_def::{AdtId, AssocContainerId, DefWithBodyId, Lookup, TypeAliasId}; 9use 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#"
623use a; 623use a;
624use a::{c, d::e}; 624use a::{c, d::e};
625
626mod 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)]
14pub struct ProjectJson { 14pub 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 @@
2use std::{mem, sync::Arc}; 2use std::{mem, sync::Arc};
3 3
4use base_db::{CrateGraph, SourceRoot, VfsPath}; 4use base_db::{CrateGraph, SourceRoot, VfsPath};
5use flycheck::FlycheckHandle; 5use flycheck::{FlycheckConfig, FlycheckHandle};
6use ide::AnalysisChange; 6use ide::AnalysisChange;
7use project_model::{ProcMacroClient, ProjectWorkspace}; 7use project_model::{ProcMacroClient, ProjectWorkspace};
8use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; 8use 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
41pub fn glob_use_tree() -> ast::UseTree {
42 ast_from_text("use *;")
43}
44
41pub fn use_tree( 45pub 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>,
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 4321de244..e9581a9b5 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -21,7 +21,7 @@ export function analyzerStatus(ctx: Ctx): Cmd {
21 provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { 21 provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
22 if (!vscode.window.activeTextEditor) return ''; 22 if (!vscode.window.activeTextEditor) return '';
23 23
24 return ctx.client.sendRequest(ra.analyzerStatus, null); 24 return ctx.client.sendRequest(ra.analyzerStatus);
25 } 25 }
26 26
27 get onDidChange(): vscode.Event<vscode.Uri> { 27 get onDidChange(): vscode.Event<vscode.Uri> {
@@ -63,7 +63,7 @@ export function memoryUsage(ctx: Ctx): Cmd {
63 provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { 63 provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
64 if (!vscode.window.activeTextEditor) return ''; 64 if (!vscode.window.activeTextEditor) return '';
65 65
66 return ctx.client.sendRequest(ra.memoryUsage, null).then((mem: any) => { 66 return ctx.client.sendRequest(ra.memoryUsage).then((mem: any) => {
67 return 'Per-query memory usage:\n' + mem + '\n(note: database has been cleared)'; 67 return 'Per-query memory usage:\n' + mem + '\n(note: database has been cleared)';
68 }); 68 });
69 } 69 }
@@ -372,7 +372,7 @@ export function expandMacro(ctx: Ctx): Cmd {
372} 372}
373 373
374export function reloadWorkspace(ctx: Ctx): Cmd { 374export function reloadWorkspace(ctx: Ctx): Cmd {
375 return async () => ctx.client.sendRequest(ra.reloadWorkspace, null); 375 return async () => ctx.client.sendRequest(ra.reloadWorkspace);
376} 376}
377 377
378export function showReferences(ctx: Ctx): Cmd { 378export function showReferences(ctx: Ctx): Cmd {
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index f280bba3d..d167041c4 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -4,8 +4,8 @@
4 4
5import * as lc from "vscode-languageclient"; 5import * as lc from "vscode-languageclient";
6 6
7export const analyzerStatus = new lc.RequestType<null, string, void>("rust-analyzer/analyzerStatus"); 7export const analyzerStatus = new lc.RequestType0<string, void>("rust-analyzer/analyzerStatus");
8export const memoryUsage = new lc.RequestType<null, string, void>("rust-analyzer/memoryUsage"); 8export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage");
9 9
10export type Status = "loading" | "ready" | "invalid" | "needsReload"; 10export type Status = "loading" | "ready" | "invalid" | "needsReload";
11export interface StatusParams { 11export interface StatusParams {
@@ -13,7 +13,7 @@ export interface StatusParams {
13} 13}
14export const status = new lc.NotificationType<StatusParams>("rust-analyzer/status"); 14export const status = new lc.NotificationType<StatusParams>("rust-analyzer/status");
15 15
16export const reloadWorkspace = new lc.RequestType<null, null, void>("rust-analyzer/reloadWorkspace"); 16export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace");
17 17
18export interface SyntaxTreeParams { 18export interface SyntaxTreeParams {
19 textDocument: lc.TextDocumentIdentifier; 19 textDocument: lc.TextDocumentIdentifier;