diff --git a/.gitattributes b/.gitattributes index a77342d72447..183e9b521184 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ -crates/ra_syntax/tests/data/** -text +crates/ra_syntax/test_data/** -text eof=LF +crates/ra_ide_api/src/snapshots/** -text eof=LF diff --git a/Cargo.lock b/Cargo.lock index 10f51403a380..3879204d9e7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -126,7 +126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "chalk-derive" version = "0.1.0" -source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809" +source = "git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05#50f9f636123bd88d0cc1b958749981d6702e4d05" dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -136,9 +136,9 @@ dependencies = [ [[package]] name = "chalk-engine" version = "0.9.0" -source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809" +source = "git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05#50f9f636123bd88d0cc1b958749981d6702e4d05" dependencies = [ - "chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", + "chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "stacker 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -146,18 +146,18 @@ dependencies = [ [[package]] name = "chalk-ir" version = "0.1.0" -source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809" +source = "git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05#50f9f636123bd88d0cc1b958749981d6702e4d05" dependencies = [ - "chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", - "chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", - "chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", + "chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", + "chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", + "chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", "lalrpop-intern 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "chalk-macros" version = "0.1.1" -source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809" +source = "git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05#50f9f636123bd88d0cc1b958749981d6702e4d05" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -165,23 +165,23 @@ dependencies = [ [[package]] name = "chalk-rust-ir" version = "0.1.0" -source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809" +source = "git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05#50f9f636123bd88d0cc1b958749981d6702e4d05" dependencies = [ - "chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", - "chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", - "chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", - "chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", + "chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", + "chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", + "chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", + "chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", ] [[package]] name = "chalk-solve" version = "0.1.0" -source = "git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809#8314f2fcec8582a58c24b638f1a259d4145a0809" +source = "git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05#50f9f636123bd88d0cc1b958749981d6702e4d05" dependencies = [ - "chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", - "chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", - "chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", - "chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", + "chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", + "chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", + "chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", + "chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", "ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -985,6 +985,7 @@ dependencies = [ "relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "salsa 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", + "test_utils 0.1.0", ] [[package]] @@ -1000,9 +1001,9 @@ name = "ra_hir" version = "0.1.0" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", - "chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", - "chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)", + "chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", + "chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", + "chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)", "ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", "insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "lalrpop-intern 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1018,7 +1019,6 @@ dependencies = [ "ra_prof 0.1.0", "ra_syntax 0.1.0", "ra_tt 0.1.0", - "relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "test_utils 0.1.0", ] @@ -1027,6 +1027,7 @@ dependencies = [ name = "ra_hir_def" version = "0.1.0" dependencies = [ + "insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "ra_arena 0.1.0", @@ -1037,7 +1038,6 @@ dependencies = [ "ra_prof 0.1.0", "ra_syntax 0.1.0", "ra_tt 0.1.0", - "relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "test_utils 0.1.0", ] @@ -1077,7 +1077,6 @@ dependencies = [ "ra_text_edit 0.1.0", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "test_utils 0.1.0", @@ -1849,12 +1848,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum cargo_metadata 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8d2d1617e838936c0d2323a65cc151e03ae19a7678dd24f72bccf27119b90a5d" "checksum cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)" = "0213d356d3c4ea2c18c40b037c3be23cd639825c18f25ee670ac7813beeef99c" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "" -"checksum chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "" -"checksum chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "" -"checksum chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "" -"checksum chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "" -"checksum chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=8314f2fcec8582a58c24b638f1a259d4145a0809)" = "" +"checksum chalk-derive 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "" +"checksum chalk-engine 0.9.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "" +"checksum chalk-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "" +"checksum chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "" +"checksum chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "" +"checksum chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=50f9f636123bd88d0cc1b958749981d6702e4d05)" = "" "checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" "checksum clicolors-control 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90082ee5dcdd64dc4e9e0d37fbf3ee325419e39c0092191e0393df65518f741e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" diff --git a/crates/ra_assists/src/assists/early_return.rs b/crates/ra_assists/src/assists/early_return.rs index ad6c5695a3a4..570a07a20c56 100644 --- a/crates/ra_assists/src/assists/early_return.rs +++ b/crates/ra_assists/src/assists/early_return.rs @@ -3,9 +3,10 @@ use std::ops::RangeInclusive; use hir::db::HirDatabase; use ra_syntax::{ algo::replace_children, - ast::{self, edit::IndentLevel, make}, + ast::{self, edit::IndentLevel, make, Block, Pat::TupleStructPat}, AstNode, SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE}, + SyntaxNode, }; use crate::{ @@ -37,7 +38,23 @@ use crate::{ // ``` pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option { let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; - let expr = if_expr.condition()?.expr()?; + let cond = if_expr.condition()?; + let mut if_let_ident: Option = None; + + // Check if there is an IfLet that we can handle. + match cond.pat() { + None => {} // No IfLet, supported. + Some(TupleStructPat(ref pat)) if pat.args().count() == 1usize => match &pat.path() { + Some(p) => match p.qualifier() { + None => if_let_ident = Some(p.syntax().text().to_string()), + _ => return None, + }, + _ => return None, + }, + _ => return None, // Unsupported IfLet. + }; + + let expr = cond.expr()?; let then_block = if_expr.then_branch()?.block()?; if if_expr.else_branch().is_some() { return None; @@ -63,8 +80,8 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Opt let parent_container = parent_block.syntax().parent()?.parent()?; let early_expression = match parent_container.kind() { - WHILE_EXPR | LOOP_EXPR => Some("continue;"), - FN_DEF => Some("return;"), + WHILE_EXPR | LOOP_EXPR => Some("continue"), + FN_DEF => Some("return"), _ => None, }?; @@ -77,34 +94,58 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Opt ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| { let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); - let new_if_expr = - if_indent_level.increase_indent(make::if_expression(&expr, early_expression)); - let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); - let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); - let end_of_then = - if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { - end_of_then.prev_sibling_or_token().unwrap() - } else { - end_of_then - }; - let mut new_if_and_then_statements = new_if_expr.syntax().children_with_tokens().chain( - then_block_items - .syntax() - .children_with_tokens() - .skip(1) - .take_while(|i| *i != end_of_then), - ); - let new_block = replace_children( - &parent_block.syntax(), - RangeInclusive::new( - if_expr.clone().syntax().clone().into(), - if_expr.syntax().clone().into(), - ), - &mut new_if_and_then_statements, - ); + let new_block = match if_let_ident { + None => { + // If. + let early_expression = &(early_expression.to_owned() + ";"); + let new_expr = + if_indent_level.increase_indent(make::if_expression(&expr, early_expression)); + replace(new_expr, &then_block, &parent_block, &if_expr) + } + Some(if_let_ident) => { + // If-let. + let new_expr = if_indent_level.increase_indent(make::let_match_early( + expr, + &if_let_ident, + early_expression, + )); + replace(new_expr, &then_block, &parent_block, &if_expr) + } + }; edit.target(if_expr.syntax().text_range()); edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap()); edit.set_cursor(cursor_position); + + fn replace( + new_expr: impl AstNode, + then_block: &Block, + parent_block: &Block, + if_expr: &ast::IfExpr, + ) -> SyntaxNode { + let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); + let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); + let end_of_then = + if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { + end_of_then.prev_sibling_or_token().unwrap() + } else { + end_of_then + }; + let mut then_statements = new_expr.syntax().children_with_tokens().chain( + then_block_items + .syntax() + .children_with_tokens() + .skip(1) + .take_while(|i| *i != end_of_then), + ); + replace_children( + &parent_block.syntax(), + RangeInclusive::new( + if_expr.clone().syntax().clone().into(), + if_expr.syntax().clone().into(), + ), + &mut then_statements, + ) + } }) } @@ -143,6 +184,68 @@ mod tests { ); } + #[test] + fn convert_let_inside_fn() { + check_assist( + convert_to_guarded_return, + r#" + fn main(n: Option) { + bar(); + if<|> let Some(n) = n { + foo(n); + + //comment + bar(); + } + } + "#, + r#" + fn main(n: Option) { + bar(); + le<|>t n = match n { + Some(it) => it, + None => return, + }; + foo(n); + + //comment + bar(); + } + "#, + ); + } + + #[test] + fn convert_let_ok_inside_fn() { + check_assist( + convert_to_guarded_return, + r#" + fn main(n: Option) { + bar(); + if<|> let Ok(n) = n { + foo(n); + + //comment + bar(); + } + } + "#, + r#" + fn main(n: Option) { + bar(); + le<|>t n = match n { + Ok(it) => it, + None => return, + }; + foo(n); + + //comment + bar(); + } + "#, + ); + } + #[test] fn convert_inside_while() { check_assist( @@ -171,6 +274,35 @@ mod tests { ); } + #[test] + fn convert_let_inside_while() { + check_assist( + convert_to_guarded_return, + r#" + fn main() { + while true { + if<|> let Some(n) = n { + foo(n); + bar(); + } + } + } + "#, + r#" + fn main() { + while true { + le<|>t n = match n { + Some(it) => it, + None => continue, + }; + foo(n); + bar(); + } + } + "#, + ); + } + #[test] fn convert_inside_loop() { check_assist( @@ -199,6 +331,35 @@ mod tests { ); } + #[test] + fn convert_let_inside_loop() { + check_assist( + convert_to_guarded_return, + r#" + fn main() { + loop { + if<|> let Some(n) = n { + foo(n); + bar(); + } + } + } + "#, + r#" + fn main() { + loop { + le<|>t n = match n { + Some(it) => it, + None => continue, + }; + foo(n); + bar(); + } + } + "#, + ); + } + #[test] fn ignore_already_converted_if() { check_assist_not_applicable( diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index 3394ae8ce6c3..bf1f7920c583 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml @@ -12,3 +12,4 @@ rustc-hash = "1.0" ra_syntax = { path = "../ra_syntax" } ra_cfg = { path = "../ra_cfg" } ra_prof = { path = "../ra_prof" } +test_utils = { path = "../test_utils" } diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs new file mode 100644 index 000000000000..f5dd59f84011 --- /dev/null +++ b/crates/ra_db/src/fixture.rs @@ -0,0 +1,186 @@ +//! FIXME: write short doc here + +use std::sync::Arc; + +use ra_cfg::CfgOptions; +use rustc_hash::FxHashMap; +use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; + +use crate::{ + CrateGraph, Edition, FileId, FilePosition, RelativePathBuf, SourceDatabaseExt, SourceRoot, + SourceRootId, +}; + +pub const WORKSPACE: SourceRootId = SourceRootId(0); + +pub trait WithFixture: Default + SourceDatabaseExt + 'static { + fn with_single_file(text: &str) -> (Self, FileId) { + let mut db = Self::default(); + let file_id = with_single_file(&mut db, text); + (db, file_id) + } + + fn with_files(fixture: &str) -> Self { + let mut db = Self::default(); + let pos = with_files(&mut db, fixture); + assert!(pos.is_none()); + db + } + + fn with_position(fixture: &str) -> (Self, FilePosition) { + let mut db = Self::default(); + let pos = with_files(&mut db, fixture); + (db, pos.unwrap()) + } +} + +impl WithFixture for DB {} + +fn with_single_file(db: &mut dyn SourceDatabaseExt, text: &str) -> FileId { + let file_id = FileId(0); + let rel_path: RelativePathBuf = "/main.rs".into(); + + let mut source_root = SourceRoot::default(); + source_root.insert_file(rel_path.clone(), file_id); + + let mut crate_graph = CrateGraph::default(); + crate_graph.add_crate_root(file_id, Edition::Edition2018, CfgOptions::default()); + + db.set_file_text(file_id, Arc::new(text.to_string())); + db.set_file_relative_path(file_id, rel_path); + db.set_file_source_root(file_id, WORKSPACE); + db.set_source_root(WORKSPACE, Arc::new(source_root)); + db.set_crate_graph(Arc::new(crate_graph)); + + file_id +} + +fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option { + let fixture = parse_fixture(fixture); + + let mut crate_graph = CrateGraph::default(); + let mut crates = FxHashMap::default(); + let mut crate_deps = Vec::new(); + let mut default_crate_root: Option = None; + + let mut source_root = SourceRoot::default(); + let mut source_root_id = WORKSPACE; + let mut source_root_prefix: RelativePathBuf = "/".into(); + let mut file_id = FileId(0); + + let mut file_position = None; + + for entry in fixture.iter() { + let meta = match parse_meta(&entry.meta) { + ParsedMeta::Root { path } => { + let source_root = std::mem::replace(&mut source_root, SourceRoot::default()); + db.set_source_root(source_root_id, Arc::new(source_root)); + source_root_id.0 += 1; + source_root_prefix = path; + continue; + } + ParsedMeta::File(it) => it, + }; + assert!(meta.path.starts_with(&source_root_prefix)); + + if let Some(krate) = meta.krate { + let crate_id = crate_graph.add_crate_root(file_id, meta.edition, meta.cfg); + let prev = crates.insert(krate.clone(), crate_id); + assert!(prev.is_none()); + for dep in meta.deps { + crate_deps.push((krate.clone(), dep)) + } + } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { + assert!(default_crate_root.is_none()); + default_crate_root = Some(file_id); + } + + let text = if entry.text.contains(CURSOR_MARKER) { + let (offset, text) = extract_offset(&entry.text); + assert!(file_position.is_none()); + file_position = Some(FilePosition { file_id, offset }); + text.to_string() + } else { + entry.text.to_string() + }; + + db.set_file_text(file_id, Arc::new(text)); + db.set_file_relative_path(file_id, meta.path.clone()); + db.set_file_source_root(file_id, source_root_id); + source_root.insert_file(meta.path, file_id); + + file_id.0 += 1; + } + + if crates.is_empty() { + let crate_root = default_crate_root.unwrap(); + crate_graph.add_crate_root(crate_root, Edition::Edition2018, CfgOptions::default()); + } else { + for (from, to) in crate_deps { + let from_id = crates[&from]; + let to_id = crates[&to]; + crate_graph.add_dep(from_id, to.into(), to_id).unwrap(); + } + } + + db.set_source_root(source_root_id, Arc::new(source_root)); + db.set_crate_graph(Arc::new(crate_graph)); + + file_position +} + +enum ParsedMeta { + Root { path: RelativePathBuf }, + File(FileMeta), +} + +struct FileMeta { + path: RelativePathBuf, + krate: Option, + deps: Vec, + cfg: CfgOptions, + edition: Edition, +} + +//- /lib.rs crate:foo deps:bar,baz +fn parse_meta(meta: &str) -> ParsedMeta { + let components = meta.split_ascii_whitespace().collect::>(); + + if components[0] == "root" { + let path: RelativePathBuf = components[1].into(); + assert!(path.starts_with("/") && path.ends_with("/")); + return ParsedMeta::Root { path }; + } + + let path: RelativePathBuf = components[0].into(); + assert!(path.starts_with("/")); + + let mut krate = None; + let mut deps = Vec::new(); + let mut edition = Edition::Edition2018; + let mut cfg = CfgOptions::default(); + for component in components[1..].iter() { + let (key, value) = split1(component, ':').unwrap(); + match key { + "crate" => krate = Some(value.to_string()), + "deps" => deps = value.split(',').map(|it| it.to_string()).collect(), + "edition" => edition = Edition::from_string(&value), + "cfg" => { + for key in value.split(',') { + match split1(key, '=') { + None => cfg.insert_atom(key.into()), + Some((k, v)) => cfg.insert_key_value(k.into(), v.into()), + } + } + } + _ => panic!("bad component: {:?}", component), + } + } + + ParsedMeta::File(FileMeta { path, krate, deps, edition, cfg }) +} + +fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { + let idx = haystack.find(delim)?; + Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..])) +} diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index eafa95921cd6..60f7dc881565 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs @@ -6,13 +6,14 @@ //! actual IO. See `vfs` and `project_model` in the `ra_lsp_server` crate for how //! actual IO is done and lowered to input. -use relative_path::{RelativePath, RelativePathBuf}; use rustc_hash::FxHashMap; use ra_cfg::CfgOptions; use ra_syntax::SmolStr; use rustc_hash::FxHashSet; +use crate::{RelativePath, RelativePathBuf}; + /// `FileId` is an integer which uniquely identifies a file. File paths are /// messy and system-dependent, so most of the code should work directly with /// `FileId`, without inspecting the path. The mapping between `FileId` and path @@ -97,6 +98,7 @@ pub enum Edition { } impl Edition { + //FIXME: replace with FromStr with proper error handling pub fn from_string(s: &str) -> Edition { match s { "2015" => Edition::Edition2015, diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index 0d1ab48438d0..b6bfd531de93 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -1,17 +1,18 @@ //! ra_db defines basic database traits. The concrete DB is defined by ra_ide_api. mod cancellation; mod input; +pub mod fixture; use std::{panic, sync::Arc}; use ra_prof::profile; use ra_syntax::{ast, Parse, SourceFile, TextRange, TextUnit}; -use relative_path::{RelativePath, RelativePathBuf}; pub use crate::{ cancellation::Canceled, input::{CrateGraph, CrateId, Dependency, Edition, FileId, SourceRoot, SourceRootId}, }; +pub use relative_path::{RelativePath, RelativePathBuf}; pub use salsa; pub trait CheckCanceled { diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml index 5df371bc0f97..324961328487 100644 --- a/crates/ra_hir/Cargo.toml +++ b/crates/ra_hir/Cargo.toml @@ -7,7 +7,6 @@ authors = ["rust-analyzer developers"] [dependencies] arrayvec = "0.5.1" log = "0.4.5" -relative-path = "1.0.0" rustc-hash = "1.0" parking_lot = "0.9.0" ena = "0.13" @@ -24,9 +23,9 @@ hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } test_utils = { path = "../test_utils" } ra_prof = { path = "../ra_prof" } -chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "8314f2fcec8582a58c24b638f1a259d4145a0809" } -chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "8314f2fcec8582a58c24b638f1a259d4145a0809" } -chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "8314f2fcec8582a58c24b638f1a259d4145a0809" } +chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "50f9f636123bd88d0cc1b958749981d6702e4d05" } +chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "50f9f636123bd88d0cc1b958749981d6702e4d05" } +chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "50f9f636123bd88d0cc1b958749981d6702e4d05" } lalrpop-intern = "0.15.1" [dev-dependencies] diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index c97ea18a24ea..181c5d47afeb 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -9,16 +9,18 @@ use hir_def::{ adt::VariantData, builtin_type::BuiltinType, type_ref::{Mutability, TypeRef}, - CrateModuleId, LocalEnumVariantId, LocalStructFieldId, ModuleId, + CrateModuleId, LocalEnumVariantId, LocalStructFieldId, ModuleId, UnionId, +}; +use hir_expand::{ + diagnostics::DiagnosticSink, + name::{self, AsName}, }; -use hir_expand::name::{self, AsName}; use ra_db::{CrateId, Edition}; use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; use crate::{ adt::VariantDef, db::{AstDatabase, DefDatabase, HirDatabase}, - diagnostics::DiagnosticSink, expr::{validation::ExprValidator, Body, BodySourceMap}, generics::HasGenericParams, ids::{ @@ -26,11 +28,10 @@ use crate::{ TypeAliasId, }, impl_block::ImplBlock, - nameres::{ImportId, ModuleScope, Namespace}, resolve::{Resolver, Scope, TypeNs}, traits::TraitData, ty::{InferenceResult, TraitRef}, - Either, HasSource, Name, Ty, + Either, HasSource, Name, ScopeDef, Ty, {ImportId, Namespace}, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -64,7 +65,7 @@ impl Crate { } pub fn root_module(self, db: &impl DefDatabase) -> Option { - let module_id = db.crate_def_map(self).root(); + let module_id = db.crate_def_map(self.crate_id).root(); Some(Module::new(self, module_id)) } @@ -118,7 +119,7 @@ impl Module { /// Name of this module. pub fn name(self, db: &impl DefDatabase) -> Option { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); let parent = def_map[self.id.module_id].parent?; def_map[parent].children.iter().find_map(|(name, module_id)| { if *module_id == self.id.module_id { @@ -149,20 +150,20 @@ impl Module { /// might be missing `krate`. This can happen if a module's file is not included /// in the module tree of any target in `Cargo.toml`. pub fn crate_root(self, db: &impl DefDatabase) -> Module { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); self.with_module_id(def_map.root()) } /// Finds a child module with the specified name. pub fn child(self, db: &impl HirDatabase, name: &Name) -> Option { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); let child_id = def_map[self.id.module_id].children.get(name)?; Some(self.with_module_id(*child_id)) } /// Iterates over all child modules. pub fn children(self, db: &impl DefDatabase) -> impl Iterator { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); let children = def_map[self.id.module_id] .children .iter() @@ -173,7 +174,7 @@ impl Module { /// Finds a parent module. pub fn parent(self, db: &impl DefDatabase) -> Option { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); let parent_id = def_map[self.id.module_id].parent?; Some(self.with_module_id(parent_id)) } @@ -189,12 +190,16 @@ impl Module { } /// Returns a `ModuleScope`: a set of items, visible in this module. - pub fn scope(self, db: &impl HirDatabase) -> ModuleScope { - db.crate_def_map(self.krate())[self.id.module_id].scope.clone() + pub fn scope(self, db: &impl HirDatabase) -> Vec<(Name, ScopeDef, Option)> { + db.crate_def_map(self.id.krate)[self.id.module_id] + .scope + .entries() + .map(|(name, res)| (name.clone(), res.def.into(), res.import)) + .collect() } pub fn diagnostics(self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { - db.crate_def_map(self.krate()).add_diagnostics(db, self.id.module_id, sink); + db.crate_def_map(self.id.krate).add_diagnostics(db, self.id.module_id, sink); for decl in self.declarations(db) { match decl { crate::ModuleDef::Function(f) => f.diagnostics(db, sink), @@ -218,12 +223,12 @@ impl Module { } pub(crate) fn resolver(self, db: &impl DefDatabase) -> Resolver { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); Resolver::default().push_module_scope(def_map, self.id.module_id) } pub fn declarations(self, db: &impl DefDatabase) -> Vec { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); def_map[self.id.module_id] .scope .entries() @@ -231,6 +236,7 @@ impl Module { .flat_map(|per_ns| { per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter()) }) + .map(ModuleDef::from) .collect() } @@ -334,12 +340,12 @@ impl Struct { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Union { - pub(crate) id: StructId, + pub(crate) id: UnionId, } impl Union { pub fn name(self, db: &impl DefDatabase) -> Option { - db.struct_data(self.id).name.clone() + db.union_data(self.id).name.clone() } pub fn module(self, db: &impl HirDatabase) -> Module { diff --git a/crates/ra_hir/src/code_model/src.rs b/crates/ra_hir/src/code_model/src.rs index 0f4c78df760d..6d116ee75c90 100644 --- a/crates/ra_hir/src/code_model/src.rs +++ b/crates/ra_hir/src/code_model/src.rs @@ -10,7 +10,7 @@ use crate::{ ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union, }; -pub use hir_def::Source; +pub use hir_expand::Source; pub trait HasSource { type Ast; @@ -22,7 +22,7 @@ pub trait HasSource { impl Module { /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items. pub fn definition_source(self, db: &(impl DefDatabase + AstDatabase)) -> Source { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); let decl_id = def_map[self.id.module_id].declaration; let file_id = def_map[self.id.module_id].definition; let ast = ModuleSource::new(db, file_id, decl_id); @@ -36,7 +36,7 @@ impl Module { self, db: &(impl DefDatabase + AstDatabase), ) -> Option> { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); let decl = def_map[self.id.module_id].declaration?; let ast = decl.to_node(db); Some(Source { file_id: decl.file_id(), ast }) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 89ca4e39f0af..eb66325f7f12 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -11,20 +11,19 @@ use crate::{ ids, impl_block::{ImplBlock, ImplSourceMap, ModuleImplBlocks}, lang_item::{LangItemTarget, LangItems}, - nameres::{CrateDefMap, Namespace}, traits::TraitData, ty::{ method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate, InferenceResult, Substs, Ty, TypableDef, TypeCtor, }, type_alias::TypeAliasData, - Const, ConstData, Crate, DefWithBody, ExprScopes, FnData, Function, Module, Static, + Const, ConstData, Crate, DefWithBody, ExprScopes, FnData, Function, Module, Namespace, Static, StructField, Trait, TypeAlias, }; pub use hir_def::db::{ - DefDatabase2, DefDatabase2Storage, EnumDataQuery, InternDatabase, InternDatabaseStorage, - RawItemsQuery, RawItemsWithSourceMapQuery, StructDataQuery, + CrateDefMapQuery, DefDatabase2, DefDatabase2Storage, EnumDataQuery, InternDatabase, + InternDatabaseStorage, RawItemsQuery, RawItemsWithSourceMapQuery, StructDataQuery, }; pub use hir_expand::db::{ AstDatabase, AstDatabaseStorage, AstIdMapQuery, MacroArgQuery, MacroDefQuery, MacroExpandQuery, @@ -41,9 +40,6 @@ pub trait DefDatabase: HirDebugDatabase + DefDatabase2 { #[salsa::invoke(crate::traits::TraitItemsIndex::trait_items_index)] fn trait_items_index(&self, module: Module) -> crate::traits::TraitItemsIndex; - #[salsa::invoke(CrateDefMap::crate_def_map_query)] - fn crate_def_map(&self, krate: Crate) -> Arc; - #[salsa::invoke(ModuleImplBlocks::impls_in_module_with_source_map_query)] fn impls_in_module_with_source_map( &self, diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index 9acdaf8ed3f4..1751e7be33a2 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs @@ -1,82 +1,13 @@ //! FIXME: write short doc here -use std::{any::Any, fmt}; +use std::any::Any; -use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, TextRange}; -use relative_path::RelativePathBuf; +use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; -use crate::{db::HirDatabase, HirFileId, Name, Source}; +use crate::{db::AstDatabase, HirFileId, Name, Source}; -/// Diagnostic defines hir API for errors and warnings. -/// -/// It is used as a `dyn` object, which you can downcast to a concrete -/// diagnostic. DiagnosticSink are structured, meaning that they include rich -/// information which can be used by IDE to create fixes. DiagnosticSink are -/// expressed in terms of macro-expanded syntax tree nodes (so, it's a bad idea -/// to diagnostic in a salsa value). -/// -/// Internally, various subsystems of hir produce diagnostics specific to a -/// subsystem (typically, an `enum`), which are safe to store in salsa but do not -/// include source locations. Such internal diagnostic are transformed into an -/// instance of `Diagnostic` on demand. -pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { - fn message(&self) -> String; - fn source(&self) -> Source; - fn highlight_range(&self) -> TextRange { - self.source().ast.range() - } - fn as_any(&self) -> &(dyn Any + Send + 'static); -} - -pub trait AstDiagnostic { - type AST; - fn ast(&self, db: &impl HirDatabase) -> Self::AST; -} - -impl dyn Diagnostic { - pub fn syntax_node(&self, db: &impl HirDatabase) -> SyntaxNode { - let node = db.parse_or_expand(self.source().file_id).unwrap(); - self.source().ast.to_node(&node) - } - - pub fn downcast_ref(&self) -> Option<&D> { - self.as_any().downcast_ref() - } -} - -pub struct DiagnosticSink<'a> { - callbacks: Vec Result<(), ()> + 'a>>, - default_callback: Box, -} - -impl<'a> DiagnosticSink<'a> { - pub fn new(cb: impl FnMut(&dyn Diagnostic) + 'a) -> DiagnosticSink<'a> { - DiagnosticSink { callbacks: Vec::new(), default_callback: Box::new(cb) } - } - - pub fn on(mut self, mut cb: F) -> DiagnosticSink<'a> { - let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::() { - Some(d) => { - cb(d); - Ok(()) - } - None => Err(()), - }; - self.callbacks.push(Box::new(cb)); - self - } - - pub(crate) fn push(&mut self, d: impl Diagnostic) { - let d: &dyn Diagnostic = &d; - for cb in self.callbacks.iter_mut() { - match cb(d) { - Ok(()) => return, - Err(()) => (), - } - } - (self.default_callback)(d) - } -} +pub use hir_def::diagnostics::UnresolvedModule; +pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; #[derive(Debug)] pub struct NoSuchField { @@ -98,25 +29,6 @@ impl Diagnostic for NoSuchField { } } -#[derive(Debug)] -pub struct UnresolvedModule { - pub file: HirFileId, - pub decl: AstPtr, - pub candidate: RelativePathBuf, -} - -impl Diagnostic for UnresolvedModule { - fn message(&self) -> String { - "unresolved module".to_string() - } - fn source(&self) -> Source { - Source { file_id: self.file, ast: self.decl.into() } - } - fn as_any(&self) -> &(dyn Any + Send + 'static) { - self - } -} - #[derive(Debug)] pub struct MissingFields { pub file: HirFileId, @@ -139,7 +51,7 @@ impl Diagnostic for MissingFields { impl AstDiagnostic for MissingFields { type AST = ast::RecordFieldList; - fn ast(&self, db: &impl HirDatabase) -> Self::AST { + fn ast(&self, db: &impl AstDatabase) -> Self::AST { let root = db.parse_or_expand(self.source().file_id).unwrap(); let node = self.source().ast.to_node(&root); ast::RecordFieldList::cast(node).unwrap() @@ -167,7 +79,7 @@ impl Diagnostic for MissingOkInTailExpr { impl AstDiagnostic for MissingOkInTailExpr { type AST = ast::Expr; - fn ast(&self, db: &impl HirDatabase) -> Self::AST { + fn ast(&self, db: &impl AstDatabase) -> Self::AST { let root = db.parse_or_expand(self.file).unwrap(); let node = self.source().ast.to_node(&root); ast::Expr::cast(node).unwrap() diff --git a/crates/ra_hir/src/expr/validation.rs b/crates/ra_hir/src/expr/validation.rs index c685edda193f..3054f1dcedf4 100644 --- a/crates/ra_hir/src/expr/validation.rs +++ b/crates/ra_hir/src/expr/validation.rs @@ -3,12 +3,13 @@ use std::sync::Arc; use hir_def::path::known; +use hir_expand::diagnostics::DiagnosticSink; use ra_syntax::ast; use rustc_hash::FxHashSet; use crate::{ db::HirDatabase, - diagnostics::{DiagnosticSink, MissingFields, MissingOkInTailExpr}, + diagnostics::{MissingFields, MissingOkInTailExpr}, expr::AstPtr, ty::{ApplicationTy, InferenceResult, Ty, TypeCtor}, Adt, Function, Name, Path, diff --git a/crates/ra_hir/src/from_id.rs b/crates/ra_hir/src/from_id.rs new file mode 100644 index 000000000000..089dbc9084a4 --- /dev/null +++ b/crates/ra_hir/src/from_id.rs @@ -0,0 +1,63 @@ +//! Utility module for converting between hir_def ids and code_model wrappers. +//! +//! It's unclear if we need this long-term, but it's definitelly useful while we +//! are splitting the hir. + +use hir_def::{AdtId, EnumVariantId, ModuleDefId}; + +use crate::{Adt, EnumVariant, ModuleDef}; + +macro_rules! from_id { + ($(($id:path, $ty:path)),*) => {$( + impl From<$id> for $ty { + fn from(id: $id) -> $ty { + $ty { id } + } + } + )*} +} + +from_id![ + (hir_def::ModuleId, crate::Module), + (hir_def::StructId, crate::Struct), + (hir_def::UnionId, crate::Union), + (hir_def::EnumId, crate::Enum), + (hir_def::TypeAliasId, crate::TypeAlias), + (hir_def::TraitId, crate::Trait), + (hir_def::StaticId, crate::Static), + (hir_def::ConstId, crate::Const), + (hir_def::FunctionId, crate::Function), + (hir_expand::MacroDefId, crate::MacroDef) +]; + +impl From for Adt { + fn from(id: AdtId) -> Self { + match id { + AdtId::StructId(it) => Adt::Struct(it.into()), + AdtId::UnionId(it) => Adt::Union(it.into()), + AdtId::EnumId(it) => Adt::Enum(it.into()), + } + } +} + +impl From for EnumVariant { + fn from(id: EnumVariantId) -> Self { + EnumVariant { parent: id.parent.into(), id: id.local_id } + } +} + +impl From for ModuleDef { + fn from(id: ModuleDefId) -> Self { + match id { + ModuleDefId::ModuleId(it) => ModuleDef::Module(it.into()), + ModuleDefId::FunctionId(it) => ModuleDef::Function(it.into()), + ModuleDefId::AdtId(it) => ModuleDef::Adt(it.into()), + ModuleDefId::EnumVariantId(it) => ModuleDef::EnumVariant(it.into()), + ModuleDefId::ConstId(it) => ModuleDef::Const(it.into()), + ModuleDefId::StaticId(it) => ModuleDef::Static(it.into()), + ModuleDefId::TraitId(it) => ModuleDef::Trait(it.into()), + ModuleDefId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), + ModuleDefId::BuiltinType(it) => ModuleDef::BuiltinType(it), + } + } +} diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs index a9de0145538b..9899bdbbce26 100644 --- a/crates/ra_hir/src/from_source.rs +++ b/crates/ra_hir/src/from_source.rs @@ -149,14 +149,20 @@ impl Module { ModuleSource::SourceFile(_) => None, }; - db.relevant_crates(src.file_id.original_file(db)) - .iter() - .map(|&crate_id| Crate { crate_id }) - .find_map(|krate| { - let def_map = db.crate_def_map(krate); - let module_id = def_map.find_module_by_source(src.file_id, decl_id)?; - Some(Module::new(krate, module_id)) - }) + db.relevant_crates(src.file_id.original_file(db)).iter().find_map(|&crate_id| { + let def_map = db.crate_def_map(crate_id); + + let (module_id, _module_data) = + def_map.modules.iter().find(|(_module_id, module_data)| { + if decl_id.is_some() { + module_data.declaration == decl_id + } else { + module_data.definition.map(|it| it.into()) == Some(src.file_id) + } + })?; + + Some(Module::new(Crate { crate_id }, module_id)) + }) } } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 40f5562b406a..3ba99d92d703 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -34,7 +34,6 @@ pub mod mock; pub mod source_binder; mod ids; -mod nameres; mod adt; mod traits; mod type_alias; @@ -47,6 +46,7 @@ mod resolve; pub mod diagnostics; mod util; +mod from_id; mod code_model; pub mod from_source; @@ -62,7 +62,7 @@ pub use crate::{ adt::VariantDef, code_model::{ docs::{DocDef, Docs, Documentation}, - src::{HasBodySource, HasSource, Source}, + src::{HasBodySource, HasSource}, Adt, AssocItem, Const, ConstData, Container, Crate, CrateDependency, DefWithBody, Enum, EnumVariant, FieldSource, FnData, Function, HasBody, MacroDef, Module, ModuleDef, ModuleSource, Static, Struct, StructField, Trait, TypeAlias, Union, @@ -72,7 +72,6 @@ pub use crate::{ generics::{GenericDef, GenericParam, GenericParams, HasGenericParams}, ids::{HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile}, impl_block::ImplBlock, - nameres::{ImportId, Namespace, PerNs}, resolve::ScopeDef, source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, ty::{ @@ -82,7 +81,11 @@ pub use crate::{ pub use hir_def::{ builtin_type::BuiltinType, + nameres::{ + per_ns::{Namespace, PerNs}, + raw::ImportId, + }, path::{Path, PathKind}, type_ref::Mutability, }; -pub use hir_expand::{either::Either, name::Name}; +pub use hir_expand::{either::Either, name::Name, Source}; diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index b423489a1117..0d4fa5b67363 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs @@ -1,21 +1,10 @@ //! See test_utils/src/marks.rs test_utils::marks!( - bogus_paths - // FIXME: restore this mark once hir is split - name_res_works_for_broken_modules - can_import_enum_variant type_var_cycles_resolve_completely type_var_cycles_resolve_as_possible type_var_resolves_to_int_var - glob_enum - glob_across_crates - std_prelude match_ergonomics_ref infer_while_let - macro_rules_from_other_crates_are_visible_with_macro_use - prelude_is_macro_use coerce_merge_fail_fallback - macro_dollar_crate_self - macro_dollar_crate_other ); diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 35dfaf3bad0e..ab97a09b9114 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -2,17 +2,17 @@ use std::{panic, sync::Arc}; +use hir_expand::diagnostics::DiagnosticSink; use parking_lot::Mutex; use ra_cfg::CfgOptions; use ra_db::{ salsa, CrateGraph, CrateId, Edition, FileId, FileLoader, FileLoaderDelegate, FilePosition, - SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId, + RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId, }; -use relative_path::{RelativePath, RelativePathBuf}; use rustc_hash::FxHashMap; use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; -use crate::{db, debug::HirDebugHelper, diagnostics::DiagnosticSink}; +use crate::{db, debug::HirDebugHelper}; pub const WORKSPACE: SourceRootId = SourceRootId(0); @@ -77,12 +77,6 @@ impl MockDatabase { (db, source_root, file_id) } - pub fn with_position(fixture: &str) -> (MockDatabase, FilePosition) { - let (db, position) = MockDatabase::from_fixture(fixture); - let position = position.expect("expected a marker ( <|> )"); - (db, position) - } - pub fn file_id_of(&self, path: &str) -> FileId { match self.files.get(path) { Some(it) => *it, @@ -90,25 +84,6 @@ impl MockDatabase { } } - pub fn set_crate_graph_from_fixture(&mut self, graph: CrateGraphFixture) { - let mut ids = FxHashMap::default(); - let mut crate_graph = CrateGraph::default(); - for (crate_name, (crate_root, edition, cfg_options, _)) in graph.0.iter() { - let crate_root = self.file_id_of(&crate_root); - let crate_id = crate_graph.add_crate_root(crate_root, *edition, cfg_options.clone()); - Arc::make_mut(&mut self.crate_names).insert(crate_id, crate_name.clone()); - ids.insert(crate_name, crate_id); - } - for (crate_name, (_, _, _, deps)) in graph.0.iter() { - let from = ids[crate_name]; - for dep in deps { - let to = ids[dep]; - crate_graph.add_dep(from, dep.as_str().into(), to).unwrap(); - } - } - self.set_crate_graph(Arc::new(crate_graph)) - } - pub fn diagnostics(&self) -> String { let mut buf = String::new(); let mut files: Vec = self.files.values().copied().collect(); @@ -285,46 +260,3 @@ impl MockDatabase { .collect() } } - -#[derive(Default)] -pub struct CrateGraphFixture(pub Vec<(String, (String, Edition, CfgOptions, Vec))>); - -#[macro_export] -macro_rules! crate_graph { - ($( - $crate_name:literal: ( - $crate_path:literal, - $($edition:literal,)? - [$($dep:literal),*] - $(, cfg = { - $($key:literal $(= $value:literal)?),* - $(,)? - })? - ), - )*) => {{ - let mut res = $crate::mock::CrateGraphFixture::default(); - $( - #[allow(unused_mut, unused_assignments)] - let mut edition = ra_db::Edition::Edition2018; - $(edition = ra_db::Edition::from_string($edition);)? - let cfg_options = { - #[allow(unused_mut)] - let mut cfg = ::ra_cfg::CfgOptions::default(); - $( - $( - if 0 == 0 $(+ { drop($value); 1})? { - cfg.insert_atom($key.into()); - } - $(cfg.insert_key_value($key.into(), $value.into());)? - )* - )? - cfg - }; - res.0.push(( - $crate_name.to_string(), - ($crate_path.to_string(), edition, cfg_options, vec![$($dep.to_string()),*]) - )); - )* - res - }} -} diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs deleted file mode 100644 index 7ba03182718c..000000000000 --- a/crates/ra_hir/src/nameres.rs +++ /dev/null @@ -1,557 +0,0 @@ -//! This module implements import-resolution/macro expansion algorithm. -//! -//! The result of this module is `CrateDefMap`: a data structure which contains: -//! -//! * a tree of modules for the crate -//! * for each module, a set of items visible in the module (directly declared -//! or imported) -//! -//! Note that `CrateDefMap` contains fully macro expanded code. -//! -//! Computing `CrateDefMap` can be partitioned into several logically -//! independent "phases". The phases are mutually recursive though, there's no -//! strict ordering. -//! -//! ## Collecting RawItems -//! -//! This happens in the `raw` module, which parses a single source file into a -//! set of top-level items. Nested imports are desugared to flat imports in -//! this phase. Macro calls are represented as a triple of (Path, Option, -//! TokenTree). -//! -//! ## Collecting Modules -//! -//! This happens in the `collector` module. In this phase, we recursively walk -//! tree of modules, collect raw items from submodules, populate module scopes -//! with defined items (so, we assign item ids in this phase) and record the set -//! of unresolved imports and macros. -//! -//! While we walk tree of modules, we also record macro_rules definitions and -//! expand calls to macro_rules defined macros. -//! -//! ## Resolving Imports -//! -//! We maintain a list of currently unresolved imports. On every iteration, we -//! try to resolve some imports from this list. If the import is resolved, we -//! record it, by adding an item to current module scope and, if necessary, by -//! recursively populating glob imports. -//! -//! ## Resolving Macros -//! -//! macro_rules from the same crate use a global mutable namespace. We expand -//! them immediately, when we collect modules. -//! -//! Macros from other crates (including proc-macros) can be used with -//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of -//! unexpanded macros. On every iteration, we try to resolve each macro call -//! path and, upon success, we run macro expansion and "collect module" phase -//! on the result - -mod per_ns; -mod collector; -#[cfg(test)] -mod tests; - -use std::sync::Arc; - -use hir_def::{builtin_type::BuiltinType, CrateModuleId}; -use once_cell::sync::Lazy; -use ra_arena::Arena; -use ra_db::{Edition, FileId}; -use ra_prof::profile; -use ra_syntax::ast; -use rustc_hash::{FxHashMap, FxHashSet}; -use test_utils::tested_by; - -use crate::{ - db::{AstDatabase, DefDatabase}, - diagnostics::DiagnosticSink, - ids::MacroDefId, - nameres::diagnostics::DefDiagnostic, - Adt, AstId, Crate, HirFileId, MacroDef, Module, ModuleDef, Name, Path, PathKind, Trait, -}; - -pub use self::per_ns::{Namespace, PerNs}; - -pub use hir_def::nameres::raw::ImportId; - -/// Contains all top-level defs from a macro-expanded crate -#[derive(Debug, PartialEq, Eq)] -pub struct CrateDefMap { - krate: Crate, - edition: Edition, - /// The prelude module for this crate. This either comes from an import - /// marked with the `prelude_import` attribute, or (in the normal case) from - /// a dependency (`std` or `core`). - prelude: Option, - extern_prelude: FxHashMap, - root: CrateModuleId, - modules: Arena, - - /// Some macros are not well-behavior, which leads to infinite loop - /// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } } - /// We mark it down and skip it in collector - /// - /// FIXME: - /// Right now it only handle a poison macro in a single crate, - /// such that if other crate try to call that macro, - /// the whole process will do again until it became poisoned in that crate. - /// We should handle this macro set globally - /// However, do we want to put it as a global variable? - poison_macros: FxHashSet, - - diagnostics: Vec, -} - -impl std::ops::Index for CrateDefMap { - type Output = ModuleData; - fn index(&self, id: CrateModuleId) -> &ModuleData { - &self.modules[id] - } -} - -#[derive(Default, Debug, PartialEq, Eq)] -pub struct ModuleData { - pub(crate) parent: Option, - pub(crate) children: FxHashMap, - pub(crate) scope: ModuleScope, - /// None for root - pub(crate) declaration: Option>, - /// None for inline modules. - /// - /// Note that non-inline modules, by definition, live inside non-macro file. - pub(crate) definition: Option, -} - -#[derive(Debug, Default, PartialEq, Eq, Clone)] -pub struct ModuleScope { - items: FxHashMap, - /// Macros visable in current module in legacy textual scope - /// - /// For macros invoked by an unquatified identifier like `bar!()`, `legacy_macros` will be searched in first. - /// If it yields no result, then it turns to module scoped `macros`. - /// It macros with name quatified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped, - /// and only normal scoped `macros` will be searched in. - /// - /// Note that this automatically inherit macros defined textually before the definition of module itself. - /// - /// Module scoped macros will be inserted into `items` instead of here. - // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will - // be all resolved to the last one defined if shadowing happens. - legacy_macros: FxHashMap, -} - -static BUILTIN_SCOPE: Lazy> = Lazy::new(|| { - BuiltinType::ALL - .iter() - .map(|(name, ty)| { - (name.clone(), Resolution { def: PerNs::types(ty.clone().into()), import: None }) - }) - .collect() -}); - -/// Legacy macros can only be accessed through special methods like `get_legacy_macros`. -/// Other methods will only resolve values, types and module scoped macros only. -impl ModuleScope { - pub fn entries<'a>(&'a self) -> impl Iterator + 'a { - //FIXME: shadowing - self.items.iter().chain(BUILTIN_SCOPE.iter()) - } - - /// Iterate over all module scoped macros - pub fn macros<'a>(&'a self) -> impl Iterator + 'a { - self.items - .iter() - .filter_map(|(name, res)| res.def.get_macros().map(|macro_| (name, macro_))) - } - - /// Iterate over all legacy textual scoped macros visable at the end of the module - pub fn legacy_macros<'a>(&'a self) -> impl Iterator + 'a { - self.legacy_macros.iter().map(|(name, def)| (name, *def)) - } - - /// Get a name from current module scope, legacy macros are not included - pub fn get(&self, name: &Name) -> Option<&Resolution> { - self.items.get(name).or_else(|| BUILTIN_SCOPE.get(name)) - } - - pub fn traits<'a>(&'a self) -> impl Iterator + 'a { - self.items.values().filter_map(|r| match r.def.take_types() { - Some(ModuleDef::Trait(t)) => Some(t), - _ => None, - }) - } - - fn get_legacy_macro(&self, name: &Name) -> Option { - self.legacy_macros.get(name).copied() - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct Resolution { - /// None for unresolved - pub def: PerNs, - /// ident by which this is imported into local scope. - pub import: Option, -} - -impl Resolution { - pub(crate) fn from_macro(macro_: MacroDef) -> Self { - Resolution { def: PerNs::macros(macro_), import: None } - } -} - -#[derive(Debug, Clone)] -struct ResolvePathResult { - resolved_def: PerNs, - segment_index: Option, - reached_fixedpoint: ReachedFixedPoint, -} - -impl ResolvePathResult { - fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult { - ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None) - } - - fn with( - resolved_def: PerNs, - reached_fixedpoint: ReachedFixedPoint, - segment_index: Option, - ) -> ResolvePathResult { - ResolvePathResult { resolved_def, reached_fixedpoint, segment_index } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum ResolveMode { - Import, - Other, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum ReachedFixedPoint { - Yes, - No, -} - -impl CrateDefMap { - pub(crate) fn crate_def_map_query( - // Note that this doesn't have `+ AstDatabase`! - // This gurantess that `CrateDefMap` is stable across reparses. - db: &impl DefDatabase, - krate: Crate, - ) -> Arc { - let _p = profile("crate_def_map_query"); - let def_map = { - let edition = krate.edition(db); - let mut modules: Arena = Arena::default(); - let root = modules.alloc(ModuleData::default()); - CrateDefMap { - krate, - edition, - extern_prelude: FxHashMap::default(), - prelude: None, - root, - modules, - poison_macros: FxHashSet::default(), - diagnostics: Vec::new(), - } - }; - let def_map = collector::collect_defs(db, def_map); - Arc::new(def_map) - } - - pub(crate) fn krate(&self) -> Crate { - self.krate - } - - pub(crate) fn root(&self) -> CrateModuleId { - self.root - } - - pub(crate) fn prelude(&self) -> Option { - self.prelude - } - - pub(crate) fn extern_prelude(&self) -> &FxHashMap { - &self.extern_prelude - } - - pub(crate) fn add_diagnostics( - &self, - db: &(impl DefDatabase + AstDatabase), - module: CrateModuleId, - sink: &mut DiagnosticSink, - ) { - self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink)) - } - - pub(crate) fn find_module_by_source( - &self, - file_id: HirFileId, - decl_id: Option>, - ) -> Option { - let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| { - if decl_id.is_some() { - module_data.declaration == decl_id - } else { - module_data.definition.map(|it| it.into()) == Some(file_id) - } - })?; - Some(module_id) - } - - pub(crate) fn resolve_path( - &self, - db: &impl DefDatabase, - original_module: CrateModuleId, - path: &Path, - ) -> (PerNs, Option) { - let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path); - (res.resolved_def, res.segment_index) - } - - // Returns Yes if we are sure that additions to `ItemMap` wouldn't change - // the result. - fn resolve_path_fp_with_macro( - &self, - db: &impl DefDatabase, - mode: ResolveMode, - original_module: CrateModuleId, - path: &Path, - ) -> ResolvePathResult { - let mut segments = path.segments.iter().enumerate(); - let mut curr_per_ns: PerNs = match path.kind { - PathKind::DollarCrate(crate_id) => { - let krate = Crate { crate_id }; - if krate == self.krate { - tested_by!(macro_dollar_crate_self); - PerNs::types(Module::new(self.krate, self.root).into()) - } else { - match krate.root_module(db) { - Some(module) => { - tested_by!(macro_dollar_crate_other); - PerNs::types(module.into()) - } - None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), - } - } - } - PathKind::Crate => PerNs::types(Module::new(self.krate, self.root).into()), - PathKind::Self_ => PerNs::types(Module::new(self.krate, original_module).into()), - // plain import or absolute path in 2015: crate-relative with - // fallback to extern prelude (with the simplification in - // rust-lang/rust#57745) - // FIXME there must be a nicer way to write this condition - PathKind::Plain | PathKind::Abs - if self.edition == Edition::Edition2015 - && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => - { - let segment = match segments.next() { - Some((_, segment)) => segment, - None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), - }; - log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); - self.resolve_name_in_crate_root_or_extern_prelude(&segment.name) - } - PathKind::Plain => { - let segment = match segments.next() { - Some((_, segment)) => segment, - None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), - }; - log::debug!("resolving {:?} in module", segment); - self.resolve_name_in_module(db, original_module, &segment.name) - } - PathKind::Super => { - if let Some(p) = self.modules[original_module].parent { - PerNs::types(Module::new(self.krate, p).into()) - } else { - log::debug!("super path in root module"); - return ResolvePathResult::empty(ReachedFixedPoint::Yes); - } - } - PathKind::Abs => { - // 2018-style absolute path -- only extern prelude - let segment = match segments.next() { - Some((_, segment)) => segment, - None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), - }; - if let Some(def) = self.extern_prelude.get(&segment.name) { - log::debug!("absolute path {:?} resolved to crate {:?}", path, def); - PerNs::types(*def) - } else { - return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude - } - } - PathKind::Type(_) => { - // This is handled in `infer::infer_path_expr` - // The result returned here does not matter - return ResolvePathResult::empty(ReachedFixedPoint::Yes); - } - }; - - for (i, segment) in segments { - let curr = match curr_per_ns.take_types() { - Some(r) => r, - None => { - // we still have path segments left, but the path so far - // didn't resolve in the types namespace => no resolution - // (don't break here because `curr_per_ns` might contain - // something in the value namespace, and it would be wrong - // to return that) - return ResolvePathResult::empty(ReachedFixedPoint::No); - } - }; - // resolve segment in curr - - curr_per_ns = match curr { - ModuleDef::Module(module) => { - if module.krate() != self.krate { - let path = - Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ }; - log::debug!("resolving {:?} in other crate", path); - let defp_map = db.crate_def_map(module.krate()); - let (def, s) = defp_map.resolve_path(db, module.id.module_id, &path); - return ResolvePathResult::with( - def, - ReachedFixedPoint::Yes, - s.map(|s| s + i), - ); - } - - // Since it is a qualified path here, it should not contains legacy macros - match self[module.id.module_id].scope.get(&segment.name) { - Some(res) => res.def, - _ => { - log::debug!("path segment {:?} not found", segment.name); - return ResolvePathResult::empty(ReachedFixedPoint::No); - } - } - } - ModuleDef::Adt(Adt::Enum(e)) => { - // enum variant - tested_by!(can_import_enum_variant); - match e.variant(db, &segment.name) { - Some(variant) => PerNs::both(variant.into(), variant.into()), - None => { - return ResolvePathResult::with( - PerNs::types(e.into()), - ReachedFixedPoint::Yes, - Some(i), - ); - } - } - } - s => { - // could be an inherent method call in UFCS form - // (`Struct::method`), or some other kind of associated item - log::debug!( - "path segment {:?} resolved to non-module {:?}, but is not last", - segment.name, - curr, - ); - - return ResolvePathResult::with( - PerNs::types(s), - ReachedFixedPoint::Yes, - Some(i), - ); - } - }; - } - ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) - } - - fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs { - let from_crate_root = - self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def); - let from_extern_prelude = self.resolve_name_in_extern_prelude(name); - - from_crate_root.or(from_extern_prelude) - } - - pub(crate) fn resolve_name_in_module( - &self, - db: &impl DefDatabase, - module: CrateModuleId, - name: &Name, - ) -> PerNs { - // Resolve in: - // - legacy scope of macro - // - current module / scope - // - extern prelude - // - std prelude - let from_legacy_macro = - self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros); - let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def); - let from_extern_prelude = - self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); - let from_prelude = self.resolve_in_prelude(db, name); - - from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude) - } - - fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { - self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) - } - - fn resolve_in_prelude(&self, db: &impl DefDatabase, name: &Name) -> PerNs { - if let Some(prelude) = self.prelude { - let keep; - let def_map = if prelude.krate() == self.krate { - self - } else { - // Extend lifetime - keep = db.crate_def_map(prelude.krate()); - &keep - }; - def_map[prelude.id.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def) - } else { - PerNs::none() - } - } -} - -mod diagnostics { - use ra_syntax::{ast, AstPtr}; - use relative_path::RelativePathBuf; - - use crate::{ - db::{AstDatabase, DefDatabase}, - diagnostics::{DiagnosticSink, UnresolvedModule}, - nameres::CrateModuleId, - AstId, - }; - - #[derive(Debug, PartialEq, Eq)] - pub(super) enum DefDiagnostic { - UnresolvedModule { - module: CrateModuleId, - declaration: AstId, - candidate: RelativePathBuf, - }, - } - - impl DefDiagnostic { - pub(super) fn add_to( - &self, - db: &(impl DefDatabase + AstDatabase), - target_module: CrateModuleId, - sink: &mut DiagnosticSink, - ) { - match self { - DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { - if *module != target_module { - return; - } - let decl = declaration.to_node(db); - sink.push(UnresolvedModule { - file: declaration.file_id(), - decl: AstPtr::new(&decl), - candidate: candidate.clone(), - }) - } - } - } - } -} diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 75b24d386f16..b932b0c8cdbc 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -3,8 +3,9 @@ use std::sync::Arc; use hir_def::{ builtin_type::BuiltinType, + nameres::CrateDefMap, path::{Path, PathKind}, - CrateModuleId, + AdtId, CrateModuleId, ModuleDefId, }; use hir_expand::name::{self, Name}; use rustc_hash::FxHashSet; @@ -18,8 +19,8 @@ use crate::{ }, generics::GenericParams, impl_block::ImplBlock, - nameres::{CrateDefMap, PerNs}, - Adt, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, Static, Struct, Trait, TypeAlias, + Adt, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, PerNs, Static, Struct, Trait, + TypeAlias, }; #[derive(Debug, Clone, Default)] @@ -90,7 +91,7 @@ impl Resolver { pub(crate) fn resolve_known_trait(&self, db: &impl HirDatabase, path: &Path) -> Option { let res = self.resolve_module_path(db, path).take_types()?; match res { - ModuleDef::Trait(it) => Some(it), + ModuleDefId::TraitId(it) => Some(it.into()), _ => None, } } @@ -103,7 +104,7 @@ impl Resolver { ) -> Option { let res = self.resolve_module_path(db, path).take_types()?; match res { - ModuleDef::Adt(Adt::Struct(it)) => Some(it), + ModuleDefId::AdtId(AdtId::StructId(it)) => Some(it.into()), _ => None, } } @@ -112,7 +113,7 @@ impl Resolver { pub(crate) fn resolve_known_enum(&self, db: &impl HirDatabase, path: &Path) -> Option { let res = self.resolve_module_path(db, path).take_types()?; match res { - ModuleDef::Adt(Adt::Enum(it)) => Some(it), + ModuleDefId::AdtId(AdtId::EnumId(it)) => Some(it.into()), _ => None, } } @@ -166,18 +167,18 @@ impl Resolver { Scope::ModuleScope(m) => { let (module_def, idx) = m.crate_def_map.resolve_path(db, m.module_id, path); let res = match module_def.take_types()? { - ModuleDef::Adt(it) => TypeNs::Adt(it), - ModuleDef::EnumVariant(it) => TypeNs::EnumVariant(it), + ModuleDefId::AdtId(it) => TypeNs::Adt(it.into()), + ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariant(it.into()), - ModuleDef::TypeAlias(it) => TypeNs::TypeAlias(it), - ModuleDef::BuiltinType(it) => TypeNs::BuiltinType(it), + ModuleDefId::TypeAliasId(it) => TypeNs::TypeAlias(it.into()), + ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), - ModuleDef::Trait(it) => TypeNs::Trait(it), + ModuleDefId::TraitId(it) => TypeNs::Trait(it.into()), - ModuleDef::Function(_) - | ModuleDef::Const(_) - | ModuleDef::Static(_) - | ModuleDef::Module(_) => return None, + ModuleDefId::FunctionId(_) + | ModuleDefId::ConstId(_) + | ModuleDefId::StaticId(_) + | ModuleDefId::ModuleId(_) => return None, }; return Some((res, idx)); } @@ -261,33 +262,35 @@ impl Resolver { return match idx { None => { let value = match module_def.take_values()? { - ModuleDef::Function(it) => ValueNs::Function(it), - ModuleDef::Adt(Adt::Struct(it)) => ValueNs::Struct(it), - ModuleDef::EnumVariant(it) => ValueNs::EnumVariant(it), - ModuleDef::Const(it) => ValueNs::Const(it), - ModuleDef::Static(it) => ValueNs::Static(it), + ModuleDefId::FunctionId(it) => ValueNs::Function(it.into()), + ModuleDefId::AdtId(AdtId::StructId(it)) => { + ValueNs::Struct(it.into()) + } + ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariant(it.into()), + ModuleDefId::ConstId(it) => ValueNs::Const(it.into()), + ModuleDefId::StaticId(it) => ValueNs::Static(it.into()), - ModuleDef::Adt(Adt::Enum(_)) - | ModuleDef::Adt(Adt::Union(_)) - | ModuleDef::Trait(_) - | ModuleDef::TypeAlias(_) - | ModuleDef::BuiltinType(_) - | ModuleDef::Module(_) => return None, + ModuleDefId::AdtId(AdtId::EnumId(_)) + | ModuleDefId::AdtId(AdtId::UnionId(_)) + | ModuleDefId::TraitId(_) + | ModuleDefId::TypeAliasId(_) + | ModuleDefId::BuiltinType(_) + | ModuleDefId::ModuleId(_) => return None, }; Some(ResolveValueResult::ValueNs(value)) } Some(idx) => { let ty = match module_def.take_types()? { - ModuleDef::Adt(it) => TypeNs::Adt(it), - ModuleDef::Trait(it) => TypeNs::Trait(it), - ModuleDef::TypeAlias(it) => TypeNs::TypeAlias(it), - ModuleDef::BuiltinType(it) => TypeNs::BuiltinType(it), + ModuleDefId::AdtId(it) => TypeNs::Adt(it.into()), + ModuleDefId::TraitId(it) => TypeNs::Trait(it.into()), + ModuleDefId::TypeAliasId(it) => TypeNs::TypeAlias(it.into()), + ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), - ModuleDef::Module(_) - | ModuleDef::Function(_) - | ModuleDef::EnumVariant(_) - | ModuleDef::Const(_) - | ModuleDef::Static(_) => return None, + ModuleDefId::ModuleId(_) + | ModuleDefId::FunctionId(_) + | ModuleDefId::EnumVariantId(_) + | ModuleDefId::ConstId(_) + | ModuleDefId::StaticId(_) => return None, }; Some(ResolveValueResult::Partial(ty, idx)) } @@ -315,7 +318,7 @@ impl Resolver { path: &Path, ) -> Option { let (item_map, module) = self.module()?; - item_map.resolve_path(db, module, path).0.get_macros() + item_map.resolve_path(db, module, path).0.get_macros().map(MacroDef::from) } pub(crate) fn process_all_names( @@ -333,10 +336,11 @@ impl Resolver { for scope in &self.scopes { if let Scope::ModuleScope(m) = scope { if let Some(prelude) = m.crate_def_map.prelude() { - let prelude_def_map = db.crate_def_map(prelude.krate()); - traits.extend(prelude_def_map[prelude.id.module_id].scope.traits()); + let prelude_def_map = db.crate_def_map(prelude.krate); + traits + .extend(prelude_def_map[prelude.module_id].scope.traits().map(Trait::from)); } - traits.extend(m.crate_def_map[m.module_id].scope.traits()); + traits.extend(m.crate_def_map[m.module_id].scope.traits().map(Trait::from)); } } traits @@ -351,7 +355,7 @@ impl Resolver { } pub(crate) fn krate(&self) -> Option { - self.module().map(|t| t.0.krate()) + self.module().map(|t| Crate { crate_id: t.0.krate() }) } pub(crate) fn where_predicates_in_scope<'a>( @@ -420,8 +424,10 @@ impl From for ScopeDef { fn from(def: PerNs) -> Self { def.take_types() .or_else(|| def.take_values()) - .map(ScopeDef::ModuleDef) - .or_else(|| def.get_macros().map(ScopeDef::MacroDef)) + .map(|module_def_id| ScopeDef::ModuleDef(module_def_id.into())) + .or_else(|| { + def.get_macros().map(|macro_def_id| ScopeDef::MacroDef(macro_def_id.into())) + }) .unwrap_or(ScopeDef::Unknown) } } @@ -441,18 +447,16 @@ impl Scope { f(name.clone(), res.def.into()); }); m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { - f(name.clone(), ScopeDef::MacroDef(macro_)); + f(name.clone(), ScopeDef::MacroDef(macro_.into())); }); - m.crate_def_map.extern_prelude().iter().for_each(|(name, def)| { - f(name.clone(), ScopeDef::ModuleDef(*def)); + m.crate_def_map.extern_prelude().iter().for_each(|(name, &def)| { + f(name.clone(), ScopeDef::ModuleDef(def.into())); }); if let Some(prelude) = m.crate_def_map.prelude() { - let prelude_def_map = db.crate_def_map(prelude.krate()); - prelude_def_map[prelude.id.module_id].scope.entries().for_each( - |(name, res)| { - f(name.clone(), res.def.into()); - }, - ); + let prelude_def_map = db.crate_def_map(prelude.krate); + prelude_def_map[prelude.module_id].scope.entries().for_each(|(name, res)| { + f(name.clone(), res.def.into()); + }); } } Scope::GenericParams(gp) => { diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index a4ca59bba1b4..66cb4b357f88 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -253,8 +253,11 @@ impl SourceAnalyzer { Some(res) }); - let items = - self.resolver.resolve_module_path(db, &path).take_types().map(PathResolution::Def); + let items = self + .resolver + .resolve_module_path(db, &path) + .take_types() + .map(|it| PathResolution::Def(it.into())); types.or(values).or(items).or_else(|| { self.resolver.resolve_path_as_macro(db, &path).map(|def| PathResolution::Macro(def)) }) diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 6694467a36cb..2370e8d4f52f 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -25,7 +25,7 @@ use hir_def::{ path::known, type_ref::{Mutability, TypeRef}, }; -use hir_expand::name; +use hir_expand::{diagnostics::DiagnosticSink, name}; use ra_arena::map::ArenaMap; use ra_prof::profile; use test_utils::tested_by; @@ -40,7 +40,6 @@ use crate::{ adt::VariantDef, code_model::TypeAlias, db::HirDatabase, - diagnostics::DiagnosticSink, expr::{BindingAnnotation, Body, ExprId, PatId}, resolve::{Resolver, TypeNs}, ty::infer::diagnostics::InferenceDiagnostic, @@ -719,12 +718,9 @@ impl Expectation { } mod diagnostics { - use crate::{ - db::HirDatabase, - diagnostics::{DiagnosticSink, NoSuchField}, - expr::ExprId, - Function, HasSource, - }; + use hir_expand::diagnostics::DiagnosticSink; + + use crate::{db::HirDatabase, diagnostics::NoSuchField, expr::ExprId, Function, HasSource}; #[derive(Debug, PartialEq, Eq, Clone)] pub(super) enum InferenceDiagnostic { diff --git a/crates/ra_hir/src/ty/infer/expr.rs b/crates/ra_hir/src/ty/infer/expr.rs index fed52df39cef..a09ef5c5d3af 100644 --- a/crates/ra_hir/src/ty/infer/expr.rs +++ b/crates/ra_hir/src/ty/infer/expr.rs @@ -11,12 +11,11 @@ use crate::{ db::HirDatabase, expr::{self, Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, generics::{GenericParams, HasGenericParams}, - nameres::Namespace, ty::{ autoderef, method_resolution, op, primitive, CallableDef, InferTy, Mutability, Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk, }, - Adt, Name, + Adt, Name, Namespace, }; impl<'a, D: HirDatabase> InferenceContext<'a, D> { diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 8e28343071a2..e29ab8492a5e 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -23,15 +23,14 @@ use crate::{ db::HirDatabase, generics::HasGenericParams, generics::{GenericDef, WherePredicate}, - nameres::Namespace, resolve::{Resolver, TypeNs}, ty::{ primitive::{FloatTy, IntTy}, Adt, }, util::make_mut_slice, - Const, Enum, EnumVariant, Function, ModuleDef, Path, Static, Struct, StructField, Trait, - TypeAlias, Union, + Const, Enum, EnumVariant, Function, ModuleDef, Namespace, Path, Static, Struct, StructField, + Trait, TypeAlias, Union, }; impl Ty { diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index bfef48b1613a..4b7e34878acd 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2,8 +2,7 @@ use std::fmt::Write; use std::sync::Arc; use insta::assert_snapshot; - -use ra_db::{salsa::Database, FilePosition, SourceDatabase}; +use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase}; use ra_syntax::{ algo, ast::{self, AstNode}, @@ -25,9 +24,9 @@ mod coercion; #[test] fn cfg_impl_block() { - let (mut db, pos) = MockDatabase::with_position( + let (db, pos) = MockDatabase::with_position( r#" -//- /main.rs +//- /main.rs crate:main deps:foo cfg:test use foo::S as T; struct S; @@ -46,7 +45,7 @@ fn test() { t<|>; } -//- /foo.rs +//- /foo.rs crate:foo struct S; #[cfg(not(test))] @@ -60,18 +59,14 @@ impl S { } "#, ); - db.set_crate_graph_from_fixture(crate_graph! { - "main": ("/main.rs", ["foo"], cfg = { "test" }), - "foo": ("/foo.rs", []), - }); assert_eq!("(i32, {unknown}, i32, {unknown})", type_at_pos(&db, pos)); } #[test] fn infer_await() { - let (mut db, pos) = MockDatabase::with_position( + let (db, pos) = MockDatabase::with_position( r#" -//- /main.rs +//- /main.rs crate:main deps:std struct IntFuture; @@ -85,7 +80,7 @@ fn test() { v<|>; } -//- /std.rs +//- /std.rs crate:std #[prelude_import] use future::*; mod future { trait Future { @@ -95,18 +90,14 @@ mod future { "#, ); - db.set_crate_graph_from_fixture(crate_graph! { - "main": ("/main.rs", ["std"]), - "std": ("/std.rs", []), - }); assert_eq!("u64", type_at_pos(&db, pos)); } #[test] fn infer_box() { - let (mut db, pos) = MockDatabase::with_position( + let (db, pos) = MockDatabase::with_position( r#" -//- /main.rs +//- /main.rs crate:main deps:std fn test() { let x = box 1; @@ -114,7 +105,7 @@ fn test() { t<|>; } -//- /std.rs +//- /std.rs crate:std #[prelude_import] use prelude::*; mod prelude {} @@ -126,10 +117,6 @@ mod boxed { "#, ); - db.set_crate_graph_from_fixture(crate_graph! { - "main": ("/main.rs", ["std"]), - "std": ("/std.rs", []), - }); assert_eq!("(Box, Box>, Box<&i32>, Box<[i32;_]>)", type_at_pos(&db, pos)); } @@ -154,9 +141,9 @@ fn test() { #[test] fn infer_try() { - let (mut db, pos) = MockDatabase::with_position( + let (db, pos) = MockDatabase::with_position( r#" -//- /main.rs +//- /main.rs crate:main deps:std fn test() { let r: Result = Result::Ok(1); @@ -164,7 +151,7 @@ fn test() { v<|>; } -//- /std.rs +//- /std.rs crate:std #[prelude_import] use ops::*; mod ops { @@ -189,18 +176,14 @@ mod result { "#, ); - db.set_crate_graph_from_fixture(crate_graph! { - "main": ("/main.rs", ["std"]), - "std": ("/std.rs", []), - }); assert_eq!("i32", type_at_pos(&db, pos)); } #[test] fn infer_for_loop() { - let (mut db, pos) = MockDatabase::with_position( + let (db, pos) = MockDatabase::with_position( r#" -//- /main.rs +//- /main.rs crate:main deps:std use std::collections::Vec; @@ -212,7 +195,7 @@ fn test() { } } -//- /std.rs +//- /std.rs crate:std #[prelude_import] use iter::*; mod iter { @@ -234,10 +217,6 @@ mod collections { } "#, ); - db.set_crate_graph_from_fixture(crate_graph! { - "main": ("/main.rs", ["std"]), - "std": ("/std.rs", []), - }); assert_eq!("&str", type_at_pos(&db, pos)); } @@ -2505,15 +2484,15 @@ pub fn main_loop() { #[test] fn cross_crate_associated_method_call() { - let (mut db, pos) = MockDatabase::with_position( + let (db, pos) = MockDatabase::with_position( r#" -//- /main.rs +//- /main.rs crate:main deps:other_crate fn test() { let x = other_crate::foo::S::thing(); x<|>; } -//- /lib.rs +//- /lib.rs crate:other_crate mod foo { struct S; impl S { @@ -2522,10 +2501,6 @@ mod foo { } "#, ); - db.set_crate_graph_from_fixture(crate_graph! { - "main": ("/main.rs", ["other_crate"]), - "other_crate": ("/lib.rs", []), - }); assert_eq!("i128", type_at_pos(&db, pos)); } @@ -3403,16 +3378,15 @@ fn test() { S.foo()<|>; } #[test] fn infer_macro_with_dollar_crate_is_correct_in_expr() { - covers!(macro_dollar_crate_other); - let (mut db, pos) = MockDatabase::with_position( + let (db, pos) = MockDatabase::with_position( r#" -//- /main.rs +//- /main.rs crate:main deps:foo fn test() { let x = (foo::foo!(1), foo::foo!(2)); x<|>; } -//- /lib.rs +//- /lib.rs crate:foo #[macro_export] macro_rules! foo { (1) => { $crate::bar!() }; @@ -3427,10 +3401,6 @@ macro_rules! bar { pub fn baz() -> usize { 31usize } "#, ); - db.set_crate_graph_from_fixture(crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }); assert_eq!("(i32, usize)", type_at_pos(&db, pos)); } @@ -3512,9 +3482,9 @@ fn test() { (&S).foo()<|>; } #[test] fn method_resolution_trait_from_prelude() { - let (mut db, pos) = MockDatabase::with_position( + let (db, pos) = MockDatabase::with_position( r#" -//- /main.rs +//- /main.rs crate:main deps:other_crate struct S; impl Clone for S {} @@ -3522,7 +3492,7 @@ fn test() { S.clone()<|>; } -//- /lib.rs +//- /lib.rs crate:other_crate #[prelude_import] use foo::*; mod foo { @@ -3532,10 +3502,6 @@ mod foo { } "#, ); - db.set_crate_graph_from_fixture(crate_graph! { - "main": ("/main.rs", ["other_crate"]), - "other_crate": ("/lib.rs", []), - }); assert_eq!("S", type_at_pos(&db, pos)); } diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index 746c907e80da..21262be79598 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml @@ -7,7 +7,6 @@ authors = ["rust-analyzer developers"] [dependencies] log = "0.4.5" once_cell = "1.0.1" -relative-path = "1.0.0" rustc-hash = "1.0" ra_arena = { path = "../ra_arena" } @@ -19,3 +18,6 @@ test_utils = { path = "../test_utils" } mbe = { path = "../ra_mbe", package = "ra_mbe" } ra_cfg = { path = "../ra_cfg" } tt = { path = "../ra_tt", package = "ra_tt" } + +[dev-dependencies] +insta = "0.12.0" diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 22bd469f0a31..8f41e55d26f9 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs @@ -8,7 +8,7 @@ use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; use crate::{ db::DefDatabase2, type_ref::TypeRef, AstItemDef, EnumId, LocalEnumVariantId, - LocalStructFieldId, StructId, + LocalStructFieldId, StructId, UnionId, }; /// Note that we use `StructData` for unions as well! @@ -56,6 +56,13 @@ impl StructData { let variant_data = Arc::new(variant_data); Arc::new(StructData { name, variant_data }) } + pub(crate) fn union_data_query(db: &impl DefDatabase2, struct_: UnionId) -> Arc { + let src = struct_.source(db); + let name = src.ast.name().map(|n| n.as_name()); + let variant_data = VariantData::new(src.ast.kind()); + let variant_data = Arc::new(variant_data); + Arc::new(StructData { name, variant_data }) + } } impl EnumData { @@ -74,6 +81,11 @@ impl EnumData { .collect(); Arc::new(EnumData { name, variants }) } + + pub(crate) fn variant(&self, name: &Name) -> Option { + let (id, _) = self.variants.iter().find(|(_id, data)| data.name.as_ref() == Some(name))?; + Some(id) + } } impl VariantData { diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index f6027013f2a8..a42348101b48 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs @@ -2,13 +2,16 @@ use std::sync::Arc; use hir_expand::{db::AstDatabase, HirFileId}; -use ra_db::{salsa, SourceDatabase}; +use ra_db::{salsa, CrateId, SourceDatabase}; use ra_syntax::ast; use crate::{ adt::{EnumData, StructData}, - nameres::raw::{ImportSourceMap, RawItems}, - EnumId, StructId, + nameres::{ + raw::{ImportSourceMap, RawItems}, + CrateDefMap, + }, + EnumId, StructId, UnionId, }; #[salsa::query_group(InternDatabaseStorage)] @@ -42,9 +45,15 @@ pub trait DefDatabase2: InternDatabase + AstDatabase { #[salsa::invoke(RawItems::raw_items_query)] fn raw_items(&self, file_id: HirFileId) -> Arc; + #[salsa::invoke(CrateDefMap::crate_def_map_query)] + fn crate_def_map(&self, krate: CrateId) -> Arc; + #[salsa::invoke(StructData::struct_data_query)] fn struct_data(&self, s: StructId) -> Arc; + #[salsa::invoke(StructData::union_data_query)] + fn union_data(&self, s: UnionId) -> Arc; + #[salsa::invoke(EnumData::enum_data_query)] fn enum_data(&self, e: EnumId) -> Arc; } diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs new file mode 100644 index 000000000000..9843009a5dc6 --- /dev/null +++ b/crates/ra_hir_def/src/diagnostics.rs @@ -0,0 +1,28 @@ +//! Diagnostics produced by `hir_def`. + +use std::any::Any; + +use hir_expand::diagnostics::Diagnostic; +use ra_db::RelativePathBuf; +use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; + +use hir_expand::{HirFileId, Source}; + +#[derive(Debug)] +pub struct UnresolvedModule { + pub file: HirFileId, + pub decl: AstPtr, + pub candidate: RelativePathBuf, +} + +impl Diagnostic for UnresolvedModule { + fn message(&self) -> String { + "unresolved module".to_string() + } + fn source(&self) -> Source { + Source { file_id: self.file, ast: self.decl.into() } + } + fn as_any(&self) -> &(dyn Any + Send + 'static) { + self + } +} diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 76d5f18522bc..63ed2a09833b 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -13,25 +13,25 @@ pub mod path; pub mod type_ref; pub mod builtin_type; pub mod adt; +pub mod diagnostics; + +#[cfg(test)] +mod test_db; +#[cfg(test)] +mod marks; // FIXME: this should be private pub mod nameres; use std::hash::{Hash, Hasher}; -use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId}; +use hir_expand::{ast_id_map::FileAstId, db::AstDatabase, AstId, HirFileId, Source}; use ra_arena::{impl_arena_id, RawId}; use ra_db::{salsa, CrateId, FileId}; use ra_syntax::{ast, AstNode, SyntaxNode}; use crate::{builtin_type::BuiltinType, db::InternDatabase}; -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct Source { - pub file_id: HirFileId, - pub ast: T, -} - pub enum ModuleSource { SourceFile(ast::SourceFile), Module(ast::Module), @@ -94,15 +94,6 @@ impl ModuleSource { } } -impl Source { - pub fn map U, U>(self, f: F) -> Source { - Source { file_id: self.file_id, ast: f(self.ast) } - } - pub fn file_syntax(&self, db: &impl AstDatabase) -> SyntaxNode { - db.parse_or_expand(self.file_id).expect("source created from invalid file") - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ModuleId { pub krate: CrateId, @@ -252,8 +243,8 @@ impl AstItemDef for EnumId { // FIXME: rename to `VariantId`, only enums can ave variants #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct EnumVariantId { - parent: EnumId, - local_id: LocalEnumVariantId, + pub parent: EnumId, + pub local_id: LocalEnumVariantId, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/crates/ra_hir_def/src/marks.rs b/crates/ra_hir_def/src/marks.rs new file mode 100644 index 000000000000..0b99eac71749 --- /dev/null +++ b/crates/ra_hir_def/src/marks.rs @@ -0,0 +1,14 @@ +//! See test_utils/src/marks.rs + +test_utils::marks!( + bogus_paths + name_res_works_for_broken_modules + can_import_enum_variant + glob_enum + glob_across_crates + std_prelude + macro_rules_from_other_crates_are_visible_with_macro_use + prelude_is_macro_use + macro_dollar_crate_self + macro_dollar_crate_other +); diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index 11ba8a7770d9..433bdde48f7f 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs @@ -1,5 +1,543 @@ -//! FIXME: write short doc here +//! This module implements import-resolution/macro expansion algorithm. +//! +//! The result of this module is `CrateDefMap`: a data structure which contains: +//! +//! * a tree of modules for the crate +//! * for each module, a set of items visible in the module (directly declared +//! or imported) +//! +//! Note that `CrateDefMap` contains fully macro expanded code. +//! +//! Computing `CrateDefMap` can be partitioned into several logically +//! independent "phases". The phases are mutually recursive though, there's no +//! strict ordering. +//! +//! ## Collecting RawItems +//! +//! This happens in the `raw` module, which parses a single source file into a +//! set of top-level items. Nested imports are desugared to flat imports in +//! this phase. Macro calls are represented as a triple of (Path, Option, +//! TokenTree). +//! +//! ## Collecting Modules +//! +//! This happens in the `collector` module. In this phase, we recursively walk +//! tree of modules, collect raw items from submodules, populate module scopes +//! with defined items (so, we assign item ids in this phase) and record the set +//! of unresolved imports and macros. +//! +//! While we walk tree of modules, we also record macro_rules definitions and +//! expand calls to macro_rules defined macros. +//! +//! ## Resolving Imports +//! +//! We maintain a list of currently unresolved imports. On every iteration, we +//! try to resolve some imports from this list. If the import is resolved, we +//! record it, by adding an item to current module scope and, if necessary, by +//! recursively populating glob imports. +//! +//! ## Resolving Macros +//! +//! macro_rules from the same crate use a global mutable namespace. We expand +//! them immediately, when we collect modules. +//! +//! Macros from other crates (including proc-macros) can be used with +//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of +//! unexpanded macros. On every iteration, we try to resolve each macro call +//! path and, upon success, we run macro expansion and "collect module" phase +//! on the result // FIXME: review privacy of submodules pub mod raw; +pub mod per_ns; +pub mod collector; pub mod mod_resolution; + +#[cfg(test)] +mod tests; + +use std::sync::Arc; + +use hir_expand::{diagnostics::DiagnosticSink, name::Name, MacroDefId}; +use once_cell::sync::Lazy; +use ra_arena::Arena; +use ra_db::{CrateId, Edition, FileId}; +use ra_prof::profile; +use ra_syntax::ast; +use rustc_hash::{FxHashMap, FxHashSet}; +use test_utils::tested_by; + +use crate::{ + builtin_type::BuiltinType, + db::DefDatabase2, + nameres::{diagnostics::DefDiagnostic, per_ns::PerNs, raw::ImportId}, + path::{Path, PathKind}, + AdtId, AstId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId, TraitId, +}; + +/// Contains all top-level defs from a macro-expanded crate +#[derive(Debug, PartialEq, Eq)] +pub struct CrateDefMap { + krate: CrateId, + edition: Edition, + /// The prelude module for this crate. This either comes from an import + /// marked with the `prelude_import` attribute, or (in the normal case) from + /// a dependency (`std` or `core`). + prelude: Option, + extern_prelude: FxHashMap, + root: CrateModuleId, + pub modules: Arena, + + /// Some macros are not well-behavior, which leads to infinite loop + /// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } } + /// We mark it down and skip it in collector + /// + /// FIXME: + /// Right now it only handle a poison macro in a single crate, + /// such that if other crate try to call that macro, + /// the whole process will do again until it became poisoned in that crate. + /// We should handle this macro set globally + /// However, do we want to put it as a global variable? + poison_macros: FxHashSet, + + diagnostics: Vec, +} + +impl std::ops::Index for CrateDefMap { + type Output = ModuleData; + fn index(&self, id: CrateModuleId) -> &ModuleData { + &self.modules[id] + } +} + +#[derive(Default, Debug, PartialEq, Eq)] +pub struct ModuleData { + pub parent: Option, + pub children: FxHashMap, + pub scope: ModuleScope, + /// None for root + pub declaration: Option>, + /// None for inline modules. + /// + /// Note that non-inline modules, by definition, live inside non-macro file. + pub definition: Option, +} + +#[derive(Debug, Default, PartialEq, Eq, Clone)] +pub struct ModuleScope { + pub items: FxHashMap, + /// Macros visable in current module in legacy textual scope + /// + /// For macros invoked by an unquatified identifier like `bar!()`, `legacy_macros` will be searched in first. + /// If it yields no result, then it turns to module scoped `macros`. + /// It macros with name quatified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped, + /// and only normal scoped `macros` will be searched in. + /// + /// Note that this automatically inherit macros defined textually before the definition of module itself. + /// + /// Module scoped macros will be inserted into `items` instead of here. + // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will + // be all resolved to the last one defined if shadowing happens. + legacy_macros: FxHashMap, +} + +static BUILTIN_SCOPE: Lazy> = Lazy::new(|| { + BuiltinType::ALL + .iter() + .map(|(name, ty)| { + (name.clone(), Resolution { def: PerNs::types(ty.clone().into()), import: None }) + }) + .collect() +}); + +/// Legacy macros can only be accessed through special methods like `get_legacy_macros`. +/// Other methods will only resolve values, types and module scoped macros only. +impl ModuleScope { + pub fn entries<'a>(&'a self) -> impl Iterator + 'a { + //FIXME: shadowing + self.items.iter().chain(BUILTIN_SCOPE.iter()) + } + + /// Iterate over all module scoped macros + pub fn macros<'a>(&'a self) -> impl Iterator + 'a { + self.items + .iter() + .filter_map(|(name, res)| res.def.get_macros().map(|macro_| (name, macro_))) + } + + /// Iterate over all legacy textual scoped macros visable at the end of the module + pub fn legacy_macros<'a>(&'a self) -> impl Iterator + 'a { + self.legacy_macros.iter().map(|(name, def)| (name, *def)) + } + + /// Get a name from current module scope, legacy macros are not included + pub fn get(&self, name: &Name) -> Option<&Resolution> { + self.items.get(name).or_else(|| BUILTIN_SCOPE.get(name)) + } + + pub fn traits<'a>(&'a self) -> impl Iterator + 'a { + self.items.values().filter_map(|r| match r.def.take_types() { + Some(ModuleDefId::TraitId(t)) => Some(t), + _ => None, + }) + } + + fn get_legacy_macro(&self, name: &Name) -> Option { + self.legacy_macros.get(name).copied() + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct Resolution { + /// None for unresolved + pub def: PerNs, + /// ident by which this is imported into local scope. + pub import: Option, +} + +impl Resolution { + pub(crate) fn from_macro(macro_: MacroDefId) -> Self { + Resolution { def: PerNs::macros(macro_), import: None } + } +} + +#[derive(Debug, Clone)] +struct ResolvePathResult { + resolved_def: PerNs, + segment_index: Option, + reached_fixedpoint: ReachedFixedPoint, +} + +impl ResolvePathResult { + fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult { + ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None) + } + + fn with( + resolved_def: PerNs, + reached_fixedpoint: ReachedFixedPoint, + segment_index: Option, + ) -> ResolvePathResult { + ResolvePathResult { resolved_def, reached_fixedpoint, segment_index } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum ResolveMode { + Import, + Other, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum ReachedFixedPoint { + Yes, + No, +} + +impl CrateDefMap { + pub(crate) fn crate_def_map_query( + // Note that this doesn't have `+ AstDatabase`! + // This gurantess that `CrateDefMap` is stable across reparses. + db: &impl DefDatabase2, + krate: CrateId, + ) -> Arc { + let _p = profile("crate_def_map_query"); + let def_map = { + let crate_graph = db.crate_graph(); + let edition = crate_graph.edition(krate); + let mut modules: Arena = Arena::default(); + let root = modules.alloc(ModuleData::default()); + CrateDefMap { + krate, + edition, + extern_prelude: FxHashMap::default(), + prelude: None, + root, + modules, + poison_macros: FxHashSet::default(), + diagnostics: Vec::new(), + } + }; + let def_map = collector::collect_defs(db, def_map); + Arc::new(def_map) + } + + pub fn krate(&self) -> CrateId { + self.krate + } + + pub fn root(&self) -> CrateModuleId { + self.root + } + + pub fn prelude(&self) -> Option { + self.prelude + } + + pub fn extern_prelude(&self) -> &FxHashMap { + &self.extern_prelude + } + + pub fn add_diagnostics( + &self, + db: &impl DefDatabase2, + module: CrateModuleId, + sink: &mut DiagnosticSink, + ) { + self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink)) + } + + pub fn resolve_path( + &self, + db: &impl DefDatabase2, + original_module: CrateModuleId, + path: &Path, + ) -> (PerNs, Option) { + let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path); + (res.resolved_def, res.segment_index) + } + + // Returns Yes if we are sure that additions to `ItemMap` wouldn't change + // the result. + fn resolve_path_fp_with_macro( + &self, + db: &impl DefDatabase2, + mode: ResolveMode, + original_module: CrateModuleId, + path: &Path, + ) -> ResolvePathResult { + let mut segments = path.segments.iter().enumerate(); + let mut curr_per_ns: PerNs = match path.kind { + PathKind::DollarCrate(krate) => { + if krate == self.krate { + tested_by!(macro_dollar_crate_self); + PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into()) + } else { + let def_map = db.crate_def_map(krate); + let module = ModuleId { krate, module_id: def_map.root }; + tested_by!(macro_dollar_crate_other); + PerNs::types(module.into()) + } + } + PathKind::Crate => { + PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into()) + } + PathKind::Self_ => { + PerNs::types(ModuleId { krate: self.krate, module_id: original_module }.into()) + } + // plain import or absolute path in 2015: crate-relative with + // fallback to extern prelude (with the simplification in + // rust-lang/rust#57745) + // FIXME there must be a nicer way to write this condition + PathKind::Plain | PathKind::Abs + if self.edition == Edition::Edition2015 + && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => + { + let segment = match segments.next() { + Some((_, segment)) => segment, + None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), + }; + log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); + self.resolve_name_in_crate_root_or_extern_prelude(&segment.name) + } + PathKind::Plain => { + let segment = match segments.next() { + Some((_, segment)) => segment, + None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), + }; + log::debug!("resolving {:?} in module", segment); + self.resolve_name_in_module(db, original_module, &segment.name) + } + PathKind::Super => { + if let Some(p) = self.modules[original_module].parent { + PerNs::types(ModuleId { krate: self.krate, module_id: p }.into()) + } else { + log::debug!("super path in root module"); + return ResolvePathResult::empty(ReachedFixedPoint::Yes); + } + } + PathKind::Abs => { + // 2018-style absolute path -- only extern prelude + let segment = match segments.next() { + Some((_, segment)) => segment, + None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), + }; + if let Some(def) = self.extern_prelude.get(&segment.name) { + log::debug!("absolute path {:?} resolved to crate {:?}", path, def); + PerNs::types(*def) + } else { + return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude + } + } + PathKind::Type(_) => { + // This is handled in `infer::infer_path_expr` + // The result returned here does not matter + return ResolvePathResult::empty(ReachedFixedPoint::Yes); + } + }; + + for (i, segment) in segments { + let curr = match curr_per_ns.take_types() { + Some(r) => r, + None => { + // we still have path segments left, but the path so far + // didn't resolve in the types namespace => no resolution + // (don't break here because `curr_per_ns` might contain + // something in the value namespace, and it would be wrong + // to return that) + return ResolvePathResult::empty(ReachedFixedPoint::No); + } + }; + // resolve segment in curr + + curr_per_ns = match curr { + ModuleDefId::ModuleId(module) => { + if module.krate != self.krate { + let path = + Path { segments: path.segments[i..].to_vec(), kind: PathKind::Self_ }; + log::debug!("resolving {:?} in other crate", path); + let defp_map = db.crate_def_map(module.krate); + let (def, s) = defp_map.resolve_path(db, module.module_id, &path); + return ResolvePathResult::with( + def, + ReachedFixedPoint::Yes, + s.map(|s| s + i), + ); + } + + // Since it is a qualified path here, it should not contains legacy macros + match self[module.module_id].scope.get(&segment.name) { + Some(res) => res.def, + _ => { + log::debug!("path segment {:?} not found", segment.name); + return ResolvePathResult::empty(ReachedFixedPoint::No); + } + } + } + ModuleDefId::AdtId(AdtId::EnumId(e)) => { + // enum variant + tested_by!(can_import_enum_variant); + let enum_data = db.enum_data(e); + match enum_data.variant(&segment.name) { + Some(local_id) => { + let variant = EnumVariantId { parent: e, local_id }; + PerNs::both(variant.into(), variant.into()) + } + None => { + return ResolvePathResult::with( + PerNs::types(e.into()), + ReachedFixedPoint::Yes, + Some(i), + ); + } + } + } + s => { + // could be an inherent method call in UFCS form + // (`Struct::method`), or some other kind of associated item + log::debug!( + "path segment {:?} resolved to non-module {:?}, but is not last", + segment.name, + curr, + ); + + return ResolvePathResult::with( + PerNs::types(s), + ReachedFixedPoint::Yes, + Some(i), + ); + } + }; + } + ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None) + } + + fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs { + let from_crate_root = + self[self.root].scope.get(name).map_or_else(PerNs::none, |res| res.def); + let from_extern_prelude = self.resolve_name_in_extern_prelude(name); + + from_crate_root.or(from_extern_prelude) + } + + pub(crate) fn resolve_name_in_module( + &self, + db: &impl DefDatabase2, + module: CrateModuleId, + name: &Name, + ) -> PerNs { + // Resolve in: + // - legacy scope of macro + // - current module / scope + // - extern prelude + // - std prelude + let from_legacy_macro = + self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros); + let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.def); + let from_extern_prelude = + self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); + let from_prelude = self.resolve_in_prelude(db, name); + + from_legacy_macro.or(from_scope).or(from_extern_prelude).or(from_prelude) + } + + fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { + self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) + } + + fn resolve_in_prelude(&self, db: &impl DefDatabase2, name: &Name) -> PerNs { + if let Some(prelude) = self.prelude { + let keep; + let def_map = if prelude.krate == self.krate { + self + } else { + // Extend lifetime + keep = db.crate_def_map(prelude.krate); + &keep + }; + def_map[prelude.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def) + } else { + PerNs::none() + } + } +} + +mod diagnostics { + use hir_expand::diagnostics::DiagnosticSink; + use ra_db::RelativePathBuf; + use ra_syntax::{ast, AstPtr}; + + use crate::{db::DefDatabase2, diagnostics::UnresolvedModule, nameres::CrateModuleId, AstId}; + + #[derive(Debug, PartialEq, Eq)] + pub(super) enum DefDiagnostic { + UnresolvedModule { + module: CrateModuleId, + declaration: AstId, + candidate: RelativePathBuf, + }, + } + + impl DefDiagnostic { + pub(super) fn add_to( + &self, + db: &impl DefDatabase2, + target_module: CrateModuleId, + sink: &mut DiagnosticSink, + ) { + match self { + DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { + if *module != target_module { + return; + } + let decl = declaration.to_node(db); + sink.push(UnresolvedModule { + file: declaration.file_id(), + decl: AstPtr::new(&decl), + candidate: candidate.clone(), + }) + } + } + } + } +} diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs similarity index 86% rename from crates/ra_hir/src/nameres/collector.rs rename to crates/ra_hir_def/src/nameres/collector.rs index ee0a4c99fe00..3b61d9895bc3 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs @@ -1,45 +1,49 @@ //! FIXME: write short doc here -use hir_def::{ - attr::Attr, - nameres::{mod_resolution::ModDir, raw}, +use hir_expand::{ + name::{self, AsName, Name}, + HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind, }; -use hir_expand::name; use ra_cfg::CfgOptions; -use ra_db::FileId; +use ra_db::{CrateId, FileId}; use ra_syntax::{ast, SmolStr}; use rustc_hash::FxHashMap; use test_utils::tested_by; use crate::{ - db::DefDatabase, - ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind}, + attr::Attr, + db::DefDatabase2, nameres::{ - diagnostics::DefDiagnostic, Crate, CrateDefMap, CrateModuleId, ModuleData, ModuleDef, - PerNs, ReachedFixedPoint, Resolution, ResolveMode, + diagnostics::DefDiagnostic, mod_resolution::ModDir, per_ns::PerNs, raw, CrateDefMap, + ModuleData, ReachedFixedPoint, Resolution, ResolveMode, }, - Adt, AstId, Const, Enum, Function, HirFileId, MacroDef, Module, Name, Path, PathKind, Static, - Struct, Trait, TypeAlias, Union, + path::{Path, PathKind}, + AdtId, AstId, AstItemDef, ConstId, CrateModuleId, EnumId, EnumVariantId, FunctionId, + LocationCtx, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, }; -pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { +pub(super) fn collect_defs(db: &impl DefDatabase2, mut def_map: CrateDefMap) -> CrateDefMap { + let crate_graph = db.crate_graph(); + // populate external prelude - for dep in def_map.krate.dependencies(db) { - log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate); - if let Some(module) = dep.krate.root_module(db) { - def_map.extern_prelude.insert(dep.name.clone(), module.into()); - } + for dep in crate_graph.dependencies(def_map.krate) { + let dep_def_map = db.crate_def_map(dep.crate_id); + log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); + def_map.extern_prelude.insert( + dep.as_name(), + ModuleId { krate: dep.crate_id, module_id: dep_def_map.root }.into(), + ); + // look for the prelude if def_map.prelude.is_none() { - let map = db.crate_def_map(dep.krate); + let map = db.crate_def_map(dep.crate_id); if map.prelude.is_some() { def_map.prelude = map.prelude; } } } - let crate_graph = db.crate_graph(); - let cfg_options = crate_graph.cfg_options(def_map.krate().crate_id()); + let cfg_options = crate_graph.cfg_options(def_map.krate); let mut collector = DefCollector { db, @@ -101,11 +105,11 @@ struct DefCollector<'a, DB> { impl DefCollector<'_, DB> where - DB: DefDatabase, + DB: DefDatabase2, { fn collect(&mut self) { let crate_graph = self.db.crate_graph(); - let file_id = crate_graph.crate_root(self.def_map.krate.crate_id()); + let file_id = crate_graph.crate_root(self.def_map.krate); let raw_items = self.db.raw_items(file_id.into()); let module_id = self.def_map.root; self.def_map.modules[module_id].definition = Some(file_id); @@ -168,7 +172,7 @@ where &mut self, module_id: CrateModuleId, name: Name, - macro_: MacroDef, + macro_: MacroDefId, export: bool, ) { // Textual scoping @@ -189,7 +193,7 @@ where /// the definition of current module. /// And also, `macro_use` on a module will import all legacy macros visable inside to /// current legacy scope, with possible shadowing. - fn define_legacy_macro(&mut self, module_id: CrateModuleId, name: Name, macro_: MacroDef) { + fn define_legacy_macro(&mut self, module_id: CrateModuleId, name: Name, macro_: MacroDefId) { // Always shadowing self.def_map.modules[module_id].scope.legacy_macros.insert(name, macro_); } @@ -213,9 +217,9 @@ where .expect("extern crate should have been desugared to one-element path"), ); - if let Some(ModuleDef::Module(m)) = res.take_types() { + if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use); - self.import_all_macros_exported(current_module_id, m.krate()); + self.import_all_macros_exported(current_module_id, m.krate); } } @@ -224,7 +228,7 @@ where /// Exported macros are just all macros in the root module scope. /// Note that it contains not only all `#[macro_export]` macros, but also all aliases /// created by `use` in the root module, ignoring the visibility of `use`. - fn import_all_macros_exported(&mut self, current_module_id: CrateModuleId, krate: Crate) { + fn import_all_macros_exported(&mut self, current_module_id: CrateModuleId, krate: CrateId) { let def_map = self.db.crate_def_map(krate); for (name, def) in def_map[def_map.root].scope.macros() { // `macro_use` only bring things into legacy scope. @@ -288,15 +292,15 @@ where if import.is_glob { log::debug!("glob import: {:?}", import); match def.take_types() { - Some(ModuleDef::Module(m)) => { + Some(ModuleDefId::ModuleId(m)) => { if import.is_prelude { tested_by!(std_prelude); self.def_map.prelude = Some(m); - } else if m.krate() != self.def_map.krate { + } else if m.krate != self.def_map.krate { tested_by!(glob_across_crates); // glob import from other crate => we can just import everything once - let item_map = self.db.crate_def_map(m.krate()); - let scope = &item_map[m.id.module_id].scope; + let item_map = self.db.crate_def_map(m.krate); + let scope = &item_map[m.module_id].scope; // Module scoped macros is included let items = scope @@ -310,7 +314,7 @@ where // glob import from same crate => we do an initial // import, and then need to propagate any further // additions - let scope = &self.def_map[m.id.module_id].scope; + let scope = &self.def_map[m.module_id].scope; // Module scoped macros is included let items = scope @@ -322,23 +326,25 @@ where self.update(module_id, Some(import_id), &items); // record the glob import in case we add further items self.glob_imports - .entry(m.id.module_id) + .entry(m.module_id) .or_default() .push((module_id, import_id)); } } - Some(ModuleDef::Adt(Adt::Enum(e))) => { + Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { tested_by!(glob_enum); // glob import from enum => just import all the variants - let variants = e.variants(self.db); - let resolutions = variants - .into_iter() - .filter_map(|variant| { + let enum_data = self.db.enum_data(e); + let resolutions = enum_data + .variants + .iter() + .filter_map(|(local_id, variant_data)| { + let name = variant_data.name.clone()?; + let variant = EnumVariantId { parent: e, local_id }; let res = Resolution { def: PerNs::both(variant.into(), variant.into()), import: Some(import_id), }; - let name = variant.name(self.db)?; Some((name, res)) }) .collect::>(); @@ -451,8 +457,8 @@ where ); if let Some(def) = resolved_res.resolved_def.get_macros() { - let call_id = self.db.intern_macro(MacroCallLoc { def: def.id, ast_id: *ast_id }); - resolved.push((*module_id, call_id, def.id)); + let call_id = self.db.intern_macro(MacroCallLoc { def, ast_id: *ast_id }); + resolved.push((*module_id, call_id, def)); res = ReachedFixedPoint::No; return false; } @@ -517,7 +523,7 @@ struct ModCollector<'a, D> { impl ModCollector<'_, &'_ mut DefCollector<'_, DB>> where - DB: DefDatabase, + DB: DefDatabase2, { fn collect(&mut self, items: &[raw::RawItem]) { // Note: don't assert that inserted value is fresh: it's simply not true @@ -526,10 +532,9 @@ where // Prelude module is always considered to be `#[macro_use]`. if let Some(prelude_module) = self.def_collector.def_map.prelude { - if prelude_module.krate() != self.def_collector.def_map.krate { + if prelude_module.krate != self.def_collector.def_map.krate { tested_by!(prelude_is_macro_use); - self.def_collector - .import_all_macros_exported(self.module_id, prelude_module.krate()); + self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); } } @@ -635,7 +640,9 @@ where modules[res].scope.legacy_macros = modules[self.module_id].scope.legacy_macros.clone(); modules[self.module_id].children.insert(name.clone(), res); let resolution = Resolution { - def: PerNs::types(Module::new(self.def_collector.def_map.krate, res).into()), + def: PerNs::types( + ModuleId { krate: self.def_collector.def_map.krate, module_id: res }.into(), + ), import: None, }; self.def_collector.update(self.module_id, None, &[(name, resolution)]); @@ -643,30 +650,32 @@ where } fn define_def(&mut self, def: &raw::DefData) { - let module = Module::new(self.def_collector.def_map.krate, self.module_id); - let ctx = LocationCtx::new(self.def_collector.db, module.id, self.file_id); + let module = + ModuleId { krate: self.def_collector.def_map.krate, module_id: self.module_id }; + let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id); - macro_rules! def { - ($kind:ident, $ast_id:ident) => { - $kind { id: AstItemDef::from_ast_id(ctx, $ast_id) }.into() - }; - } let name = def.name.clone(); let def: PerNs = match def.kind { - raw::DefKind::Function(ast_id) => PerNs::values(def!(Function, ast_id)), + raw::DefKind::Function(ast_id) => { + PerNs::values(FunctionId::from_ast_id(ctx, ast_id).into()) + } raw::DefKind::Struct(ast_id) => { - let s = def!(Struct, ast_id); + let s = StructId::from_ast_id(ctx, ast_id).into(); PerNs::both(s, s) } raw::DefKind::Union(ast_id) => { - let s = def!(Union, ast_id); + let s = UnionId::from_ast_id(ctx, ast_id).into(); PerNs::both(s, s) } - raw::DefKind::Enum(ast_id) => PerNs::types(def!(Enum, ast_id)), - raw::DefKind::Const(ast_id) => PerNs::values(def!(Const, ast_id)), - raw::DefKind::Static(ast_id) => PerNs::values(def!(Static, ast_id)), - raw::DefKind::Trait(ast_id) => PerNs::types(def!(Trait, ast_id)), - raw::DefKind::TypeAlias(ast_id) => PerNs::types(def!(TypeAlias, ast_id)), + raw::DefKind::Enum(ast_id) => PerNs::types(EnumId::from_ast_id(ctx, ast_id).into()), + raw::DefKind::Const(ast_id) => PerNs::values(ConstId::from_ast_id(ctx, ast_id).into()), + raw::DefKind::Static(ast_id) => { + PerNs::values(StaticId::from_ast_id(ctx, ast_id).into()) + } + raw::DefKind::Trait(ast_id) => PerNs::types(TraitId::from_ast_id(ctx, ast_id).into()), + raw::DefKind::TypeAlias(ast_id) => { + PerNs::types(TypeAliasId::from_ast_id(ctx, ast_id).into()) + } }; let resolution = Resolution { def, import: None }; self.def_collector.update(self.module_id, None, &[(name, resolution)]) @@ -678,10 +687,8 @@ where // Case 1: macro rules, define a macro in crate-global mutable scope if is_macro_rules(&mac.path) { if let Some(name) = &mac.name { - let macro_id = - MacroDefId { ast_id, krate: self.def_collector.def_map.krate.crate_id }; - let macro_ = MacroDef { id: macro_id }; - self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export); + let macro_id = MacroDefId { ast_id, krate: self.def_collector.def_map.krate }; + self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export); } return; } @@ -691,10 +698,10 @@ where if let Some(macro_def) = mac.path.as_ident().and_then(|name| { self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) }) { - let def = macro_def.id; - let macro_call_id = self.def_collector.db.intern_macro(MacroCallLoc { def, ast_id }); + let macro_call_id = + self.def_collector.db.intern_macro(MacroCallLoc { def: macro_def, ast_id }); - self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def); + self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_def); return; } @@ -733,15 +740,16 @@ fn is_macro_rules(path: &Path) -> bool { #[cfg(test)] mod tests { - use ra_db::SourceDatabase; - - use super::*; - use crate::{db::DefDatabase, mock::MockDatabase, Crate}; use ra_arena::Arena; + use ra_db::{fixture::WithFixture, SourceDatabase}; use rustc_hash::FxHashSet; + use crate::{db::DefDatabase2, test_db::TestDB}; + + use super::*; + fn do_collect_defs( - db: &impl DefDatabase, + db: &impl DefDatabase2, def_map: CrateDefMap, monitor: MacroStackMonitor, ) -> CrateDefMap { @@ -760,12 +768,11 @@ mod tests { } fn do_limited_resolve(code: &str, limit: u32, poison_limit: u32) -> CrateDefMap { - let (db, _source_root, _) = MockDatabase::with_single_file(&code); - let crate_id = db.crate_graph().iter().next().unwrap(); - let krate = Crate { crate_id }; + let (db, _file_id) = TestDB::with_single_file(&code); + let krate = db.crate_graph().iter().next().unwrap(); let def_map = { - let edition = krate.edition(&db); + let edition = db.crate_graph().edition(krate); let mut modules: Arena = Arena::default(); let root = modules.alloc(ModuleData::default()); CrateDefMap { diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs index 7d7e2779aa20..f6b0b8fb1ec6 100644 --- a/crates/ra_hir_def/src/nameres/mod_resolution.rs +++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs @@ -1,8 +1,7 @@ //! This module resolves `mod foo;` declaration to file. use hir_expand::name::Name; -use ra_db::FileId; +use ra_db::{FileId, RelativePathBuf}; use ra_syntax::SmolStr; -use relative_path::RelativePathBuf; use crate::{db::DefDatabase2, HirFileId}; diff --git a/crates/ra_hir/src/nameres/per_ns.rs b/crates/ra_hir_def/src/nameres/per_ns.rs similarity index 75% rename from crates/ra_hir/src/nameres/per_ns.rs rename to crates/ra_hir_def/src/nameres/per_ns.rs index 0da6789de609..298b0b0c724b 100644 --- a/crates/ra_hir/src/nameres/per_ns.rs +++ b/crates/ra_hir_def/src/nameres/per_ns.rs @@ -1,6 +1,8 @@ //! FIXME: write short doc here -use crate::{MacroDef, ModuleDef}; +use hir_expand::MacroDefId; + +use crate::ModuleDefId; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Namespace { @@ -12,11 +14,11 @@ pub enum Namespace { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct PerNs { - pub types: Option, - pub values: Option, + pub types: Option, + pub values: Option, /// Since macros has different type, many methods simply ignore it. /// We can only use special method like `get_macros` to access it. - pub macros: Option, + pub macros: Option, } impl Default for PerNs { @@ -30,19 +32,19 @@ impl PerNs { PerNs { types: None, values: None, macros: None } } - pub fn values(t: ModuleDef) -> PerNs { + pub fn values(t: ModuleDefId) -> PerNs { PerNs { types: None, values: Some(t), macros: None } } - pub fn types(t: ModuleDef) -> PerNs { + pub fn types(t: ModuleDefId) -> PerNs { PerNs { types: Some(t), values: None, macros: None } } - pub fn both(types: ModuleDef, values: ModuleDef) -> PerNs { + pub fn both(types: ModuleDefId, values: ModuleDefId) -> PerNs { PerNs { types: Some(types), values: Some(values), macros: None } } - pub fn macros(macro_: MacroDef) -> PerNs { + pub fn macros(macro_: MacroDefId) -> PerNs { PerNs { types: None, values: None, macros: Some(macro_) } } @@ -54,15 +56,15 @@ impl PerNs { self.types.is_some() && self.values.is_some() && self.macros.is_some() } - pub fn take_types(self) -> Option { + pub fn take_types(self) -> Option { self.types } - pub fn take_values(self) -> Option { + pub fn take_values(self) -> Option { self.values } - pub fn get_macros(&self) -> Option { + pub fn get_macros(&self) -> Option { self.macros } diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs index 86c05d6028e7..cb47fa317d2d 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs @@ -14,6 +14,7 @@ use ra_syntax::{ ast::{self, AttrsOwner, NameOwner}, AstNode, AstPtr, SourceFile, }; +use test_utils::tested_by; use crate::{attr::Attr, db::DefDatabase2, path::Path, FileAstId, HirFileId, ModuleSource, Source}; @@ -297,8 +298,7 @@ impl RawItemsCollector { self.push_item(current_module, attrs, RawItemKind::Module(item)); return; } - // FIXME: restore this mark once we complete hir splitting - // tested_by!(name_res_works_for_broken_modules); + tested_by!(name_res_works_for_broken_modules); } fn add_use_item(&mut self, current_module: Option, use_item: ast::UseItem) { diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs similarity index 73% rename from crates/ra_hir/src/nameres/tests.rs rename to crates/ra_hir_def/src/nameres/tests.rs index 8c6b40aaf2a0..52bd0aa91e8c 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir_def/src/nameres/tests.rs @@ -1,35 +1,31 @@ -mod macros; mod globs; mod incremental; -mod primitives; +mod macros; mod mod_resolution; +mod primitives; use std::sync::Arc; use insta::assert_snapshot; -use ra_db::SourceDatabase; +use ra_db::{fixture::WithFixture, SourceDatabase}; use test_utils::covers; -use crate::{ - mock::{CrateGraphFixture, MockDatabase}, - Crate, -}; +use crate::{db::DefDatabase2, nameres::*, test_db::TestDB, CrateModuleId}; -use super::*; +fn def_map(fixtute: &str) -> String { + let dm = compute_crate_def_map(fixtute); + render_crate_def_map(&dm) +} -fn compute_crate_def_map(fixture: &str, graph: Option) -> Arc { - let mut db = MockDatabase::with_files(fixture); - if let Some(graph) = graph { - db.set_crate_graph_from_fixture(graph); - } - let crate_id = db.crate_graph().iter().next().unwrap(); - let krate = Crate { crate_id }; +fn compute_crate_def_map(fixture: &str) -> Arc { + let db = TestDB::with_files(fixture); + let krate = db.crate_graph().iter().next().unwrap(); db.crate_def_map(krate) } fn render_crate_def_map(map: &CrateDefMap) -> String { let mut buf = String::new(); - go(&mut buf, map, "\ncrate", map.root); + go(&mut buf, map, "\ncrate", map.root()); return buf.trim().to_string(); fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: CrateModuleId) { @@ -70,16 +66,6 @@ fn render_crate_def_map(map: &CrateDefMap) -> String { } } -fn def_map(fixtute: &str) -> String { - let dm = compute_crate_def_map(fixtute, None); - render_crate_def_map(&dm) -} - -fn def_map_with_crate_graph(fixture: &str, graph: CrateGraphFixture) -> String { - let dm = compute_crate_def_map(fixture, Some(graph)); - render_crate_def_map(&dm) -} - #[test] fn crate_def_map_smoke_test() { let map = def_map( @@ -234,12 +220,12 @@ fn re_exports() { #[test] fn std_prelude() { covers!(std_prelude); - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:test_crate use Foo::*; - //- /lib.rs + //- /lib.rs crate:test_crate mod prelude; #[prelude_import] use prelude::*; @@ -247,10 +233,6 @@ fn std_prelude() { //- /prelude.rs pub enum Foo { Bar, Baz }; ", - crate_graph! { - "main": ("/main.rs", ["test_crate"]), - "test_crate": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" ⋮crate @@ -279,9 +261,9 @@ fn can_import_enum_variant() { #[test] fn edition_2015_imports() { - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:other_crate edition:2015 mod foo; mod bar; @@ -292,13 +274,9 @@ fn edition_2015_imports() { use bar::Bar; use other_crate::FromLib; - //- /lib.rs + //- /lib.rs crate:other_crate edition:2018 struct FromLib; ", - crate_graph! { - "main": ("/main.rs", "2015", ["other_crate"]), - "other_crate": ("/lib.rs", "2018", []), - }, ); assert_snapshot!(map, @r###" @@ -343,18 +321,14 @@ fn item_map_using_self() { #[test] fn item_map_across_crates() { - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:test_crate use test_crate::Baz; - //- /lib.rs + //- /lib.rs crate:test_crate pub struct Baz; ", - crate_graph! { - "main": ("/main.rs", ["test_crate"]), - "test_crate": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" @@ -365,9 +339,9 @@ fn item_map_across_crates() { #[test] fn extern_crate_rename() { - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:alloc extern crate alloc as alloc_crate; mod alloc; @@ -376,13 +350,9 @@ fn extern_crate_rename() { //- /sync.rs use alloc_crate::Arc; - //- /lib.rs + //- /lib.rs crate:alloc struct Arc; ", - crate_graph! { - "main": ("/main.rs", ["alloc"]), - "alloc": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" @@ -397,9 +367,9 @@ fn extern_crate_rename() { #[test] fn extern_crate_rename_2015_edition() { - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:alloc edition:2015 extern crate alloc as alloc_crate; mod alloc; @@ -408,13 +378,9 @@ fn extern_crate_rename_2015_edition() { //- /sync.rs use alloc_crate::Arc; - //- /lib.rs + //- /lib.rs crate:alloc struct Arc; ", - crate_graph! { - "main": ("/main.rs", "2015", ["alloc"]), - "alloc": ("/lib.rs", []), - }, ); assert_snapshot!(map, @@ -431,24 +397,21 @@ fn extern_crate_rename_2015_edition() { #[test] fn import_across_source_roots() { - let map = def_map_with_crate_graph( + let map = def_map( " - //- /lib.rs + //- /main.rs crate:main deps:test_crate + use test_crate::a::b::C; + + //- root /test_crate/ + + //- /test_crate/lib.rs crate:test_crate pub mod a { pub mod b { pub struct C; } } - //- root /main/ - - //- /main/main.rs - use test_crate::a::b::C; ", - crate_graph! { - "main": ("/main/main.rs", ["test_crate"]), - "test_crate": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" @@ -459,12 +422,12 @@ fn import_across_source_roots() { #[test] fn reexport_across_crates() { - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:test_crate use test_crate::Baz; - //- /lib.rs + //- /lib.rs crate:test_crate pub use foo::Baz; mod foo; @@ -472,10 +435,6 @@ fn reexport_across_crates() { //- /foo.rs pub struct Baz; ", - crate_graph! { - "main": ("/main.rs", ["test_crate"]), - "test_crate": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" @@ -486,19 +445,15 @@ fn reexport_across_crates() { #[test] fn values_dont_shadow_extern_crates() { - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:foo fn foo() {} use foo::Bar; - //- /foo/lib.rs + //- /foo/lib.rs crate:foo pub struct Bar; ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/foo/lib.rs", []), - }, ); assert_snapshot!(map, @r###" @@ -510,11 +465,12 @@ fn values_dont_shadow_extern_crates() { #[test] fn cfg_not_test() { - let map = def_map_with_crate_graph( + let map = def_map( r#" - //- /main.rs + //- /main.rs crate:main deps:std use {Foo, Bar, Baz}; - //- /lib.rs + + //- /lib.rs crate:std #[prelude_import] pub use self::prelude::*; mod prelude { @@ -526,10 +482,6 @@ fn cfg_not_test() { pub struct Baz; } "#, - crate_graph! { - "main": ("/main.rs", ["std"]), - "std": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" @@ -542,11 +494,12 @@ fn cfg_not_test() { #[test] fn cfg_test() { - let map = def_map_with_crate_graph( + let map = def_map( r#" - //- /main.rs + //- /main.rs crate:main deps:std use {Foo, Bar, Baz}; - //- /lib.rs + + //- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42 #[prelude_import] pub use self::prelude::*; mod prelude { @@ -558,15 +511,6 @@ fn cfg_test() { pub struct Baz; } "#, - crate_graph! { - "main": ("/main.rs", ["std"]), - "std": ("/lib.rs", [], cfg = { - "test", - "feature" = "foo", - "feature" = "bar", - "opt" = "42", - }), - }, ); assert_snapshot!(map, @r###" diff --git a/crates/ra_hir/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs similarity index 88% rename from crates/ra_hir/src/nameres/tests/globs.rs rename to crates/ra_hir_def/src/nameres/tests/globs.rs index 7ac22b47b808..5b03fe36504a 100644 --- a/crates/ra_hir/src/nameres/tests/globs.rs +++ b/crates/ra_hir_def/src/nameres/tests/globs.rs @@ -76,18 +76,14 @@ fn glob_2() { #[test] fn glob_across_crates() { covers!(glob_across_crates); - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:test_crate use test_crate::*; - //- /lib.rs + //- /lib.rs crate:test_crate pub struct Baz; ", - crate_graph! { - "main": ("/main.rs", ["test_crate"]), - "test_crate": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" ⋮crate diff --git a/crates/ra_hir/src/nameres/tests/incremental.rs b/crates/ra_hir_def/src/nameres/tests/incremental.rs similarity index 74% rename from crates/ra_hir/src/nameres/tests/incremental.rs rename to crates/ra_hir_def/src/nameres/tests/incremental.rs index af9c39760268..80dcec62f0f9 100644 --- a/crates/ra_hir/src/nameres/tests/incremental.rs +++ b/crates/ra_hir_def/src/nameres/tests/incremental.rs @@ -1,13 +1,12 @@ -use super::*; - use std::sync::Arc; use ra_db::{SourceDatabase, SourceDatabaseExt}; +use super::*; + fn check_def_map_is_not_recomputed(initial: &str, file_change: &str) { - let (mut db, pos) = MockDatabase::with_position(initial); - let crate_id = db.crate_graph().iter().next().unwrap(); - let krate = Crate { crate_id }; + let (mut db, pos) = TestDB::with_position(initial); + let krate = db.crate_graph().iter().next().unwrap(); { let events = db.log_executed(|| { db.crate_def_map(krate); @@ -92,7 +91,7 @@ fn adding_inner_items_should_not_invalidate_def_map() { #[test] fn typing_inside_a_macro_should_not_invalidate_def_map() { - let (mut db, pos) = MockDatabase::with_position( + let (mut db, pos) = TestDB::with_position( " //- /lib.rs macro_rules! m { @@ -112,15 +111,12 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() { m!(X); ", ); + let krate = db.crate_graph().iter().next().unwrap(); { let events = db.log_executed(|| { - let src = crate::Source { - file_id: pos.file_id.into(), - ast: crate::ModuleSource::new(&db, Some(pos.file_id), None), - }; - let module = crate::Module::from_definition(&db, src).unwrap(); - let decls = module.declarations(&db); - assert_eq!(decls.len(), 18); + let crate_def_map = db.crate_def_map(krate); + let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); + assert_eq!(module_data.scope.items.len(), 1); }); assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) } @@ -128,13 +124,9 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() { { let events = db.log_executed(|| { - let src = crate::Source { - file_id: pos.file_id.into(), - ast: crate::ModuleSource::new(&db, Some(pos.file_id), None), - }; - let module = crate::Module::from_definition(&db, src).unwrap(); - let decls = module.declarations(&db); - assert_eq!(decls.len(), 18); + let crate_def_map = db.crate_def_map(krate); + let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); + assert_eq!(module_data.scope.items.len(), 1); }); assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) } diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs similarity index 86% rename from crates/ra_hir/src/nameres/tests/macros.rs rename to crates/ra_hir_def/src/nameres/tests/macros.rs index 4f52ad2c543a..704065633a5f 100644 --- a/crates/ra_hir/src/nameres/tests/macros.rs +++ b/crates/ra_hir_def/src/nameres/tests/macros.rs @@ -71,16 +71,16 @@ fn macro_rules_can_define_modules() { #[test] fn macro_rules_from_other_crates_are_visible() { - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:foo foo::structs!(Foo, Bar) mod bar; //- /bar.rs use crate::*; - //- /lib.rs + //- /lib.rs crate:foo #[macro_export] macro_rules! structs { ($($i:ident),*) => { @@ -88,10 +88,6 @@ fn macro_rules_from_other_crates_are_visible() { } } ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" ⋮crate @@ -108,16 +104,16 @@ fn macro_rules_from_other_crates_are_visible() { #[test] fn macro_rules_export_with_local_inner_macros_are_visible() { - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:foo foo::structs!(Foo, Bar) mod bar; //- /bar.rs use crate::*; - //- /lib.rs + //- /lib.rs crate:foo #[macro_export(local_inner_macros)] macro_rules! structs { ($($i:ident),*) => { @@ -125,10 +121,6 @@ fn macro_rules_export_with_local_inner_macros_are_visible() { } } ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" ⋮crate @@ -145,9 +137,9 @@ fn macro_rules_export_with_local_inner_macros_are_visible() { #[test] fn unexpanded_macro_should_expand_by_fixedpoint_loop() { - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:foo macro_rules! baz { () => { use foo::bar; @@ -158,7 +150,7 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() { bar!(); baz!(); - //- /lib.rs + //- /lib.rs crate:foo #[macro_export] macro_rules! foo { () => { @@ -172,10 +164,6 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() { } } ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" ⋮crate @@ -188,9 +176,9 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() { #[test] fn macro_rules_from_other_crates_are_visible_with_macro_use() { covers!(macro_rules_from_other_crates_are_visible_with_macro_use); - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:foo structs!(Foo); structs_priv!(Bar); structs_not_exported!(MacroNotResolved1); @@ -205,7 +193,7 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() { structs!(Baz); crate::structs!(MacroNotResolved3); - //- /lib.rs + //- /lib.rs crate:foo #[macro_export] macro_rules! structs { ($i:ident) => { struct $i; } @@ -222,10 +210,6 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() { } } ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" ⋮crate @@ -242,9 +226,9 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() { #[test] fn prelude_is_macro_use() { covers!(prelude_is_macro_use); - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:foo structs!(Foo); structs_priv!(Bar); structs_outside!(Out); @@ -256,7 +240,7 @@ fn prelude_is_macro_use() { structs!(Baz); crate::structs!(MacroNotResolved3); - //- /lib.rs + //- /lib.rs crate:foo #[prelude_import] use self::prelude::*; @@ -279,10 +263,6 @@ fn prelude_is_macro_use() { ($i:ident) => { struct $i; } } ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" ⋮crate @@ -447,16 +427,16 @@ fn type_value_macro_live_in_different_scopes() { #[test] fn macro_use_can_be_aliased() { - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:foo #[macro_use] extern crate foo; foo!(Direct); bar!(Alias); - //- /lib.rs + //- /lib.rs crate:foo use crate::foo as bar; mod m { @@ -466,10 +446,6 @@ fn macro_use_can_be_aliased() { } } ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" ⋮crate @@ -533,9 +509,9 @@ fn path_qualified_macros() { fn macro_dollar_crate_is_correct_in_item() { covers!(macro_dollar_crate_self); covers!(macro_dollar_crate_other); - let map = def_map_with_crate_graph( + let map = def_map( " - //- /main.rs + //- /main.rs crate:main deps:foo #[macro_use] extern crate foo; @@ -554,7 +530,7 @@ fn macro_dollar_crate_is_correct_in_item() { not_current1!(); foo::not_current2!(); - //- /lib.rs + //- /lib.rs crate:foo mod m { #[macro_export] macro_rules! not_current1 { @@ -574,10 +550,6 @@ fn macro_dollar_crate_is_correct_in_item() { struct Bar; struct Baz; ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, ); assert_snapshot!(map, @r###" ⋮crate @@ -596,12 +568,12 @@ fn macro_dollar_crate_is_correct_in_item() { fn macro_dollar_crate_is_correct_in_indirect_deps() { covers!(macro_dollar_crate_other); // From std - let map = def_map_with_crate_graph( + let map = def_map( r#" - //- /main.rs + //- /main.rs crate:main deps:std foo!(); - //- /std.rs + //- /std.rs crate:std deps:core #[prelude_import] use self::prelude::*; @@ -612,7 +584,7 @@ fn macro_dollar_crate_is_correct_in_indirect_deps() { #[macro_use] mod std_macros; - //- /core.rs + //- /core.rs crate:core #[macro_export] macro_rules! foo { () => { @@ -622,11 +594,6 @@ fn macro_dollar_crate_is_correct_in_indirect_deps() { pub struct bar; "#, - crate_graph! { - "main": ("/main.rs", ["std"]), - "std": ("/std.rs", ["core"]), - "core": ("/core.rs", []), - }, ); assert_snapshot!(map, @r###" ⋮crate diff --git a/crates/ra_hir/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs similarity index 93% rename from crates/ra_hir/src/nameres/tests/mod_resolution.rs rename to crates/ra_hir_def/src/nameres/tests/mod_resolution.rs index abfe8b1c346e..dee364a1422f 100644 --- a/crates/ra_hir/src/nameres/tests/mod_resolution.rs +++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs @@ -2,7 +2,7 @@ use super::*; #[test] fn name_res_works_for_broken_modules() { - // covers!(name_res_works_for_broken_modules); + covers!(name_res_works_for_broken_modules); let map = def_map( " //- /lib.rs @@ -54,18 +54,15 @@ fn nested_module_resolution() { #[test] fn module_resolution_works_for_non_standard_filenames() { - let map = def_map_with_crate_graph( + let map = def_map( " - //- /my_library.rs + //- /my_library.rs crate:my_library mod foo; use self::foo::Bar; //- /foo/mod.rs pub struct Bar; ", - crate_graph! { - "my_library": ("/my_library.rs", []), - }, ); assert_snapshot!(map, @r###" @@ -650,7 +647,7 @@ fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() { #[test] fn unresolved_module_diagnostics() { - let diagnostics = MockDatabase::with_files( + let db = TestDB::with_files( r" //- /lib.rs mod foo; @@ -658,11 +655,37 @@ fn unresolved_module_diagnostics() { mod baz {} //- /foo.rs ", - ) - .diagnostics(); + ); + let krate = db.crate_graph().iter().next().unwrap(); - assert_snapshot!(diagnostics, @r###" - "mod bar;": unresolved module + let crate_def_map = db.crate_def_map(krate); + + insta::assert_debug_snapshot!( + crate_def_map.diagnostics, + @r###" + [ + UnresolvedModule { + module: CrateModuleId( + 0, + ), + declaration: AstId { + file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + file_ast_id: FileAstId { + raw: ErasedFileAstId( + 1, + ), + _ty: PhantomData, + }, + }, + candidate: "bar.rs", + }, + ] "### ); } diff --git a/crates/ra_hir/src/nameres/tests/primitives.rs b/crates/ra_hir_def/src/nameres/tests/primitives.rs similarity index 100% rename from crates/ra_hir/src/nameres/tests/primitives.rs rename to crates/ra_hir_def/src/nameres/tests/primitives.rs diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs new file mode 100644 index 000000000000..8ee8e40d0814 --- /dev/null +++ b/crates/ra_hir_def/src/test_db.rs @@ -0,0 +1,75 @@ +//! Database used for testing `hir_def`. + +use std::{ + panic, + sync::{Arc, Mutex}, +}; + +use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath}; + +#[salsa::database( + ra_db::SourceDatabaseExtStorage, + ra_db::SourceDatabaseStorage, + hir_expand::db::AstDatabaseStorage, + crate::db::InternDatabaseStorage, + crate::db::DefDatabase2Storage +)] +#[derive(Debug, Default)] +pub struct TestDB { + runtime: salsa::Runtime, + events: Mutex>>>, +} + +impl salsa::Database for TestDB { + fn salsa_runtime(&self) -> &salsa::Runtime { + &self.runtime + } + + fn salsa_event(&self, event: impl Fn() -> salsa::Event) { + let mut events = self.events.lock().unwrap(); + if let Some(events) = &mut *events { + events.push(event()); + } + } +} + +impl panic::RefUnwindSafe for TestDB {} + +impl FileLoader for TestDB { + fn file_text(&self, file_id: FileId) -> Arc { + FileLoaderDelegate(self).file_text(file_id) + } + fn resolve_relative_path( + &self, + anchor: FileId, + relative_path: &RelativePath, + ) -> Option { + FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) + } + fn relevant_crates(&self, file_id: FileId) -> Arc> { + FileLoaderDelegate(self).relevant_crates(file_id) + } +} + +impl TestDB { + pub fn log(&self, f: impl FnOnce()) -> Vec> { + *self.events.lock().unwrap() = Some(Vec::new()); + f(); + self.events.lock().unwrap().take().unwrap() + } + + pub fn log_executed(&self, f: impl FnOnce()) -> Vec { + let events = self.log(f); + events + .into_iter() + .filter_map(|e| match e.kind { + // This pretty horrible, but `Debug` is the only way to inspect + // QueryDescriptor at the moment. + salsa::EventKind::WillExecute { database_key } => { + Some(format!("{:?}", database_key)) + } + _ => None, + }) + .collect() + } +} diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs new file mode 100644 index 000000000000..201884b95745 --- /dev/null +++ b/crates/ra_hir_expand/src/diagnostics.rs @@ -0,0 +1,85 @@ +//! Semantic errors and warnings. +//! +//! The `Diagnostic` trait defines a trait object which can represent any +//! diagnostic. +//! +//! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating +//! a `DiagnosticSink`, you supply a callback which can react to a `dyn +//! Diagnostic` or to any concrete diagnostic (downcasting is sued internally). +//! +//! Because diagnostics store file offsets, it's a bad idea to store them +//! directly in salsa. For this reason, every hir subsytem defines it's own +//! strongly-typed closed set of diagnostics which use hir ids internally, are +//! stored in salsa and do *not* implement the `Diagnostic` trait. Instead, a +//! subsystem provides a separate, non-query-based API which can walk all stored +//! values and transform them into instances of `Diagnostic`. + +use std::{any::Any, fmt}; + +use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange}; + +use crate::{db::AstDatabase, Source}; + +pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { + fn message(&self) -> String; + fn source(&self) -> Source; + fn highlight_range(&self) -> TextRange { + self.source().ast.range() + } + fn as_any(&self) -> &(dyn Any + Send + 'static); +} + +pub trait AstDiagnostic { + type AST; + fn ast(&self, db: &impl AstDatabase) -> Self::AST; +} + +impl dyn Diagnostic { + pub fn syntax_node(&self, db: &impl AstDatabase) -> SyntaxNode { + let node = db.parse_or_expand(self.source().file_id).unwrap(); + self.source().ast.to_node(&node) + } + + pub fn downcast_ref(&self) -> Option<&D> { + self.as_any().downcast_ref() + } +} + +pub struct DiagnosticSink<'a> { + callbacks: Vec Result<(), ()> + 'a>>, + default_callback: Box, +} + +impl<'a> DiagnosticSink<'a> { + /// FIXME: split `new` and `on` into a separate builder type + pub fn new(cb: impl FnMut(&dyn Diagnostic) + 'a) -> DiagnosticSink<'a> { + DiagnosticSink { callbacks: Vec::new(), default_callback: Box::new(cb) } + } + + pub fn on(mut self, mut cb: F) -> DiagnosticSink<'a> { + let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::() { + Some(d) => { + cb(d); + Ok(()) + } + None => Err(()), + }; + self.callbacks.push(Box::new(cb)); + self + } + + pub fn push(&mut self, d: impl Diagnostic) { + let d: &dyn Diagnostic = &d; + self._push(d); + } + + fn _push(&mut self, d: &dyn Diagnostic) { + for cb in self.callbacks.iter_mut() { + match cb(d) { + Ok(()) => return, + Err(()) => (), + } + } + (self.default_callback)(d) + } +} diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 5a0e5a19c533..dd07a16b4cad 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs @@ -9,11 +9,15 @@ pub mod ast_id_map; pub mod either; pub mod name; pub mod hygiene; +pub mod diagnostics; use std::hash::{Hash, Hasher}; use ra_db::{salsa, CrateId, FileId}; -use ra_syntax::ast::{self, AstNode}; +use ra_syntax::{ + ast::{self, AstNode}, + SyntaxNode, +}; use crate::ast_id_map::FileAstId; @@ -151,3 +155,18 @@ impl AstId { db.ast_id_map(self.file_id).get(self.file_ast_id).to_node(&root) } } + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Source { + pub file_id: HirFileId, + pub ast: T, +} + +impl Source { + pub fn map U, U>(self, f: F) -> Source { + Source { file_id: self.file_id, ast: f(self.ast) } + } + pub fn file_syntax(&self, db: &impl db::AstDatabase) -> SyntaxNode { + db.parse_or_expand(self.file_id).expect("source created from invalid file") + } +} diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml index bf6ef12f3ffb..fa353b5dd8f2 100644 --- a/crates/ra_ide_api/Cargo.toml +++ b/crates/ra_ide_api/Cargo.toml @@ -12,7 +12,6 @@ format-buf = "1.0.0" itertools = "0.8.0" join_to_string = "0.1.3" log = "0.4.5" -relative-path = "1.0.0" rayon = "1.0.2" fst = { version = "0.3.1", default-features = false } rustc-hash = "1.0" diff --git a/crates/ra_ide_api/src/change.rs b/crates/ra_ide_api/src/change.rs index 39c5946c7f18..4416421ae013 100644 --- a/crates/ra_ide_api/src/change.rs +++ b/crates/ra_ide_api/src/change.rs @@ -4,13 +4,13 @@ use std::{fmt, sync::Arc, time}; use ra_db::{ salsa::{Database, Durability, SweepStrategy}, - CrateGraph, CrateId, FileId, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId, + CrateGraph, CrateId, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, + SourceRootId, }; use ra_prof::{memory_usage, profile, Bytes}; use ra_syntax::SourceFile; #[cfg(not(feature = "wasm"))] use rayon::prelude::*; -use relative_path::RelativePathBuf; use rustc_hash::FxHashMap; use crate::{ diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index 9ac9768afb20..09ca401794ea 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs @@ -18,15 +18,15 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { match def { hir::ModuleDef::Module(module) => { let module_scope = module.scope(ctx.db); - for (name, res) in module_scope.entries() { - if let Some(hir::ModuleDef::BuiltinType(..)) = res.def.take_types() { + for (name, def, import) in module_scope { + if let hir::ScopeDef::ModuleDef(hir::ModuleDef::BuiltinType(..)) = def { if ctx.use_item_syntax.is_some() { tested_by!(dont_complete_primitive_in_use); continue; } } if Some(module) == ctx.module { - if let Some(import) = res.import { + if let Some(import) = import { if let Either::A(use_tree) = module.import_source(ctx.db, import) { if use_tree.syntax().text_range().contains_inclusive(ctx.offset) { // for `use self::foo<|>`, don't suggest `foo` as a completion @@ -36,7 +36,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { } } } - acc.add_resolution(ctx, name.to_string(), &res.def.into()); + acc.add_resolution(ctx, name.to_string(), &def); } } hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => { diff --git a/crates/ra_ide_api/src/completion/complete_record_literal.rs b/crates/ra_ide_api/src/completion/complete_record_literal.rs index 4406695d5179..0295b8101373 100644 --- a/crates/ra_ide_api/src/completion/complete_record_literal.rs +++ b/crates/ra_ide_api/src/completion/complete_record_literal.rs @@ -31,6 +31,34 @@ mod tests { do_completion(code, CompletionKind::Reference) } + #[test] + fn test_record_literal_deprecated_field() { + let completions = complete( + r" + struct A { + #[deprecated] + the_field: u32, + } + fn foo() { + A { the<|> } + } + ", + ); + assert_debug_snapshot!(completions, @r###" + ⋮[ + ⋮ CompletionItem { + ⋮ label: "the_field", + ⋮ source_range: [142; 145), + ⋮ delete: [142; 145), + ⋮ insert: "the_field", + ⋮ kind: Field, + ⋮ detail: "u32", + ⋮ deprecated: true, + ⋮ }, + ⋮] + "###); + } + #[test] fn test_record_literal_field() { let completions = complete( diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs index 5c9c44704b3b..93f336370915 100644 --- a/crates/ra_ide_api/src/completion/completion_item.rs +++ b/crates/ra_ide_api/src/completion/completion_item.rs @@ -44,6 +44,9 @@ pub struct CompletionItem { /// Additional info to show in the UI pop up. detail: Option, documentation: Option, + + /// Whether this item is marked as deprecated + deprecated: bool, } // We use custom debug for CompletionItem to make `insta`'s diffs more readable. @@ -70,6 +73,9 @@ impl fmt::Debug for CompletionItem { if let Some(documentation) = self.documentation() { s.field("documentation", &documentation); } + if self.deprecated { + s.field("deprecated", &true); + } s.finish() } } @@ -132,6 +138,7 @@ impl CompletionItem { lookup: None, kind: None, text_edit: None, + deprecated: None, } } /// What user sees in pop-up in the UI. @@ -166,6 +173,10 @@ impl CompletionItem { pub fn kind(&self) -> Option { self.kind } + + pub fn deprecated(&self) -> bool { + self.deprecated + } } /// A helper to make `CompletionItem`s. @@ -181,6 +192,7 @@ pub(crate) struct Builder { lookup: Option, kind: Option, text_edit: Option, + deprecated: Option, } impl Builder { @@ -208,6 +220,7 @@ impl Builder { lookup: self.lookup, kind: self.kind, completion_kind: self.completion_kind, + deprecated: self.deprecated.unwrap_or(false), } } pub(crate) fn lookup_by(mut self, lookup: impl Into) -> Builder { @@ -254,6 +267,10 @@ impl Builder { self.documentation = docs.map(Into::into); self } + pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { + self.deprecated = Some(deprecated); + self + } } impl<'a> Into for Builder { diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs index 65bb639ed508..cb55d18751b3 100644 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ b/crates/ra_ide_api/src/completion/presentation.rs @@ -2,7 +2,7 @@ use hir::{db::HirDatabase, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk}; use join_to_string::join; -use ra_syntax::ast::NameOwner; +use ra_syntax::ast::{AttrsOwner, NameOwner}; use test_utils::tested_by; use crate::completion::{ @@ -18,6 +18,11 @@ impl Completions { field: hir::StructField, substs: &hir::Substs, ) { + let ast_node = field.source(ctx.db).ast; + let is_deprecated = match ast_node { + hir::FieldSource::Named(m) => is_deprecated(m), + hir::FieldSource::Pos(m) => is_deprecated(m), + }; CompletionItem::new( CompletionKind::Reference, ctx.source_range(), @@ -26,6 +31,7 @@ impl Completions { .kind(CompletionItemKind::Field) .detail(field.ty(ctx.db).subst(substs).display(ctx.db).to_string()) .set_documentation(field.docs(ctx.db)) + .set_deprecated(is_deprecated) .add_to(self); } @@ -179,6 +185,7 @@ impl Completions { CompletionItem::new(CompletionKind::Reference, ctx.source_range(), ¯o_declaration) .kind(CompletionItemKind::Macro) .set_documentation(docs.clone()) + .set_deprecated(is_deprecated(ast_node)) .detail(detail); builder = if ctx.use_item_syntax.is_some() { @@ -211,6 +218,7 @@ impl Completions { CompletionItemKind::Function }) .set_documentation(func.docs(ctx.db)) + .set_deprecated(is_deprecated(ast_node)) .detail(detail); // Add `<>` for generic types @@ -242,6 +250,7 @@ impl Completions { CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string()) .kind(CompletionItemKind::Const) .set_documentation(constant.docs(ctx.db)) + .set_deprecated(is_deprecated(ast_node)) .detail(detail) .add_to(self); } @@ -257,11 +266,13 @@ impl Completions { CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.text().to_string()) .kind(CompletionItemKind::TypeAlias) .set_documentation(type_alias.docs(ctx.db)) + .set_deprecated(is_deprecated(type_def)) .detail(detail) .add_to(self); } pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) { + let is_deprecated = is_deprecated(variant.source(ctx.db).ast); let name = match variant.name(ctx.db) { Some(it) => it, None => return, @@ -274,11 +285,16 @@ impl Completions { CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) .kind(CompletionItemKind::EnumVariant) .set_documentation(variant.docs(ctx.db)) + .set_deprecated(is_deprecated) .detail(detail) .add_to(self); } } +fn is_deprecated(node: impl AttrsOwner) -> bool { + node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated") +} + fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool { let subst = db.generic_defaults(def); subst.iter().any(|ty| ty == &Ty::Unknown) @@ -295,6 +311,56 @@ mod tests { do_completion(code, CompletionKind::Reference) } + #[test] + fn sets_deprecated_flag_in_completion_items() { + assert_debug_snapshot!( + do_reference_completion( + r#" + #[deprecated] + fn something_deprecated() {} + + #[deprecated(since = "1.0.0")] + fn something_else_deprecated() {} + + fn main() { som<|> } + "#, + ), + @r###" + [ + CompletionItem { + label: "main()", + source_range: [203; 206), + delete: [203; 206), + insert: "main()$0", + kind: Function, + lookup: "main", + detail: "fn main()", + }, + CompletionItem { + label: "something_deprecated()", + source_range: [203; 206), + delete: [203; 206), + insert: "something_deprecated()$0", + kind: Function, + lookup: "something_deprecated", + detail: "fn something_deprecated()", + deprecated: true, + }, + CompletionItem { + label: "something_else_deprecated()", + source_range: [203; 206), + delete: [203; 206), + insert: "something_else_deprecated()$0", + kind: Function, + lookup: "something_else_deprecated", + detail: "fn something_else_deprecated()", + deprecated: true, + }, + ] + "### + ); + } + #[test] fn inserts_parens_for_function_calls() { covers!(inserts_parens_for_function_calls); diff --git a/crates/ra_ide_api/src/db.rs b/crates/ra_ide_api/src/db.rs index 785e71808b7c..c96465b6a3d9 100644 --- a/crates/ra_ide_api/src/db.rs +++ b/crates/ra_ide_api/src/db.rs @@ -4,10 +4,9 @@ use std::sync::Arc; use ra_db::{ salsa::{self, Database, Durability}, - Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, - SourceDatabaseExt, SourceRootId, + Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, + SourceDatabase, SourceDatabaseExt, SourceRootId, }; -use relative_path::RelativePath; use rustc_hash::FxHashMap; use crate::{ diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 1f1f5cd742c9..2890a3d2b519 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -4,7 +4,7 @@ use std::cell::RefCell; use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}; use itertools::Itertools; -use ra_db::{SourceDatabase, SourceDatabaseExt}; +use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; use ra_prof::profile; use ra_syntax::{ algo, @@ -12,7 +12,6 @@ use ra_syntax::{ Location, SyntaxNode, TextRange, T, }; use ra_text_edit::{TextEdit, TextEditBuilder}; -use relative_path::RelativePath; use crate::{db::RootDatabase, Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; diff --git a/crates/ra_ide_api/src/mock_analysis.rs b/crates/ra_ide_api/src/mock_analysis.rs index 80b71894cdf6..2b1c96dbfd5b 100644 --- a/crates/ra_ide_api/src/mock_analysis.rs +++ b/crates/ra_ide_api/src/mock_analysis.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use ra_cfg::CfgOptions; -use relative_path::RelativePathBuf; +use ra_db::RelativePathBuf; use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; use crate::{ diff --git a/crates/ra_ide_api/src/references/rename.rs b/crates/ra_ide_api/src/references/rename.rs index a8783d7a0dc4..11f81cbb32f4 100644 --- a/crates/ra_ide_api/src/references/rename.rs +++ b/crates/ra_ide_api/src/references/rename.rs @@ -1,10 +1,9 @@ //! FIXME: write short doc here use hir::ModuleSource; -use ra_db::{SourceDatabase, SourceDatabaseExt}; +use ra_db::{RelativePath, RelativePathBuf, SourceDatabase, SourceDatabaseExt}; use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode}; use ra_text_edit::TextEdit; -use relative_path::{RelativePath, RelativePathBuf}; use crate::{ db::RootDatabase, FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange, diff --git a/crates/ra_ide_api/src/source_change.rs b/crates/ra_ide_api/src/source_change.rs index 4e63bbf6f0c8..f5f7f8807c44 100644 --- a/crates/ra_ide_api/src/source_change.rs +++ b/crates/ra_ide_api/src/source_change.rs @@ -3,8 +3,8 @@ //! //! It can be viewed as a dual for `AnalysisChange`. +use ra_db::RelativePathBuf; use ra_text_edit::TextEdit; -use relative_path::RelativePathBuf; use crate::{FileId, FilePosition, SourceRootId, TextUnit}; diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index ee503633d7ee..94ed619faeca 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -127,6 +127,7 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem { text_edit: Some(text_edit), additional_text_edits: Some(additional_text_edits), documentation: self.documentation().map(|it| it.conv()), + deprecated: Some(self.deprecated()), ..Default::default() }; res.insert_text_format = Some(match self.insert_text_format() { diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 3d5f18bfae68..95062ef6c467 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs @@ -110,6 +110,23 @@ pub fn match_arm_list(arms: impl Iterator) -> ast::MatchAr } } +pub fn let_match_early(expr: ast::Expr, path: &str, early_expression: &str) -> ast::LetStmt { + return from_text(&format!( + r#"let {} = match {} {{ + {}(it) => it, + None => {}, +}};"#, + expr.syntax().text(), + expr.syntax().text(), + path, + early_expression + )); + + fn from_text(text: &str) -> ast::LetStmt { + ast_from_text(&format!("fn f() {{ {} }}", text)) + } +} + pub fn where_pred(path: ast::Path, bounds: impl Iterator) -> ast::WherePred { let bounds = bounds.map(|b| b.syntax().to_string()).join(" + "); return from_text(&format!("{}: {}", path.syntax(), bounds)); diff --git a/crates/ra_tt/src/buffer.rs b/crates/ra_tt/src/buffer.rs index ef3f2323c98b..14b3f707df3b 100644 --- a/crates/ra_tt/src/buffer.rs +++ b/crates/ra_tt/src/buffer.rs @@ -111,7 +111,7 @@ impl<'a> Cursor<'a> { /// If the cursor is pointing at the end of a subtree, returns /// the parent subtree - pub fn end(self) -> Option<(&'a Subtree)> { + pub fn end(self) -> Option<&'a Subtree> { match self.entry() { Some(Entry::End(Some(ptr))) => { let idx = ptr.1; @@ -127,7 +127,7 @@ impl<'a> Cursor<'a> { } } - fn entry(self) -> Option<(&'a Entry<'a>)> { + fn entry(self) -> Option<&'a Entry<'a>> { self.buffer.entry(&self.ptr) } diff --git a/rustfmt.toml b/rustfmt.toml index f2e1646181bc..71007de81b9f 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,2 @@ reorder_modules = false use_small_heuristics = "Max" -newline_style = "Unix" diff --git a/xtask/tests/tidy-tests/docs.rs b/xtask/tests/tidy-tests/docs.rs index 6a629ce63e8f..227937f462a6 100644 --- a/xtask/tests/tidy-tests/docs.rs +++ b/xtask/tests/tidy-tests/docs.rs @@ -36,6 +36,7 @@ fn is_hidden(entry: &DirEntry) -> bool { fn no_docs_comments() { let crates = project_root().join("crates"); let iter = WalkDir::new(crates); + let mut missing_docs = Vec::new(); for f in iter.into_iter().filter_entry(|e| !is_hidden(e)) { let f = f.unwrap(); if f.file_type().is_dir() { @@ -54,12 +55,14 @@ fn no_docs_comments() { let mut line = String::new(); reader.read_line(&mut line).unwrap(); if !line.starts_with("//!") { - panic!( - "\nMissing docs strings\n\ - module: {}\n\ - Need add doc for module\n", - f.path().display() - ) + missing_docs.push(f.path().display().to_string()); } } + if !missing_docs.is_empty() { + panic!( + "\nMissing docs strings\n\n\ + modules:\n{}\n\n", + missing_docs.join("\n") + ) + } }